@@ -91,3 +91,119 @@ export function brushInkShape(container: HTMLElement, cancellationSignal: AbortS
9191 container . addEventListener ( 'touchmove' , onTouch , { capture : true } ) ;
9292 } ) ;
9393}
94+
95+ const styles = new CSSStyleSheet ( ) ;
96+ styles . replaceSync ( `
97+ html:has([folk-selected-element]) * {
98+ cursor: crosshair;
99+ }
100+
101+ [folk-selected-element] {
102+ outline: solid 1px blue !important;
103+ outline-offset: -1px;
104+ }
105+ ` ) ;
106+
107+ export function brushToSelectElements < T extends Element = Element > (
108+ container : HTMLElement ,
109+ cancellationSignal : AbortSignal ,
110+ filter ?: ( el : Element ) => T | null ,
111+ ) {
112+ return new Promise < T [ ] > ( ( resolve ) => {
113+ const elements = new Set < T > ( ) ;
114+
115+ function elementsToSelect ( event : PointerEvent ) {
116+ document . elementsFromPoint ( event . pageX , event . pageY ) . forEach ( ( el ) => {
117+ if ( filter === undefined ) {
118+ el . setAttribute ( 'folk-selected-element' , '' ) ;
119+ elements . add ( el as T ) ;
120+ return ;
121+ }
122+
123+ const filteredEl = filter ( el ) ;
124+ if ( filteredEl ) {
125+ elements . add ( el as T ) ;
126+ el . setAttribute ( 'folk-selected-element' , '' ) ;
127+ }
128+ } ) ;
129+ }
130+
131+ function onPointerDown ( event : PointerEvent ) {
132+ event . preventDefault ( ) ;
133+ event . stopImmediatePropagation ( ) ;
134+ event . stopPropagation ( ) ;
135+ container . setPointerCapture ( event . pointerId ) ;
136+
137+ container . addEventListener ( 'pointermove' , onPointerMove , { capture : true } ) ;
138+ container . addEventListener ( 'pointerup' , onPointerUp , { capture : true } ) ;
139+ elementsToSelect ( event ) ;
140+ }
141+
142+ function onPointerMove ( event : PointerEvent ) {
143+ event . preventDefault ( ) ;
144+ event . stopImmediatePropagation ( ) ;
145+ event . stopPropagation ( ) ;
146+ elementsToSelect ( event ) ;
147+ }
148+
149+ function onPointerUp ( event : PointerEvent ) {
150+ event . preventDefault ( ) ;
151+ event . stopImmediatePropagation ( ) ;
152+ event . stopPropagation ( ) ;
153+
154+ cleanUp ( ) ;
155+ resolve ( Array . from ( elements ) ) ;
156+ }
157+
158+ function onTouch ( event : TouchEvent ) {
159+ event . preventDefault ( ) ;
160+ event . stopImmediatePropagation ( ) ;
161+ event . stopPropagation ( ) ;
162+ }
163+
164+ function onCancel ( ) {
165+ cleanUp ( ) ;
166+ resolve ( [ ] ) ;
167+ }
168+
169+ function cleanUp ( ) {
170+ elements . forEach ( ( el ) => el . removeAttribute ( 'folk-selected-element' ) ) ;
171+ cancellationSignal . removeEventListener ( 'abort' , onCancel ) ;
172+ container . removeEventListener ( 'touchmove' , onTouch , { capture : true } ) ;
173+ container . removeEventListener ( 'pointerdown' , onPointerDown , { capture : true } ) ;
174+ container . removeEventListener ( 'pointermove' , onPointerMove , { capture : true } ) ;
175+ container . removeEventListener ( 'pointerup' , onPointerUp , { capture : true } ) ;
176+ container . ownerDocument . adoptedStyleSheets . splice ( container . ownerDocument . adoptedStyleSheets . indexOf ( styles ) , 1 ) ;
177+ }
178+
179+ cancellationSignal . addEventListener ( 'abort' , onCancel ) ;
180+ container . addEventListener ( 'pointerdown' , onPointerDown , { capture : true } ) ;
181+ container . addEventListener ( 'touchmove' , onTouch , { capture : true } ) ;
182+ container . ownerDocument . adoptedStyleSheets . push ( styles ) ;
183+ } ) ;
184+ }
185+
186+ const deleteStyles = new CSSStyleSheet ( ) ;
187+ styles . replaceSync ( `
188+ [folk-selected-element] {
189+ outline: none;
190+ opacity: 0.4;
191+ }
192+ ` ) ;
193+
194+ export async function brushToDeleteElements < T extends Element = Element > (
195+ container : HTMLElement ,
196+ cancellationSignal : AbortSignal ,
197+ filter ?: ( el : Element ) => T | null ,
198+ ) {
199+ container . ownerDocument . adoptedStyleSheets . push ( deleteStyles ) ;
200+ const elements = await brushToSelectElements < T > ( container , cancellationSignal , filter ) ;
201+ container . ownerDocument . adoptedStyleSheets . splice (
202+ container . ownerDocument . adoptedStyleSheets . indexOf ( deleteStyles ) ,
203+ 1 ,
204+ ) ;
205+
206+ elements . forEach ( ( el ) => el . remove ( ) ) ;
207+
208+ return elements ;
209+ }
0 commit comments