Fossil SCM
Simplify CSS selectors. Improve handling of attaching a given filename twice.
Commit
13d99044c886640311e048d9ff8d501142b33dfdfba31df7b1def597f2b7f378
Parent
b3814a431a26024…
2 files changed
+37
-34
+38
-33
+37
-34
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -2009,29 +2009,29 @@ | ||
| 2009 | 2009 | div.helpPage blockquote { |
| 2010 | 2010 | margin-left: 0.2em; |
| 2011 | 2011 | } |
| 2012 | 2012 | |
| 2013 | 2013 | /* .attach* = styles for file attachments */ |
| 2014 | -.attach-container { | |
| 2014 | +.attach-widget { | |
| 2015 | 2015 | margin-bottom: 1em; |
| 2016 | 2016 | display: flex; |
| 2017 | 2017 | flex-direction: column; |
| 2018 | 2018 | gap: 0.75em; |
| 2019 | 2019 | } |
| 2020 | -.attach-container > .attach-row { | |
| 2020 | +.attach-widget .attach-row { | |
| 2021 | 2021 | display: flex; |
| 2022 | 2022 | flex-direction: column; |
| 2023 | 2023 | gap: 0.5em; |
| 2024 | 2024 | padding: 0.75em; |
| 2025 | 2025 | border: 1px dashed #ccc; |
| 2026 | 2026 | border-radius: 0.25em; |
| 2027 | 2027 | background-color: #fafafa; |
| 2028 | 2028 | } |
| 2029 | -body.fossil-dark-style .attach-container > .attach-row { | |
| 2029 | +body.fossil-dark-style .attach-widget .attach-row { | |
| 2030 | 2030 | background-color: initial; |
| 2031 | 2031 | } |
| 2032 | -.attach-container > .attach-row > .attach-dropzone { | |
| 2032 | +.attach-widget .attach-dropzone { | |
| 2033 | 2033 | padding: 1em; |
| 2034 | 2034 | text-align: center; |
| 2035 | 2035 | background: #ffffff; |
| 2036 | 2036 | border: 1px solid #ddd; |
| 2037 | 2037 | cursor: pointer; |
| @@ -2039,70 +2039,73 @@ | ||
| 2039 | 2039 | transition: background-color 0.15s ease-in-out; |
| 2040 | 2040 | display: flex; |
| 2041 | 2041 | flex-direction: row; |
| 2042 | 2042 | flex-wrap: nowrap; |
| 2043 | 2043 | } |
| 2044 | -body.fossil-dark-style .attach-container > .attach-row > .attach-dropzone{ | |
| 2044 | +body.fossil-dark-style .attach-widget .attach-dropzone{ | |
| 2045 | 2045 | background: initial; |
| 2046 | 2046 | } |
| 2047 | -.attach-container > .attach-row > .attach-dropzone.populated { | |
| 2047 | +.attach-widget .attach-dropzone.populated { | |
| 2048 | 2048 | background-color: #f1f8e9; |
| 2049 | 2049 | border-color: #8bc34a; |
| 2050 | 2050 | border-style: solid; |
| 2051 | 2051 | text-align: left; |
| 2052 | 2052 | } |
| 2053 | -body.fossil-dark-style .attach-container > .attach-row > .attach-dropzone.populated{ | |
| 2054 | - background-color: initial; | |
| 2055 | - border-color: #8bc34a; | |
| 2056 | -} | |
| 2057 | -.attach-container > .attach-row > .attach-dropzone.dragover { | |
| 2058 | - background-color: #e1f5fe; | |
| 2059 | - border-color: #03a9f4; | |
| 2060 | -} | |
| 2061 | -body.fossil-dark-style .attach-container > .attach-row > .attach-dropzone.dragover{ | |
| 2062 | - border-color: #03a9f4; | |
| 2063 | -} | |
| 2064 | -body.fossil-dark-style > .attach-row > .attach-dropzone.dragover{ | |
| 2065 | - background-color: #e1f5fe; | |
| 2066 | - border-color: #03a9f4; | |
| 2067 | -} | |
| 2068 | -.attach-container > .attach-row > .attach-dropzone > .thumbnail { | |
| 2069 | - max-width: 10em; | |
| 2070 | - max-height: 10em; | |
| 2071 | -} | |
| 2072 | -.attach-container > .attach-row .attach-row-info { | |
| 2073 | - font-family: monospace; | |
| 2074 | - flex-grow: 1; | |
| 2075 | -} | |
| 2076 | -.attach-container > .attach-row .attach-desc { | |
| 2053 | +body.fossil-dark-style .attach-widget .attach-dropzone.populated{ | |
| 2054 | + background-color: initial; | |
| 2055 | +} | |
| 2056 | +.attach-widget .attach-dropzone.dragover { | |
| 2057 | + background-color: #e1f5fe; | |
| 2058 | + border-color: #03a9f4; | |
| 2059 | +} | |
| 2060 | +body.fossil-dark-style .attach-widget .attach-dropzone.dragover{ | |
| 2061 | + border-color: #03a9f4; | |
| 2062 | +} | |
| 2063 | +body.fossil-dark-style .attach-widget .attach-dropzone.dragover{ | |
| 2064 | + background-color: #e1f5fe; | |
| 2065 | + border-color: #03a9f4; | |
| 2066 | +} | |
| 2067 | +.attach-widget .thumbnail { | |
| 2068 | + max-width: 10em; | |
| 2069 | + max-height: 10em; | |
| 2070 | +} | |
| 2071 | +.attach-widget .attach-row-info{ | |
| 2072 | + font-family: monospace; | |
| 2073 | + flex-grow: 1; | |
| 2074 | + display: flex; | |
| 2075 | + flex-direction: column; | |
| 2076 | +} | |
| 2077 | +.attach-widget .attach-filename {} | |
| 2078 | +.attach-widget .attach-size {/*size and mimetype*/} | |
| 2079 | +.attach-widget .attach-desc { | |
| 2077 | 2080 | max-width: initial; |
| 2078 | 2081 | width: 100%; |
| 2079 | 2082 | box-sizing: border-box; |
| 2080 | 2083 | min-height: 4em; |
| 2081 | 2084 | padding: 0.5em; |
| 2082 | 2085 | font-family: inherit; |
| 2083 | 2086 | resize: vertical; |
| 2084 | 2087 | } |
| 2085 | -.attach-container > .attach-row .attach-row-remove { | |
| 2088 | +.attach-widget .attach-row-remove { | |
| 2086 | 2089 | align-self: center; |
| 2087 | 2090 | padding: 0.25em 0.75em; |
| 2088 | 2091 | margin-left: 1em; |
| 2089 | 2092 | background-color: #d32f2f; |
| 2090 | 2093 | color: #fff; |
| 2091 | 2094 | border: none; |
| 2092 | 2095 | border-radius: 0.25em; |
| 2093 | 2096 | cursor: pointer; |
| 2094 | 2097 | } |
| 2095 | -.attach-container > .attach-row .attach-row-remove:hover { | |
| 2098 | +.attach-widget .attach-row-remove:hover { | |
| 2096 | 2099 | background-color: #b71c1c; |
| 2097 | 2100 | } |
| 2098 | -.attach-container > .attach-controls { | |
| 2101 | +.attach-widget .attach-controls { | |
| 2099 | 2102 | display: flex; |
| 2100 | 2103 | flex-direction: row; |
| 2101 | 2104 | gap: 1em; |
| 2102 | 2105 | } |
| 2103 | -.attach-container > .attach-controls .attach-add-button { | |
| 2106 | +.attach-widget .attach-controls .attach-add-button { | |
| 2104 | 2107 | padding: 0.5em 1em; |
| 2105 | 2108 | cursor: pointer; |
| 2106 | 2109 | flex-grow: 2; |
| 2107 | 2110 | } |
| 2108 | 2111 | |
| 2109 | 2112 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -2009,29 +2009,29 @@ | |
| 2009 | div.helpPage blockquote { |
| 2010 | margin-left: 0.2em; |
| 2011 | } |
| 2012 | |
| 2013 | /* .attach* = styles for file attachments */ |
| 2014 | .attach-container { |
| 2015 | margin-bottom: 1em; |
| 2016 | display: flex; |
| 2017 | flex-direction: column; |
| 2018 | gap: 0.75em; |
| 2019 | } |
| 2020 | .attach-container > .attach-row { |
| 2021 | display: flex; |
| 2022 | flex-direction: column; |
| 2023 | gap: 0.5em; |
| 2024 | padding: 0.75em; |
| 2025 | border: 1px dashed #ccc; |
| 2026 | border-radius: 0.25em; |
| 2027 | background-color: #fafafa; |
| 2028 | } |
| 2029 | body.fossil-dark-style .attach-container > .attach-row { |
| 2030 | background-color: initial; |
| 2031 | } |
| 2032 | .attach-container > .attach-row > .attach-dropzone { |
| 2033 | padding: 1em; |
| 2034 | text-align: center; |
| 2035 | background: #ffffff; |
| 2036 | border: 1px solid #ddd; |
| 2037 | cursor: pointer; |
| @@ -2039,70 +2039,73 @@ | |
| 2039 | transition: background-color 0.15s ease-in-out; |
| 2040 | display: flex; |
| 2041 | flex-direction: row; |
| 2042 | flex-wrap: nowrap; |
| 2043 | } |
| 2044 | body.fossil-dark-style .attach-container > .attach-row > .attach-dropzone{ |
| 2045 | background: initial; |
| 2046 | } |
| 2047 | .attach-container > .attach-row > .attach-dropzone.populated { |
| 2048 | background-color: #f1f8e9; |
| 2049 | border-color: #8bc34a; |
| 2050 | border-style: solid; |
| 2051 | text-align: left; |
| 2052 | } |
| 2053 | body.fossil-dark-style .attach-container > .attach-row > .attach-dropzone.populated{ |
| 2054 | background-color: initial; |
| 2055 | border-color: #8bc34a; |
| 2056 | } |
| 2057 | .attach-container > .attach-row > .attach-dropzone.dragover { |
| 2058 | background-color: #e1f5fe; |
| 2059 | border-color: #03a9f4; |
| 2060 | } |
| 2061 | body.fossil-dark-style .attach-container > .attach-row > .attach-dropzone.dragover{ |
| 2062 | border-color: #03a9f4; |
| 2063 | } |
| 2064 | body.fossil-dark-style > .attach-row > .attach-dropzone.dragover{ |
| 2065 | background-color: #e1f5fe; |
| 2066 | border-color: #03a9f4; |
| 2067 | } |
| 2068 | .attach-container > .attach-row > .attach-dropzone > .thumbnail { |
| 2069 | max-width: 10em; |
| 2070 | max-height: 10em; |
| 2071 | } |
| 2072 | .attach-container > .attach-row .attach-row-info { |
| 2073 | font-family: monospace; |
| 2074 | flex-grow: 1; |
| 2075 | } |
| 2076 | .attach-container > .attach-row .attach-desc { |
| 2077 | max-width: initial; |
| 2078 | width: 100%; |
| 2079 | box-sizing: border-box; |
| 2080 | min-height: 4em; |
| 2081 | padding: 0.5em; |
| 2082 | font-family: inherit; |
| 2083 | resize: vertical; |
| 2084 | } |
| 2085 | .attach-container > .attach-row .attach-row-remove { |
| 2086 | align-self: center; |
| 2087 | padding: 0.25em 0.75em; |
| 2088 | margin-left: 1em; |
| 2089 | background-color: #d32f2f; |
| 2090 | color: #fff; |
| 2091 | border: none; |
| 2092 | border-radius: 0.25em; |
| 2093 | cursor: pointer; |
| 2094 | } |
| 2095 | .attach-container > .attach-row .attach-row-remove:hover { |
| 2096 | background-color: #b71c1c; |
| 2097 | } |
| 2098 | .attach-container > .attach-controls { |
| 2099 | display: flex; |
| 2100 | flex-direction: row; |
| 2101 | gap: 1em; |
| 2102 | } |
| 2103 | .attach-container > .attach-controls .attach-add-button { |
| 2104 | padding: 0.5em 1em; |
| 2105 | cursor: pointer; |
| 2106 | flex-grow: 2; |
| 2107 | } |
| 2108 | |
| 2109 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -2009,29 +2009,29 @@ | |
| 2009 | div.helpPage blockquote { |
| 2010 | margin-left: 0.2em; |
| 2011 | } |
| 2012 | |
| 2013 | /* .attach* = styles for file attachments */ |
| 2014 | .attach-widget { |
| 2015 | margin-bottom: 1em; |
| 2016 | display: flex; |
| 2017 | flex-direction: column; |
| 2018 | gap: 0.75em; |
| 2019 | } |
| 2020 | .attach-widget .attach-row { |
| 2021 | display: flex; |
| 2022 | flex-direction: column; |
| 2023 | gap: 0.5em; |
| 2024 | padding: 0.75em; |
| 2025 | border: 1px dashed #ccc; |
| 2026 | border-radius: 0.25em; |
| 2027 | background-color: #fafafa; |
| 2028 | } |
| 2029 | body.fossil-dark-style .attach-widget .attach-row { |
| 2030 | background-color: initial; |
| 2031 | } |
| 2032 | .attach-widget .attach-dropzone { |
| 2033 | padding: 1em; |
| 2034 | text-align: center; |
| 2035 | background: #ffffff; |
| 2036 | border: 1px solid #ddd; |
| 2037 | cursor: pointer; |
| @@ -2039,70 +2039,73 @@ | |
| 2039 | transition: background-color 0.15s ease-in-out; |
| 2040 | display: flex; |
| 2041 | flex-direction: row; |
| 2042 | flex-wrap: nowrap; |
| 2043 | } |
| 2044 | body.fossil-dark-style .attach-widget .attach-dropzone{ |
| 2045 | background: initial; |
| 2046 | } |
| 2047 | .attach-widget .attach-dropzone.populated { |
| 2048 | background-color: #f1f8e9; |
| 2049 | border-color: #8bc34a; |
| 2050 | border-style: solid; |
| 2051 | text-align: left; |
| 2052 | } |
| 2053 | body.fossil-dark-style .attach-widget .attach-dropzone.populated{ |
| 2054 | background-color: initial; |
| 2055 | } |
| 2056 | .attach-widget .attach-dropzone.dragover { |
| 2057 | background-color: #e1f5fe; |
| 2058 | border-color: #03a9f4; |
| 2059 | } |
| 2060 | body.fossil-dark-style .attach-widget .attach-dropzone.dragover{ |
| 2061 | border-color: #03a9f4; |
| 2062 | } |
| 2063 | body.fossil-dark-style .attach-widget .attach-dropzone.dragover{ |
| 2064 | background-color: #e1f5fe; |
| 2065 | border-color: #03a9f4; |
| 2066 | } |
| 2067 | .attach-widget .thumbnail { |
| 2068 | max-width: 10em; |
| 2069 | max-height: 10em; |
| 2070 | } |
| 2071 | .attach-widget .attach-row-info{ |
| 2072 | font-family: monospace; |
| 2073 | flex-grow: 1; |
| 2074 | display: flex; |
| 2075 | flex-direction: column; |
| 2076 | } |
| 2077 | .attach-widget .attach-filename {} |
| 2078 | .attach-widget .attach-size {/*size and mimetype*/} |
| 2079 | .attach-widget .attach-desc { |
| 2080 | max-width: initial; |
| 2081 | width: 100%; |
| 2082 | box-sizing: border-box; |
| 2083 | min-height: 4em; |
| 2084 | padding: 0.5em; |
| 2085 | font-family: inherit; |
| 2086 | resize: vertical; |
| 2087 | } |
| 2088 | .attach-widget .attach-row-remove { |
| 2089 | align-self: center; |
| 2090 | padding: 0.25em 0.75em; |
| 2091 | margin-left: 1em; |
| 2092 | background-color: #d32f2f; |
| 2093 | color: #fff; |
| 2094 | border: none; |
| 2095 | border-radius: 0.25em; |
| 2096 | cursor: pointer; |
| 2097 | } |
| 2098 | .attach-widget .attach-row-remove:hover { |
| 2099 | background-color: #b71c1c; |
| 2100 | } |
| 2101 | .attach-widget .attach-controls { |
| 2102 | display: flex; |
| 2103 | flex-direction: row; |
| 2104 | gap: 1em; |
| 2105 | } |
| 2106 | .attach-widget .attach-controls .attach-add-button { |
| 2107 | padding: 0.5em 1em; |
| 2108 | cursor: pointer; |
| 2109 | flex-grow: 2; |
| 2110 | } |
| 2111 | |
| 2112 |
+38
-33
| --- src/fossil.attach.js | ||
| +++ src/fossil.attach.js | ||
| @@ -78,11 +78,11 @@ | ||
| 78 | 78 | ); |
| 79 | 79 | eBtnAdd.type = 'button'; |
| 80 | 80 | const eControls = this.#e.controls = |
| 81 | 81 | D.addClass(D.div(), 'attach-controls'); |
| 82 | 82 | eControls.append(eBtnAdd); |
| 83 | - this.#e.list = D.addClass(D.div(), 'attach-container'); | |
| 83 | + this.#e.list = D.addClass(D.div(), 'attach-widget'); | |
| 84 | 84 | opt.container.appendChild(this.#e.list); |
| 85 | 85 | this.#e.list.appendChild(eControls); |
| 86 | 86 | if( opt.listener ){ |
| 87 | 87 | const doCb = (eventType, cb)=>{ |
| 88 | 88 | const f = cb || opt.listener.all; |
| @@ -130,11 +130,11 @@ | ||
| 130 | 130 | get controlsElement(){ |
| 131 | 131 | return this.#e.controls; |
| 132 | 132 | } |
| 133 | 133 | |
| 134 | 134 | #removeRow(rowObj){ |
| 135 | - rowObj.eRow.remove(); | |
| 135 | + rowObj.e.row.remove(); | |
| 136 | 136 | this.#rows = this.#rows.filter(v=>v!==rowObj); |
| 137 | 137 | this.#updateControls(); |
| 138 | 138 | this.#events.dispatchEvent( |
| 139 | 139 | new CustomEvent('entry-removed',{ |
| 140 | 140 | detail: F.nu({ |
| @@ -176,16 +176,18 @@ | ||
| 176 | 176 | eRow.dataset.id = id; |
| 177 | 177 | const eDropzone = D.addClass(D.div(), 'attach-dropzone'); |
| 178 | 178 | const eFile = D.addClass( |
| 179 | 179 | D.input('file'), 'attach-file-input', 'hidden' |
| 180 | 180 | ); |
| 181 | - const eInfo = D.append( | |
| 182 | - D.addClass(D.span(), 'attach-row-info'), | |
| 181 | + const eInfo = D.addClass(D.span(), 'attach-row-info'); | |
| 182 | + const eFilename = D.append( | |
| 183 | + D.addClass(D.span(), 'attach-filename'), | |
| 183 | 184 | "Select/drop file or click the outer border and tap your "+ |
| 184 | 185 | "platform's conventional Paste keyboard shortcut." |
| 185 | 186 | ); |
| 186 | - | |
| 187 | + const eSize = D.addClass(D.span(), 'attach-size'); | |
| 188 | + eInfo.append(eFilename, eSize); | |
| 187 | 189 | const eDesc = D.addClass( |
| 188 | 190 | D.attr(D.textarea(), 'placeholder', |
| 189 | 191 | 'Optional description...'), |
| 190 | 192 | 'hidden', 'attach-desc' |
| 191 | 193 | ); |
| @@ -246,15 +248,19 @@ | ||
| 246 | 248 | break; |
| 247 | 249 | } |
| 248 | 250 | } |
| 249 | 251 | }); |
| 250 | 252 | D.append(eRow, eDropzone, eDesc); |
| 251 | - rowObj.eDropzone = eDropzone; | |
| 252 | - rowObj.eInfo = eInfo; | |
| 253 | - rowObj.eDesc = eDesc; | |
| 254 | - rowObj.eRow = eRow; | |
| 255 | - rowObj.eRemove = eRemove; | |
| 253 | + rowObj.e = F.nu({ | |
| 254 | + dropzone: eDropzone, | |
| 255 | + info: eInfo, | |
| 256 | + filename: eFilename, | |
| 257 | + size: eSize, | |
| 258 | + desc: eDesc, | |
| 259 | + row: eRow, | |
| 260 | + remove: eRemove | |
| 261 | + }); | |
| 256 | 262 | this.#e.list.append(eRow); |
| 257 | 263 | this.#rows.push( rowObj ); |
| 258 | 264 | this.#updateControls(); |
| 259 | 265 | this.#events.dispatchEvent( |
| 260 | 266 | new CustomEvent('entry-added',{ |
| @@ -290,47 +296,46 @@ | ||
| 290 | 296 | rowObj.name = `pasted-image-${Date.now()}.png`; |
| 291 | 297 | } |
| 292 | 298 | if( rowObj.name && rowObj.name!==file.name ){ |
| 293 | 299 | file = new File([file], rowObj.name, {type: file.type}); |
| 294 | 300 | } |
| 295 | - /* | |
| 296 | - Fossil attachments treat the name as a unique-per-target key, | |
| 297 | - with the newest one being the primary. If a name is given | |
| 298 | - twice, replace the prior entry before adding the new | |
| 299 | - one. There are conceivable, but also unlikely, cases where | |
| 300 | - this will have unintended side-effects, but that seems like a | |
| 301 | - lesser evil than attaching the same file N times, leading to N | |
| 302 | - attachment artifacts. | |
| 303 | - */ | |
| 304 | - rowObj.file = file; | |
| 305 | - rowObj.mimeType = file.type || 'application/octet-stream'; | |
| 306 | - | |
| 307 | - const lbl = file.name || 'Pasted Content'; | |
| 301 | + | |
| 308 | 302 | let szLbl; |
| 309 | 303 | if( file.size < 500000 ){ |
| 310 | 304 | szLbl = file.size + ' bytes'; |
| 311 | 305 | }else if( file.size < 1000000 ){ |
| 312 | 306 | szLbl = (file.size / 1024).toFixed(2)+' KB'; |
| 313 | 307 | }else{ |
| 314 | 308 | szLbl = (file.size / (1024 * 1024)).toFixed(2)+' MB'; |
| 315 | 309 | } |
| 316 | - D.append( | |
| 317 | - D.clearElement(rowObj.eInfo), | |
| 318 | - lbl, D.br(), szLbl, ' ', rowObj.mimeType || '' | |
| 319 | - ); | |
| 320 | 310 | const old = this.#rowMatchingName(file.name); |
| 321 | 311 | if( old && rowObj !== old){ |
| 322 | - /* FIXME: recycle `old` instead to avoid UI flicker. */ | |
| 323 | - this.#removeRow(old); | |
| 312 | + /* | |
| 313 | + Fossil attachments treat the name as a unique-per-target | |
| 314 | + key, with the newest one being the primary. If a name is | |
| 315 | + given twice, remove the new entry and reuse the older | |
| 316 | + one. There are conceivable, but also unlikely, cases where | |
| 317 | + this will have unintended side-effects, e.g. attaching both | |
| 318 | + /foo/bar and /baz/bar, but that seems like a lesser evil | |
| 319 | + than attaching the same file N times, leading to N | |
| 320 | + attachment artifacts. | |
| 321 | + */ | |
| 322 | + /* recycle `old` instead to avoid UI flicker. */ | |
| 323 | + this.#removeRow(rowObj); | |
| 324 | + rowObj.e = old.e; | |
| 324 | 325 | } |
| 325 | - rowObj.eDropzone.classList.add('populated'); | |
| 326 | - rowObj.eDesc.classList.remove('hidden'); | |
| 326 | + rowObj.file = file; | |
| 327 | + rowObj.mimeType = file.type || 'application/octet-stream'; | |
| 328 | + D.clearElement(rowObj.e.filename).append(file.name || 'Pasted Content'); | |
| 329 | + D.clearElement(rowObj.e.size).append(szLbl, ' ', rowObj.mimeType || ''); | |
| 330 | + rowObj.e.dropzone.classList.add('populated'); | |
| 331 | + rowObj.e.desc.classList.remove('hidden'); | |
| 327 | 332 | if( file.type?.startsWith?.('image/') || file.type==='BITMAP' ){ |
| 328 | 333 | /* Add a thumbnail */ |
| 329 | - const img = rowObj.eDropzone.querySelector('img.thumbnail') || D.img(); | |
| 334 | + const img = rowObj.e.dropzone.querySelector('img.thumbnail') || D.img(); | |
| 330 | 335 | img.classList.add('thumbnail'); |
| 331 | - rowObj.eDropzone.insertBefore(img, rowObj.eRemove); | |
| 336 | + rowObj.e.dropzone.insertBefore(img, rowObj.e.remove); | |
| 332 | 337 | const reader = new FileReader(); |
| 333 | 338 | reader.onload = (e)=>img.setAttribute('src', e.target.result); |
| 334 | 339 | reader.readAsDataURL(file); |
| 335 | 340 | } |
| 336 | 341 | this.#events.dispatchEvent( |
| 337 | 342 |
| --- src/fossil.attach.js | |
| +++ src/fossil.attach.js | |
| @@ -78,11 +78,11 @@ | |
| 78 | ); |
| 79 | eBtnAdd.type = 'button'; |
| 80 | const eControls = this.#e.controls = |
| 81 | D.addClass(D.div(), 'attach-controls'); |
| 82 | eControls.append(eBtnAdd); |
| 83 | this.#e.list = D.addClass(D.div(), 'attach-container'); |
| 84 | opt.container.appendChild(this.#e.list); |
| 85 | this.#e.list.appendChild(eControls); |
| 86 | if( opt.listener ){ |
| 87 | const doCb = (eventType, cb)=>{ |
| 88 | const f = cb || opt.listener.all; |
| @@ -130,11 +130,11 @@ | |
| 130 | get controlsElement(){ |
| 131 | return this.#e.controls; |
| 132 | } |
| 133 | |
| 134 | #removeRow(rowObj){ |
| 135 | rowObj.eRow.remove(); |
| 136 | this.#rows = this.#rows.filter(v=>v!==rowObj); |
| 137 | this.#updateControls(); |
| 138 | this.#events.dispatchEvent( |
| 139 | new CustomEvent('entry-removed',{ |
| 140 | detail: F.nu({ |
| @@ -176,16 +176,18 @@ | |
| 176 | eRow.dataset.id = id; |
| 177 | const eDropzone = D.addClass(D.div(), 'attach-dropzone'); |
| 178 | const eFile = D.addClass( |
| 179 | D.input('file'), 'attach-file-input', 'hidden' |
| 180 | ); |
| 181 | const eInfo = D.append( |
| 182 | D.addClass(D.span(), 'attach-row-info'), |
| 183 | "Select/drop file or click the outer border and tap your "+ |
| 184 | "platform's conventional Paste keyboard shortcut." |
| 185 | ); |
| 186 | |
| 187 | const eDesc = D.addClass( |
| 188 | D.attr(D.textarea(), 'placeholder', |
| 189 | 'Optional description...'), |
| 190 | 'hidden', 'attach-desc' |
| 191 | ); |
| @@ -246,15 +248,19 @@ | |
| 246 | break; |
| 247 | } |
| 248 | } |
| 249 | }); |
| 250 | D.append(eRow, eDropzone, eDesc); |
| 251 | rowObj.eDropzone = eDropzone; |
| 252 | rowObj.eInfo = eInfo; |
| 253 | rowObj.eDesc = eDesc; |
| 254 | rowObj.eRow = eRow; |
| 255 | rowObj.eRemove = eRemove; |
| 256 | this.#e.list.append(eRow); |
| 257 | this.#rows.push( rowObj ); |
| 258 | this.#updateControls(); |
| 259 | this.#events.dispatchEvent( |
| 260 | new CustomEvent('entry-added',{ |
| @@ -290,47 +296,46 @@ | |
| 290 | rowObj.name = `pasted-image-${Date.now()}.png`; |
| 291 | } |
| 292 | if( rowObj.name && rowObj.name!==file.name ){ |
| 293 | file = new File([file], rowObj.name, {type: file.type}); |
| 294 | } |
| 295 | /* |
| 296 | Fossil attachments treat the name as a unique-per-target key, |
| 297 | with the newest one being the primary. If a name is given |
| 298 | twice, replace the prior entry before adding the new |
| 299 | one. There are conceivable, but also unlikely, cases where |
| 300 | this will have unintended side-effects, but that seems like a |
| 301 | lesser evil than attaching the same file N times, leading to N |
| 302 | attachment artifacts. |
| 303 | */ |
| 304 | rowObj.file = file; |
| 305 | rowObj.mimeType = file.type || 'application/octet-stream'; |
| 306 | |
| 307 | const lbl = file.name || 'Pasted Content'; |
| 308 | let szLbl; |
| 309 | if( file.size < 500000 ){ |
| 310 | szLbl = file.size + ' bytes'; |
| 311 | }else if( file.size < 1000000 ){ |
| 312 | szLbl = (file.size / 1024).toFixed(2)+' KB'; |
| 313 | }else{ |
| 314 | szLbl = (file.size / (1024 * 1024)).toFixed(2)+' MB'; |
| 315 | } |
| 316 | D.append( |
| 317 | D.clearElement(rowObj.eInfo), |
| 318 | lbl, D.br(), szLbl, ' ', rowObj.mimeType || '' |
| 319 | ); |
| 320 | const old = this.#rowMatchingName(file.name); |
| 321 | if( old && rowObj !== old){ |
| 322 | /* FIXME: recycle `old` instead to avoid UI flicker. */ |
| 323 | this.#removeRow(old); |
| 324 | } |
| 325 | rowObj.eDropzone.classList.add('populated'); |
| 326 | rowObj.eDesc.classList.remove('hidden'); |
| 327 | if( file.type?.startsWith?.('image/') || file.type==='BITMAP' ){ |
| 328 | /* Add a thumbnail */ |
| 329 | const img = rowObj.eDropzone.querySelector('img.thumbnail') || D.img(); |
| 330 | img.classList.add('thumbnail'); |
| 331 | rowObj.eDropzone.insertBefore(img, rowObj.eRemove); |
| 332 | const reader = new FileReader(); |
| 333 | reader.onload = (e)=>img.setAttribute('src', e.target.result); |
| 334 | reader.readAsDataURL(file); |
| 335 | } |
| 336 | this.#events.dispatchEvent( |
| 337 |
| --- src/fossil.attach.js | |
| +++ src/fossil.attach.js | |
| @@ -78,11 +78,11 @@ | |
| 78 | ); |
| 79 | eBtnAdd.type = 'button'; |
| 80 | const eControls = this.#e.controls = |
| 81 | D.addClass(D.div(), 'attach-controls'); |
| 82 | eControls.append(eBtnAdd); |
| 83 | this.#e.list = D.addClass(D.div(), 'attach-widget'); |
| 84 | opt.container.appendChild(this.#e.list); |
| 85 | this.#e.list.appendChild(eControls); |
| 86 | if( opt.listener ){ |
| 87 | const doCb = (eventType, cb)=>{ |
| 88 | const f = cb || opt.listener.all; |
| @@ -130,11 +130,11 @@ | |
| 130 | get controlsElement(){ |
| 131 | return this.#e.controls; |
| 132 | } |
| 133 | |
| 134 | #removeRow(rowObj){ |
| 135 | rowObj.e.row.remove(); |
| 136 | this.#rows = this.#rows.filter(v=>v!==rowObj); |
| 137 | this.#updateControls(); |
| 138 | this.#events.dispatchEvent( |
| 139 | new CustomEvent('entry-removed',{ |
| 140 | detail: F.nu({ |
| @@ -176,16 +176,18 @@ | |
| 176 | eRow.dataset.id = id; |
| 177 | const eDropzone = D.addClass(D.div(), 'attach-dropzone'); |
| 178 | const eFile = D.addClass( |
| 179 | D.input('file'), 'attach-file-input', 'hidden' |
| 180 | ); |
| 181 | const eInfo = D.addClass(D.span(), 'attach-row-info'); |
| 182 | const eFilename = D.append( |
| 183 | D.addClass(D.span(), 'attach-filename'), |
| 184 | "Select/drop file or click the outer border and tap your "+ |
| 185 | "platform's conventional Paste keyboard shortcut." |
| 186 | ); |
| 187 | const eSize = D.addClass(D.span(), 'attach-size'); |
| 188 | eInfo.append(eFilename, eSize); |
| 189 | const eDesc = D.addClass( |
| 190 | D.attr(D.textarea(), 'placeholder', |
| 191 | 'Optional description...'), |
| 192 | 'hidden', 'attach-desc' |
| 193 | ); |
| @@ -246,15 +248,19 @@ | |
| 248 | break; |
| 249 | } |
| 250 | } |
| 251 | }); |
| 252 | D.append(eRow, eDropzone, eDesc); |
| 253 | rowObj.e = F.nu({ |
| 254 | dropzone: eDropzone, |
| 255 | info: eInfo, |
| 256 | filename: eFilename, |
| 257 | size: eSize, |
| 258 | desc: eDesc, |
| 259 | row: eRow, |
| 260 | remove: eRemove |
| 261 | }); |
| 262 | this.#e.list.append(eRow); |
| 263 | this.#rows.push( rowObj ); |
| 264 | this.#updateControls(); |
| 265 | this.#events.dispatchEvent( |
| 266 | new CustomEvent('entry-added',{ |
| @@ -290,47 +296,46 @@ | |
| 296 | rowObj.name = `pasted-image-${Date.now()}.png`; |
| 297 | } |
| 298 | if( rowObj.name && rowObj.name!==file.name ){ |
| 299 | file = new File([file], rowObj.name, {type: file.type}); |
| 300 | } |
| 301 | |
| 302 | let szLbl; |
| 303 | if( file.size < 500000 ){ |
| 304 | szLbl = file.size + ' bytes'; |
| 305 | }else if( file.size < 1000000 ){ |
| 306 | szLbl = (file.size / 1024).toFixed(2)+' KB'; |
| 307 | }else{ |
| 308 | szLbl = (file.size / (1024 * 1024)).toFixed(2)+' MB'; |
| 309 | } |
| 310 | const old = this.#rowMatchingName(file.name); |
| 311 | if( old && rowObj !== old){ |
| 312 | /* |
| 313 | Fossil attachments treat the name as a unique-per-target |
| 314 | key, with the newest one being the primary. If a name is |
| 315 | given twice, remove the new entry and reuse the older |
| 316 | one. There are conceivable, but also unlikely, cases where |
| 317 | this will have unintended side-effects, e.g. attaching both |
| 318 | /foo/bar and /baz/bar, but that seems like a lesser evil |
| 319 | than attaching the same file N times, leading to N |
| 320 | attachment artifacts. |
| 321 | */ |
| 322 | /* recycle `old` instead to avoid UI flicker. */ |
| 323 | this.#removeRow(rowObj); |
| 324 | rowObj.e = old.e; |
| 325 | } |
| 326 | rowObj.file = file; |
| 327 | rowObj.mimeType = file.type || 'application/octet-stream'; |
| 328 | D.clearElement(rowObj.e.filename).append(file.name || 'Pasted Content'); |
| 329 | D.clearElement(rowObj.e.size).append(szLbl, ' ', rowObj.mimeType || ''); |
| 330 | rowObj.e.dropzone.classList.add('populated'); |
| 331 | rowObj.e.desc.classList.remove('hidden'); |
| 332 | if( file.type?.startsWith?.('image/') || file.type==='BITMAP' ){ |
| 333 | /* Add a thumbnail */ |
| 334 | const img = rowObj.e.dropzone.querySelector('img.thumbnail') || D.img(); |
| 335 | img.classList.add('thumbnail'); |
| 336 | rowObj.e.dropzone.insertBefore(img, rowObj.e.remove); |
| 337 | const reader = new FileReader(); |
| 338 | reader.onload = (e)=>img.setAttribute('src', e.target.result); |
| 339 | reader.readAsDataURL(file); |
| 340 | } |
| 341 | this.#events.dispatchEvent( |
| 342 |