Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added keyboard text selection support #118

Merged
merged 83 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
21677a9
Adapted user select action usage
oleksandr-danylchenko Jul 9, 2024
580082b
Made the content focusable
oleksandr-danylchenko Jul 9, 2024
e9832d5
Generalized down event recording
oleksandr-danylchenko Jul 9, 2024
1168059
Added explicit pointer event cloning
oleksandr-danylchenko Jul 9, 2024
ffa46eb
Added keyboard event registering
oleksandr-danylchenko Jul 10, 2024
0bbe064
Re-exported select action
oleksandr-danylchenko Jul 10, 2024
268c6e3
Fixed keyboard selection detection w/o prior pointer event
oleksandr-danylchenko Jul 10, 2024
1780bf0
Added keyboard selection finish handling
oleksandr-danylchenko Jul 12, 2024
11aae73
Allowed shifting over the selection content
oleksandr-danylchenko Jul 18, 2024
ae27926
Merge branch 'refs/heads/main' into keyboard-event-selection
oleksandr-danylchenko Jul 23, 2024
e4a4b69
Added reaction to the user event
oleksandr-danylchenko Jul 23, 2024
5fb2789
Merge branch 'refs/heads/main' into keyboard-event-selection
oleksandr-danylchenko Jul 30, 2024
461bcac
Merge branch 'refs/heads/main' into keyboard-event-selection
oleksandr-danylchenko Jul 30, 2024
719edaf
Added annotation discarding when the selection collapses
oleksandr-danylchenko Aug 16, 2024
753cee9
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Aug 16, 2024
e20c04e
Updated `currentTarget` check comment
oleksandr-danylchenko Aug 16, 2024
0e2f5cf
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Aug 16, 2024
0bdc419
Added completion for the keyboard selection
oleksandr-danylchenko Aug 16, 2024
dfb2e19
Merge branch '#136-fix-not-dismisssed-annotation-on-native-range-clic…
oleksandr-danylchenko Aug 16, 2024
34b3585
Made popup appear on `keydown`
oleksandr-danylchenko Aug 19, 2024
3442d4b
Added closing of the popup on selection cleanup
oleksandr-danylchenko Aug 19, 2024
bf6ef6f
Added focusing on the popup content on `pointerup` event
oleksandr-danylchenko Aug 19, 2024
9df1a89
Made the `TextAnnotatorPopup` handle the focus for the popup content
oleksandr-danylchenko Aug 19, 2024
b2b25c1
Added focus message supply
oleksandr-danylchenko Aug 20, 2024
d56359c
Added automatic focusing on the popup with the focus manager
oleksandr-danylchenko Aug 20, 2024
5e4944b
Removed unused `ts-key-enum`
oleksandr-danylchenko Aug 20, 2024
c8159a1
Fixed arrow button not dismissing selection, when the focus isn't on …
oleksandr-danylchenko Aug 20, 2024
4b6cbc2
Disabled autofocus for the keyboard selection
oleksandr-danylchenko Aug 20, 2024
fde29cc
Named `useAnnotator` result consistently
oleksandr-danylchenko Aug 20, 2024
d1263ad
Merge branch 'keyboard-popup-focus-handling' into keyboard-event-sele…
oleksandr-danylchenko Aug 20, 2024
d6609a6
Removed unused `TextAnnotatorPopup` props
oleksandr-danylchenko Aug 20, 2024
e0bb05f
Made the popup non-modal
oleksandr-danylchenko Aug 20, 2024
9b2df10
Added caret position restoration upon closing the popup
oleksandr-danylchenko Aug 20, 2024
5653502
Made the caret restored on the up-to-date position
oleksandr-danylchenko Aug 20, 2024
22481b1
Removed unused ref propagation
oleksandr-danylchenko Aug 20, 2024
b86b86d
Added selection caret restoration hook
oleksandr-danylchenko Aug 20, 2024
a3b589c
Fixed jumping viewport on focus return
oleksandr-danylchenko Aug 21, 2024
fd6c07b
Made the caret restored on the selection range start
oleksandr-danylchenko Aug 21, 2024
94b182f
Limited arrow listener only to the annotatable container
oleksandr-danylchenko Aug 21, 2024
66ede9f
Added focusing on the content upon restoring the caret
oleksandr-danylchenko Aug 21, 2024
6afa6fe
Added selection discard on focus leaving the popup
oleksandr-danylchenko Aug 21, 2024
7b71687
Added sr-only popup close message
oleksandr-danylchenko Aug 21, 2024
17a1606
Typo fix
oleksandr-danylchenko Aug 23, 2024
8aeec59
Added popup rendering above the highlights
oleksandr-danylchenko Aug 23, 2024
5014052
Moved `currentTarget` cleanup into the `pointerdown` handler
oleksandr-danylchenko Aug 23, 2024
1ac04d0
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Aug 23, 2024
8684a8a
Added annotation reference cleanup
oleksandr-danylchenko Aug 27, 2024
74e8e13
Bumped `floating-ui` to `^0.26.23`
oleksandr-danylchenko Aug 28, 2024
e6f8540
Added `focus-out` reason listening
oleksandr-danylchenko Aug 28, 2024
12da782
Removed duplicated `currentTarget` cleanup
oleksandr-danylchenko Aug 28, 2024
9517a97
Added the whole selection range restoration upon popup dismissal
oleksandr-danylchenko Aug 28, 2024
ea1fc24
Removed the selection range restoration hook
oleksandr-danylchenko Aug 28, 2024
356f72e
Fixed processing of a collapsed selection on `selectionchange` event
oleksandr-danylchenko Aug 28, 2024
3a57f75
Removed testing log
oleksandr-danylchenko Aug 28, 2024
d0d14db
Added arrow movement handling on the `body`
oleksandr-danylchenko Aug 29, 2024
fd74af8
Limited arrow handling to the container & body explicitly
oleksandr-danylchenko Aug 29, 2024
1883141
Merge branch 'main' into fix-annotation-deselection
oleksandr-danylchenko Aug 30, 2024
fcbfc8f
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Aug 30, 2024
247ef33
Merge branch '#136-fix-not-dismisssed-annotation-on-native-range-clic…
oleksandr-danylchenko Aug 30, 2024
690008f
Moved `currentTarget` reset back to the `pointerup` handler
oleksandr-danylchenko Sep 2, 2024
ef3c620
Added `selectstart` emulation when text is clicked
oleksandr-danylchenko Sep 2, 2024
38ad41d
Added existence check before explicit reselection
oleksandr-danylchenko Sep 2, 2024
be8478b
Merge branch 'main' into fix-annotation-deselection
oleksandr-danylchenko Sep 2, 2024
2dcb13c
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Sep 2, 2024
dcd8291
Updated popup props
oleksandr-danylchenko Sep 2, 2024
1252404
Limited emulation logic only to the pointer events
oleksandr-danylchenko Sep 2, 2024
51a92c6
Merge branch 'fix-testing-component-popup-rendering' into keyboard-ev…
oleksandr-danylchenko Sep 2, 2024
b35904c
Added `FC` usage of the tei test popup
oleksandr-danylchenko Sep 2, 2024
9316a66
Merge branch '#136-fix-not-dismisssed-annotation-on-native-range-clic…
oleksandr-danylchenko Sep 2, 2024
166540e
Merge branch 'main' into fix-annotation-deselection
oleksandr-danylchenko Sep 3, 2024
90861b4
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Sep 3, 2024
1b9a5b1
Re-hash
oleksandr-danylchenko Sep 3, 2024
c6f71d3
Re-hash
oleksandr-danylchenko Sep 3, 2024
694247d
Added `pointerdown` capturing on the `document`
oleksandr-danylchenko Sep 3, 2024
ed63ccd
Added ignoring `pointerdown` on `not-annotatable`
oleksandr-danylchenko Sep 3, 2024
92e4156
Merge branch 'main' into #133-fix-not-dismissed-annotation-on-outside…
oleksandr-danylchenko Sep 10, 2024
1ce62a0
Merge branch 'main' into fix-annotation-deselection
oleksandr-danylchenko Sep 10, 2024
cbe374d
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Sep 10, 2024
43872e8
Merge branch '#133-fix-not-dismissed-annotation-on-outside-click' int…
oleksandr-danylchenko Sep 10, 2024
dc27158
Merge branch 'main' into #133-fix-not-dismissed-annotation-on-outside…
oleksandr-danylchenko Sep 26, 2024
5d7ddcc
Merge branch 'main' into fix-annotation-deselection
oleksandr-danylchenko Sep 26, 2024
fdb047a
Merge branch 'fix-annotation-deselection' into keyboard-event-selection
oleksandr-danylchenko Sep 26, 2024
d18ff7b
Merge branch '#133-fix-not-dismissed-annotation-on-outside-click' int…
oleksandr-danylchenko Sep 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/text-annotator-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@
"@recogito/text-annotator-tei": "3.0.0-rc.46",
"CETEIcean": "^1.9.3"
}
}
}
124 changes: 0 additions & 124 deletions packages/text-annotator-react/src/TextAnnotatorPopup.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Close message should be visible only to the keyboard
* or the screen reader users as the popup behavior hint
* Inspired by https://gist.github.com/ffoodd/000b59f431e3e64e4ce1a24d5bb36034
*/
.popup-close-message {
border: 0 !important;
clip: rect(1px, 1px, 1px, 1px);
-webkit-clip-path: inset(50%);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}

