this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Multiple fixes on composer highlighting

- Hide scrollbar for the faux highlight div
- Use unicode-aware split for highlighting exceeded characters
- Disable highlight of mentions, hashtags, etc if exceeded max characters
- Sync scroll as often as possible

+23 -6
+6
package-lock.json
··· 32 32 "react-intersection-observer": "~9.5.3", 33 33 "react-quick-pinch-zoom": "~5.1.0", 34 34 "react-router-dom": "6.6.2", 35 + "runes2": "~1.1.3", 35 36 "string-length": "5.0.1", 36 37 "swiped-events": "~1.1.9", 37 38 "toastify-js": "~1.12.0", ··· 6056 6057 "dependencies": { 6057 6058 "queue-microtask": "^1.2.2" 6058 6059 } 6060 + }, 6061 + "node_modules/runes2": { 6062 + "version": "1.1.3", 6063 + "resolved": "https://registry.npmjs.org/runes2/-/runes2-1.1.3.tgz", 6064 + "integrity": "sha512-sJ/0iVFLne4f2S7cMB1OckBtC9lqkzP5a/wPnDIkbrWzgUsJ+JMQv6y7hk76U7zvbua+je5GltfpsZazUhG05w==" 6059 6065 }, 6060 6066 "node_modules/safe-buffer": { 6061 6067 "version": "5.2.1",
+1
package.json
··· 34 34 "react-intersection-observer": "~9.5.3", 35 35 "react-quick-pinch-zoom": "~5.1.0", 36 36 "react-router-dom": "6.6.2", 37 + "runes2": "~1.1.3", 37 38 "string-length": "5.0.1", 38 39 "swiped-events": "~1.1.9", 39 40 "toastify-js": "~1.12.0",
+5
src/components/compose.css
··· 653 653 white-space: pre-wrap; 654 654 min-height: 5em; 655 655 max-height: 50vh; 656 + scrollbar-width: none; 657 + 658 + &::-webkit-scrollbar { 659 + display: none; 660 + } 656 661 657 662 /* Follow textarea styles */ 658 663 @media (min-width: 40em) {
+11 -6
src/components/compose.jsx
··· 5 5 import { forwardRef } from 'preact/compat'; 6 6 import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; 7 7 import { useHotkeys } from 'react-hotkeys-hook'; 8 + import { substring } from 'runes2'; 8 9 import stringLength from 'string-length'; 9 10 import { uid } from 'uid/single'; 10 11 import { useDebouncedCallback, useThrottledCallback } from 'use-debounce'; ··· 131 132 const { composerCharacterCount } = states; 132 133 let leftoverHTML = ''; 133 134 if (composerCharacterCount > maxCharacters) { 134 - const leftoverCount = composerCharacterCount - maxCharacters; 135 + // NOTE: runes2 substring considers surrogate pairs 136 + // const leftoverCount = composerCharacterCount - maxCharacters; 135 137 // Highlight exceeded characters 136 138 leftoverHTML = 137 139 '<mark class="compose-highlight-exceeded">' + 138 - html.slice(-leftoverCount) + 140 + // html.slice(-leftoverCount) + 141 + substring(html, maxCharacters) + 139 142 '</mark>'; 140 - html = html.slice(0, -leftoverCount); 143 + // html = html.slice(0, -leftoverCount); 144 + html = substring(html, 0, maxCharacters); 145 + return html + leftoverHTML; 141 146 } 142 147 143 - html = html 148 + return html 144 149 .replace(urlRegexObj, '$2<mark class="compose-highlight-url">$3</mark>') // URLs 145 150 .replace(MENTION_RE, '$1<mark class="compose-highlight-mention">$2</mark>') // Mentions 146 151 .replace(HASHTAG_RE, '$1<mark class="compose-highlight-hashtag">$2</mark>') // Hashtags ··· 148 153 SCAN_RE, 149 154 '$1<mark class="compose-highlight-emoji-shortcode">$2</mark>', 150 155 ); // Emoji shortcodes 151 - 152 - return html + leftoverHTML; 153 156 } 154 157 155 158 function Compose({ ··· 1538 1541 target.setRangeText('', pos, selectionStart); 1539 1542 } 1540 1543 autoResizeTextarea(target); 1544 + target.dispatchEvent(new Event('input')); 1541 1545 } 1542 1546 } 1543 1547 } catch (e) { ··· 1545 1549 console.error(e); 1546 1550 } 1547 1551 } 1552 + composeHighlightRef.current.scrollTop = target.scrollTop; 1548 1553 }} 1549 1554 onInput={(e) => { 1550 1555 const { target } = e;