1+ import './StringField.css' ;
2+
13import { TextInput , Textarea } from '@mantine/core' ;
24import { ChangeEvent } from 'preact/compat' ;
5+ import { useRef } from 'preact/hooks' ;
36import * as schema from '../../../../core/schema.js' ;
47import { useDraftDocValue } from '../../../hooks/useDraftDoc.js' ;
58import { requestHighlightNode } from '../../../utils/iframe-preview.js' ;
69import { FieldProps } from './FieldProps.js' ;
710
11+ interface HighlighterProps {
12+ value : string ;
13+ variant : 'input' | 'textarea' ;
14+ scrollRef ?: any ;
15+ }
16+
17+ function Highlighter ( props : HighlighterProps ) {
18+ const { value, variant, scrollRef} = props ;
19+ const parts = value . split ( / ( [ \u00A0 \u2011 ] ) / g) ;
20+ return (
21+ < div
22+ ref = { scrollRef }
23+ className = { `StringField__highlighter StringField__highlighter--${ variant } ` }
24+ aria-hidden = "true"
25+ >
26+ { parts . map ( ( part , i ) => {
27+ if ( part === '\u00A0' || part === '\u2011' ) {
28+ return (
29+ < span key = { i } className = "StringField__highlight" >
30+ { part }
31+ </ span >
32+ ) ;
33+ }
34+ return < span key = { i } > { part } </ span > ;
35+ } ) }
36+ { variant === 'textarea' && value . endsWith ( '\n' ) && < br /> }
37+ </ div >
38+ ) ;
39+ }
40+
841export function StringField ( props : FieldProps ) {
942 const field = props . field as schema . StringField ;
1043 const [ value , setValue ] = useDraftDocValue ( props . deepKey , '' ) ;
44+ const highlighterRef = useRef < HTMLDivElement > ( null ) ;
45+ const inputHighlighterRef = useRef < HTMLDivElement > ( null ) ;
1146
1247 const onChange = ( e : ChangeEvent < HTMLInputElement > ) => {
1348 setValue ( e . currentTarget . value ) ;
@@ -23,29 +58,60 @@ export function StringField(props: FieldProps) {
2358 requestHighlightNode ( null ) ;
2459 } ;
2560
61+ const onScroll = ( e : any ) => {
62+ if ( highlighterRef . current ) {
63+ highlighterRef . current . scrollTop = e . target . scrollTop ;
64+ highlighterRef . current . scrollLeft = e . target . scrollLeft ;
65+ }
66+ } ;
67+
68+ const onInputScroll = ( e : any ) => {
69+ if ( inputHighlighterRef . current ) {
70+ inputHighlighterRef . current . scrollLeft = e . target . scrollLeft ;
71+ }
72+ } ;
73+
2674 if ( field . variant === 'textarea' ) {
2775 return (
28- < Textarea
76+ < div className = "StringField__container" >
77+ < Highlighter
78+ value = { value || '' }
79+ variant = "textarea"
80+ scrollRef = { highlighterRef }
81+ />
82+ < Textarea
83+ className = "StringField__input"
84+ size = "xs"
85+ radius = { 0 }
86+ autosize = { field . autosize }
87+ minRows = { 4 }
88+ maxRows = { field . maxRows || 12 }
89+ value = { value }
90+ onChange = { onChange }
91+ onFocus = { onFocus }
92+ onBlur = { onBlur }
93+ onScroll = { onScroll }
94+ />
95+ </ div >
96+ ) ;
97+ }
98+ return (
99+ < div className = "StringField__container" >
100+ < Highlighter
101+ value = { value || '' }
102+ variant = "input"
103+ scrollRef = { inputHighlighterRef }
104+ />
105+ < TextInput
106+ className = "StringField__input"
29107 size = "xs"
30108 radius = { 0 }
31- autosize = { field . autosize }
32- minRows = { 4 }
33- maxRows = { field . maxRows || 12 }
34109 value = { value }
35110 onChange = { onChange }
36111 onFocus = { onFocus }
37112 onBlur = { onBlur }
113+ onScroll = { onInputScroll }
38114 />
39- ) ;
40- }
41- return (
42- < TextInput
43- size = "xs"
44- radius = { 0 }
45- value = { value }
46- onChange = { onChange }
47- onFocus = { onFocus }
48- onBlur = { onBlur }
49- />
115+ </ div >
50116 ) ;
51117}
0 commit comments