toggle narrow width mode tooltips via html+css; code cosmetics
This commit is contained in:
		
							parent
							
								
									1348eeb4e3
								
							
						
					
					
						commit
						e50ff316ba
					
				
							
								
								
									
										17
									
								
								edit.html
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								edit.html
									
									
									
									
									
								
							|  | @ -179,17 +179,26 @@ | |||
|                     class="CodeMirror-search-field" rows="1" required | ||||
|                     spellcheck="false"></textarea> | ||||
|         </div> | ||||
|         <button data-action="replace" i18n-text="replace" disabled> | ||||
|         <button data-action="replace" i18n-text="replace" disabled></button> | ||||
|         <button data-action="replaceAll" i18n-text="replaceAll" disabled></button> | ||||
|         <button data-action="undo" i18n-text="undo" disabled></button> | ||||
|         <!-- | ||||
|         Using a separate set of buttons because | ||||
|         1. FF can display tooltips only when specified on the <button>, ignores the nested <title> in <svg> | ||||
|         2. the icon doesn't fill the entire button area so tooltips aren't shown when the edges are hovered | ||||
|         --> | ||||
|         <button class="hidden" data-action="replace" i18n-title="replace" disabled> | ||||
|           <svg class="svg-icon" viewBox="0 0 20 20"> | ||||
|             <polygon points="15.83 4.75 8.76 11.82 5.2 8.26 3.51 9.95 8.76 15.19 17.52 6.43 15.83 4.75"/> | ||||
|           </svg> | ||||
|         </button> | ||||
|         <button data-action="replaceAll" i18n-text="replaceAll" disabled> | ||||
|         <button class="hidden" data-action="replaceAll" i18n-title="replaceAll" disabled> | ||||
|           <svg class="svg-icon" viewBox="0 0 20 20"> | ||||
|             <polygon points="15.83 4.75 8.76 11.82 5.2 8.26 3.51 9.95 8.76 15.19 17.52 6.43 15.83 4.75"/> | ||||
|             <polygon points="15.8,1.8 8.8,8.8 5.2,5.3 3.5,6.9 8.8,12.2 17.5,3.4 "/> | ||||
|             <polygon points="15.8,7.8 8.8,14.8 5.2,11.3 3.5,12.9 8.8,18.2 17.5,9.4 "/> | ||||
|           </svg> | ||||
|         </button> | ||||
|         <button data-action="undo" i18n-text="undo" disabled> | ||||
|         <button class="hidden" data-action="undo" i18n-title="undo" disabled> | ||||
|           <svg class="svg-icon" viewBox="0 0 20 20"> | ||||
|             <path d="M11.3,5.5H8.7V1.4L1.9,6.5l6.8,5.1V7.5h2.6c1.8,0,3.2,1.4,3.2,3.2s-1.4,3.2-3.2,3.2H7.8v2h3.5c2.9,0,5.2-2.3,5.2-5.2S14.2,5.5,11.3,5.5z"/> | ||||
|           </svg> | ||||
|  |  | |||
|  | @ -209,10 +209,15 @@ | |||
|     right: 0; | ||||
|     max-width: none; | ||||
|   } | ||||
|   #search-replace-dialog[data-type="replace"] button { | ||||
|   #search-replace-dialog[data-type="replace"] button:not(.hidden) { | ||||
|     display: none !important; | ||||
|   } | ||||
|   #search-replace-dialog[data-type="replace"] button.hidden { | ||||
|     display: block !important; | ||||
|     font-size: 0; | ||||
|     width: 24px; | ||||
|     text-align: center; | ||||
|     cursor: pointer; | ||||
|   } | ||||
|   #search-replace-dialog[data-type="replace"] button svg { | ||||
|     display: inline; | ||||
|  | @ -222,11 +227,4 @@ | |||
|     max-width: calc(50vw - 120px); | ||||
|     width: auto; | ||||
|   } | ||||
|   #search-replace-dialog[data-type="replace"] button[data-action="replaceAll"] svg { | ||||
|     top: -3px; | ||||
|     position: relative; | ||||
|     /* unprefixed since Chrome 53 */ | ||||
|     -webkit-filter: drop-shadow(0 5px 0 currentColor); | ||||
|     filter: drop-shadow(0 5px 0 currentColor); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -22,16 +22,6 @@ onDOMready().then(() => { | |||
| 
 | ||||
|   const RX_MAYBE_REGEXP = /^\s*\/(.+?)\/([simguy]*)\s*$/; | ||||
| 
 | ||||
|   const NARROW_WIDTH = [...document.styleSheets] | ||||
|     .filter(({href}) => href && href.endsWith('global-search.css')) | ||||
|     .map(sheet => | ||||
|       [...sheet.cssRules] | ||||
|         .filter(r => r.media && r.conditionText.includes('max-width')) | ||||
|         .map(r => r.conditionText.match(/\d+/) | 0) | ||||
|         .sort((a, b) => a - b) | ||||
|         .pop()) | ||||
|     .pop() || 800; | ||||
| 
 | ||||
|   const state = { | ||||
|     // used for case-sensitive matching directly
 | ||||
|     find: '', | ||||
|  | @ -188,10 +178,12 @@ onDOMready().then(() => { | |||
|       state.replaceValue = state.replace.replace(/(\\r)?\\n/g, '\n').replace(/\\t/g, '\t'); | ||||
|       state.replaceHasRefs = /\$[$&`'\d]/.test(state.replaceValue); | ||||
|     } | ||||
|     const cmFocused = document.activeElement && document.activeElement.closest('.CodeMirror'); | ||||
|     state.activeAppliesTo = $(`.${APPLIES_VALUE_CLASS}:focus, .${APPLIES_VALUE_CLASS}.${TARGET_CLASS}`); | ||||
|     state.cmStart = CodeMirror.closestVisible( | ||||
|       document.activeElement && document.activeElement.closest('.CodeMirror') && document.activeElement || | ||||
|       state.activeAppliesTo || state.cm); | ||||
|       cmFocused && document.activeElement || | ||||
|       state.activeAppliesTo || | ||||
|       state.cm); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -208,21 +200,19 @@ onDOMready().then(() => { | |||
|       return; | ||||
|     } | ||||
|     initState(); | ||||
|     if (!state.find) { | ||||
|       clearMarker(); | ||||
|       makeTargetVisible(null); | ||||
|       setupOverlay(editors.slice()); | ||||
|       showTally(0, 0); | ||||
|       return; | ||||
|     } | ||||
|     const {cmStart} = state; | ||||
|     const {index, found, foundInCode} = doSearchInEditors({cmStart, canAdvance, inApplies}) || {}; | ||||
|     const {index, found, foundInCode} = state.find && doSearchInEditors({cmStart, canAdvance, inApplies}) || {}; | ||||
|     if (!foundInCode) clearMarker(); | ||||
|     if (!found) makeTargetVisible(null); | ||||
|     const radiateFrom = foundInCode ? index : editors.indexOf(cmStart); | ||||
|     setupOverlay(radiateArray(editors, radiateFrom)); | ||||
|     enableReplaceButtons(foundInCode); | ||||
|     debounce(showTally, 0, foundInCode && !state.numFound ? 1 : undefined); | ||||
|     if (state.find) { | ||||
|       const firstSuccessfulSearch = foundInCode && !state.numFound; | ||||
|       debounce(showTally, 0, firstSuccessfulSearch ? 1 : undefined); | ||||
|     } else { | ||||
|       showTally(0, 0); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -326,7 +316,7 @@ onDOMready().then(() => { | |||
| 
 | ||||
|     if (cursor) { | ||||
|       state.undoHistory.push([cm]); | ||||
|       state.undo.disabled = false; | ||||
|       enableUndoButton(true); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -338,7 +328,7 @@ onDOMready().then(() => { | |||
|     if (found.length) { | ||||
|       state.lastFind = null; | ||||
|       state.undoHistory.push(found); | ||||
|       state.undo.disabled = false; | ||||
|       enableUndoButton(true); | ||||
|       doSearch({canAdvance: false}); | ||||
|     } | ||||
|   } | ||||
|  | @ -388,8 +378,8 @@ onDOMready().then(() => { | |||
|         undoneSome = true; | ||||
|       } | ||||
|     } | ||||
|     state.undo.disabled = !state.undoHistory.length; | ||||
|     (state.undo.disabled ? state.input : state.undo).focus(); | ||||
|     enableUndoButton(state.undoHistory.length); | ||||
|     (state.undoHistory.length ? state.undo : state.input).focus(); | ||||
|     if (undoneSome) { | ||||
|       state.lastFind = null; | ||||
|       restoreWindowScrollPos(); | ||||
|  | @ -463,7 +453,6 @@ onDOMready().then(() => { | |||
|         cmState.annotateTimer = 0; | ||||
|       } | ||||
|       if (hasMatches) { | ||||
|         if (cmState.annotateTimer) clearTimeout(cmState.annotateTimer); | ||||
|         cmState.annotateTimer = setTimeout(annotateScrollbar, ANNOTATE_SCROLLBAR_DELAY, | ||||
|           cm, query, state.icase); | ||||
|       } | ||||
|  | @ -481,11 +470,10 @@ onDOMready().then(() => { | |||
|     const match = this.query.exec(stream.string); | ||||
|     if (match && match.index === stream.pos) { | ||||
|       this.numFound++; | ||||
|       //state.numFound++;
 | ||||
|       const t = performance.now(); | ||||
|       if (t - this.tallyShownTime > 10) { | ||||
|         debounce(showTally); | ||||
|         this.tallyShownTime = t; | ||||
|         debounce(showTally); | ||||
|       } | ||||
|       stream.pos += match[0].length || 1; | ||||
|       return MATCH_TOKEN_NAME; | ||||
|  | @ -573,6 +561,7 @@ onDOMready().then(() => { | |||
|       #search-replace-dialog [data-action="case"] { | ||||
|         color: ${colors.icon.fill}; | ||||
|       } | ||||
|       #search-replace-dialog[data-type="replace"] button:hover svg, | ||||
|       #search-replace-dialog svg:hover { | ||||
|         fill: inherit; | ||||
|       } | ||||
|  | @ -587,20 +576,11 @@ onDOMready().then(() => { | |||
|     document.body.appendChild(dialog); | ||||
|     dispatchEvent(new Event('showHotkeyInTooltip')); | ||||
| 
 | ||||
|     measureInput(state.input); | ||||
|     adjustTextareaSize(state.input); | ||||
|     if (type === 'replace') { | ||||
|       measureInput(state.input2); | ||||
|       adjustTextareaSize(state.input2); | ||||
|       enableReplaceButtons(state.find !== ''); | ||||
| 
 | ||||
|       addEventListener('resize', toggleReplaceButtonTooltips, {passive: true}); | ||||
|       toggleReplaceButtonTooltips(true); | ||||
| 
 | ||||
|       state.undo = $('[data-action="undo"]'); | ||||
|       state.undo.disabled = !state.undoHistory.length; | ||||
|     } else { | ||||
|       removeEventListener('resize', toggleReplaceButtonTooltips, {passive: true}); | ||||
|       enableUndoButton(state.undoHistory.length); | ||||
|     } | ||||
| 
 | ||||
|     return dialog; | ||||
|  | @ -620,20 +600,11 @@ onDOMready().then(() => { | |||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   function measureInput(input) { | ||||
|     const style = getComputedStyle(input); | ||||
|     input._padding = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom); | ||||
|     input._maxWidth = parseFloat(style.maxWidth); | ||||
|     input._rowHeight = input.clientHeight - input._padding; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   function destroyDialog({restoreFocus = false} = {}) { | ||||
|     state.input = null; | ||||
|     $.remove(DIALOG_SELECTOR); | ||||
|     debounce.unregister(doSearch); | ||||
|     makeTargetVisible(null); | ||||
|     removeEventListener('resize', toggleReplaceButtonTooltips, {passive: true}); | ||||
|     if (restoreFocus) setTimeout(focusNoScroll, 0, state.originalFocus); | ||||
|   } | ||||
| 
 | ||||
|  | @ -675,20 +646,10 @@ onDOMready().then(() => { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   function toggleReplaceButtonTooltips(debounced) { | ||||
|     if (debounced !== true) { | ||||
|       debounce(toggleReplaceButtonTooltips, 0, true); | ||||
|     } else { | ||||
|       const addTitle = window.innerWidth <= NARROW_WIDTH; | ||||
|       for (const el of state.dialog.getElementsByTagName('button')) { | ||||
|         if (addTitle && !el.title) { | ||||
|           el.title = el.textContent; | ||||
|         } else if (!addTitle && el.title) { | ||||
|           el.title = ''; | ||||
|         } else { | ||||
|           break; | ||||
|         } | ||||
|   function enableUndoButton(enabled) { | ||||
|     if (state.dialog && state.dialog.dataset.type === 'replace') { | ||||
|       for (const el of $$('[data-action="undo"]', state.dialog)) { | ||||
|         el.disabled = !enabled; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user