.popup-close-message:focus,
.popup-close-message:active {
clip: auto;
-webkit-clip-path: none;
clip-path: none;
height: auto;
margin: auto;
overflow: visible;
width: auto;
white-space: normal;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import React, { PointerEvent, ReactNode, useCallback, useEffect, useState } from 'react';
import {
autoUpdate,
flip,
FloatingFocusManager,
FloatingPortal,
inline,
offset,
shift,
useDismiss,
useFloating,
useInteractions,
useRole
} from '@floating-ui/react';

import { useAnnotator, useSelection } from '@annotorious/react';
import type { TextAnnotation, TextAnnotator } from '@recogito/text-annotator';

import './TextAnnotatorPopup.css';

interface TextAnnotationPopupProps {

popup(props: TextAnnotationPopupContentProps): ReactNode;

}

export interface TextAnnotationPopupContentProps {

annotation: TextAnnotation;

editable?: boolean;

event?: PointerEvent;

}

export const TextAnnotatorPopup = (props: TextAnnotationPopupProps) => {

const r = useAnnotator<TextAnnotator>();

const { selected, event } = useSelection<TextAnnotation>();
const annotation = selected[0]?.annotation;

const [isOpen, setOpen] = useState(selected?.length > 0);

const handleClose = () => {
r?.cancelSelected();
};

const { refs, floatingStyles, update, context } = useFloating({
placement: 'top',
open: isOpen,
onOpenChange: (open, _event, reason) => {
setOpen(open);

if (!open) {
if (reason === 'escape-key' || reason === 'focus-out') {
r?.cancelSelected();
}
}
},
middleware: [
offset(10),
inline(),
flip(),
shift({ mainAxis: false, crossAxis: true, padding: 10 })
],
whileElementsMounted: autoUpdate
});

const dismiss = useDismiss(context);
const role = useRole(context, { role: 'dialog' });
const { getFloatingProps } = useInteractions([dismiss, role]);

const selectedKey = selected.map(a => a.annotation.id).join('-');
useEffect(() => {
// Ignore all selection changes except those accompanied by a user event.
if (selected.length > 0 && event) {
setOpen(event.type === 'pointerup' || event.type === 'keydown');
}
}, [selectedKey, event]);

useEffect(() => {
// Close the popup if the selection is cleared
if (selected.length === 0 && isOpen) {
setOpen(false);
}
}, [isOpen, selectedKey]);

useEffect(() => {
if (isOpen && annotation) {
const {
target: {
selector: [{ range }]
}
} = annotation;

refs.setPositionReference({
getBoundingClientRect: range.getBoundingClientRect.bind(range),
getClientRects: range.getClientRects.bind(range)
});
} else {
// Don't leave the reference depending on the previously selected annotation
refs.setPositionReference(null);
}
}, [isOpen, annotation, refs]);

// Prevent text-annotator from handling the irrelevant events triggered from the popup
const getStopEventsPropagationProps = useCallback(
() => ({ onPointerUp: (event: PointerEvent<HTMLDivElement>) => event.stopPropagation() }),
[]
);

useEffect(() => {
const config: MutationObserverInit = { attributes: true, childList: true, subtree: true };

const mutationObserver = new MutationObserver(() => update());
mutationObserver.observe(document.body, config);

window.document.addEventListener('scroll', update, true);

return () => {
mutationObserver.disconnect();
window.document.removeEventListener('scroll', update, true);
};
}, [update]);

return isOpen && selected.length > 0 ? (
<FloatingPortal>
<FloatingFocusManager
context={context}
modal={false}
closeOnFocusOut={true}
initialFocus={
/**
* Don't shift focus to the floating element
* when the selection performed with the keyboard
*/
event?.type === 'keydown' ? -1 : 0
}
returnFocus={false}
>
<div
className="annotation-popup text-annotation-popup not-annotatable"
ref={refs.setFloating}
style={floatingStyles}
{...getFloatingProps()}
{...getStopEventsPropagationProps()}>
{props.popup({
annotation: selected[0].annotation,
editable: selected[0].editable,
event
})}

{/* It lets keyboard/sr users to know that the dialog closes when they focus out of it */}
<button className="popup-close-message" onClick={handleClose}>
This dialog closes when you leave it.
</button>
</div>
</FloatingFocusManager>
</FloatingPortal>
) : null;

};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './TextAnnotatorPopup';
2 changes: 1 addition & 1 deletion packages/text-annotator-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type {
export {
createBody,
Origin,
UserSelectAction,
UserSelectAction
} from '@annotorious/core';

// Essential re-exports from @annotorious/react
Expand Down
Loading