Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
06c4bdec
Commit
06c4bdec
authored
Nov 16, 2022
by
Lyza Danger Gardner
Committed by
Lyza Gardner
Dec 05, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Convert TagEditor to TS
parent
87e819a4
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
23 additions
and
39 deletions
+23
-39
TagEditor.tsx
src/sidebar/components/TagEditor.tsx
+23
-39
No files found.
src/sidebar/components/TagEditor.
js
→
src/sidebar/components/TagEditor.
tsx
View file @
06c4bdec
...
@@ -3,32 +3,29 @@ import { Input } from '@hypothesis/frontend-shared/lib/next';
...
@@ -3,32 +3,29 @@ import { Input } from '@hypothesis/frontend-shared/lib/next';
import
{
useRef
,
useState
}
from
'preact/hooks'
;
import
{
useRef
,
useState
}
from
'preact/hooks'
;
import
{
withServices
}
from
'../service-context'
;
import
{
withServices
}
from
'../service-context'
;
import
type
{
TagsService
}
from
'../services/tags'
;
import
AutocompleteList
from
'./AutocompleteList'
;
import
AutocompleteList
from
'./AutocompleteList'
;
import
TagList
from
'./TagList'
;
import
TagList
from
'./TagList'
;
import
TagListItem
from
'./TagListItem'
;
import
TagListItem
from
'./TagListItem'
;
/** @typedef {import("preact").JSX.Element} JSXElement */
// Global counter used to create a unique id for each instance of a TagEditor
// Global counter used to create a unique id for each instance of a TagEditor
let
tagEditorIdCounter
=
0
;
let
tagEditorIdCounter
=
0
;
/**
export
type
TagEditorProps
=
{
* @typedef TagEditorProps
onAddTag
:
(
tag
:
string
)
=>
boolean
;
* @prop {(tag: string) => boolean} onAddTag - Callback to add a tag to the annotation
onRemoveTag
:
(
tag
:
string
)
=>
boolean
;
* @prop {(tag: string) => boolean} onRemoveTag - Callback to remove a tag from the annotation
onTagInput
:
(
tag
:
string
)
=>
void
;
* @prop {(tag: string) => void} onTagInput - Callback when inputted tag text changes
tagList
:
string
[];
* @prop {string[]} tagList - The list of tags for the annotation under edit
* @prop {import('../services/tags').TagsService} tags
*/
// injected
tags
:
TagsService
;
};
/**
/**
* Component to edit annotation's tags.
* Component to edit annotation's tags.
*
*
* Component accessibility is modeled after "Combobox with Listbox Popup Examples" found here:
* Component accessibility is modeled after "Combobox with Listbox Popup Examples" found here:
* https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
* https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
*
* @param {TagEditorProps} props
*/
*/
function
TagEditor
({
function
TagEditor
({
onAddTag
,
onAddTag
,
...
@@ -36,9 +33,9 @@ function TagEditor({
...
@@ -36,9 +33,9 @@ function TagEditor({
onTagInput
,
onTagInput
,
tagList
,
tagList
,
tags
:
tagsService
,
tags
:
tagsService
,
})
{
}
:
TagEditorProps
)
{
const
inputEl
=
/** @type {{ current: HTMLInputElement }} */
(
useRef
()
);
const
inputEl
=
useRef
<
HTMLInputElement
>
(
);
const
[
suggestions
,
setSuggestions
]
=
useState
(
/** @type {string[]} */
([])
);
const
[
suggestions
,
setSuggestions
]
=
useState
(
[]
as
string
[]
);
const
[
activeItem
,
setActiveItem
]
=
useState
(
-
1
);
// -1 is unselected
const
[
activeItem
,
setActiveItem
]
=
useState
(
-
1
);
// -1 is unselected
const
[
suggestionsListOpen
,
setSuggestionsListOpen
]
=
useState
(
false
);
const
[
suggestionsListOpen
,
setSuggestionsListOpen
]
=
useState
(
false
);
const
[
tagEditorId
]
=
useState
(()
=>
{
const
[
tagEditorId
]
=
useState
(()
=>
{
...
@@ -47,7 +44,7 @@ function TagEditor({
...
@@ -47,7 +44,7 @@ function TagEditor({
});
});
// Set up callback to monitor outside click events to close the AutocompleteList
// Set up callback to monitor outside click events to close the AutocompleteList
const
closeWrapperRef
=
/** @type {{ current: HTMLDivElement }} */
(
useRef
()
);
const
closeWrapperRef
=
useRef
<
HTMLDivElement
>
(
null
);
useElementShouldClose
(
closeWrapperRef
,
suggestionsListOpen
,
()
=>
{
useElementShouldClose
(
closeWrapperRef
,
suggestionsListOpen
,
()
=>
{
setSuggestionsListOpen
(
false
);
setSuggestionsListOpen
(
false
);
});
});
...
@@ -55,24 +52,20 @@ function TagEditor({
...
@@ -55,24 +52,20 @@ function TagEditor({
/**
/**
* Retrieve the current trimmed text value of the tag <input>
* Retrieve the current trimmed text value of the tag <input>
*/
*/
const
pendingTag
=
()
=>
inputEl
.
current
.
value
.
trim
();
const
pendingTag
=
()
=>
inputEl
.
current
!
.
value
.
trim
();
const
hasPendingTag
=
()
=>
pendingTag
()
&&
pendingTag
().
length
>
0
;
const
hasPendingTag
=
()
=>
pendingTag
()
&&
pendingTag
().
length
>
0
;
const
clearPendingTag
=
()
=>
{
const
clearPendingTag
=
()
=>
{
inputEl
.
current
.
value
=
''
;
inputEl
.
current
!
.
value
=
''
;
onTagInput
?.(
''
);
onTagInput
?.(
''
);
};
};
/**
/**
* Helper function that returns a list of suggestions less any
* Helper function that returns a list of suggestions less any
* results also found from the duplicates list.
* results also found from the duplicates list.
*
* @param {string[]} suggestions - Original list of suggestions
* @param {string[]} duplicates - Items to be removed from the result
* @return {string[]}
*/
*/
const
removeDuplicates
=
(
suggestions
,
duplicates
)
=>
{
const
removeDuplicates
=
(
suggestions
:
string
[],
duplicates
:
string
[]
)
=>
{
const
suggestionsSet
=
[];
const
suggestionsSet
=
[];
for
(
le
t
suggestion
of
suggestions
)
{
for
(
cons
t
suggestion
of
suggestions
)
{
if
(
duplicates
.
indexOf
(
suggestion
)
<
0
)
{
if
(
duplicates
.
indexOf
(
suggestion
)
<
0
)
{
suggestionsSet
.
push
(
suggestion
);
suggestionsSet
.
push
(
suggestion
);
}
}
...
@@ -102,16 +95,14 @@ function TagEditor({
...
@@ -102,16 +95,14 @@ function TagEditor({
/**
/**
* Invokes callback to add tag. If the tag was added, close the suggestions
* Invokes callback to add tag. If the tag was added, close the suggestions
* list, clear the field content and maintain focus.
* list, clear the field content and maintain focus.
*
* @param {string} newTag
*/
*/
const
addTag
=
newTag
=>
{
const
addTag
=
(
newTag
:
string
)
=>
{
if
(
onAddTag
(
newTag
))
{
if
(
onAddTag
(
newTag
))
{
setSuggestionsListOpen
(
false
);
setSuggestionsListOpen
(
false
);
setActiveItem
(
-
1
);
setActiveItem
(
-
1
);
clearPendingTag
();
clearPendingTag
();
inputEl
.
current
.
focus
();
inputEl
.
current
!
.
focus
();
}
}
};
};
...
@@ -123,10 +114,8 @@ function TagEditor({
...
@@ -123,10 +114,8 @@ function TagEditor({
/**
/**
* Callback when the user clicked one of the items in the suggestions list.
* Callback when the user clicked one of the items in the suggestions list.
* This will add a new tag.
* This will add a new tag.
*
* @param {string} item
*/
*/
const
handleSelect
=
item
=>
{
const
handleSelect
=
(
item
:
string
)
=>
{
if
(
item
)
{
if
(
item
)
{
addTag
(
item
);
addTag
(
item
);
}
}
...
@@ -152,7 +141,7 @@ function TagEditor({
...
@@ -152,7 +141,7 @@ function TagEditor({
*
*
* @param {number} direction - Pass 1 for the next item or -1 for the previous
* @param {number} direction - Pass 1 for the next item or -1 for the previous
*/
*/
const
changeSelectedItem
=
direction
=>
{
const
changeSelectedItem
=
(
direction
:
-
1
|
1
)
=>
{
let
nextActiveItem
=
activeItem
+
direction
;
let
nextActiveItem
=
activeItem
+
direction
;
if
(
nextActiveItem
<
-
1
)
{
if
(
nextActiveItem
<
-
1
)
{
nextActiveItem
=
suggestions
.
length
-
1
;
nextActiveItem
=
suggestions
.
length
-
1
;
...
@@ -165,10 +154,8 @@ function TagEditor({
...
@@ -165,10 +154,8 @@ function TagEditor({
/**
/**
* Keydown handler for keyboard navigation of the tag editor field and the
* Keydown handler for keyboard navigation of the tag editor field and the
* suggested-tags list.
* suggested-tags list.
*
* @param {KeyboardEvent} e
*/
*/
const
handleKeyDown
=
e
=>
{
const
handleKeyDown
=
(
e
:
KeyboardEvent
)
=>
{
switch
(
e
.
key
)
{
switch
(
e
.
key
)
{
case
'ArrowUp'
:
case
'ArrowUp'
:
// Select the previous item in the suggestion list
// Select the previous item in the suggestion list
...
@@ -227,11 +214,8 @@ function TagEditor({
...
@@ -227,11 +214,8 @@ function TagEditor({
* Callback for formatting a suggested tag item. Use selective bolding
* Callback for formatting a suggested tag item. Use selective bolding
* to help delineate which part of the entered tag text matches the
* to help delineate which part of the entered tag text matches the
* suggestion.
* suggestion.
*
* @param {string} item - Suggested tag
* @return {JSXElement} - Formatted tag for use in list
*/
*/
const
formatSuggestedItem
=
item
=>
{
const
formatSuggestedItem
=
(
item
:
string
)
=>
{
// filtering of tags is case-insensitive
// filtering of tags is case-insensitive
const
curVal
=
pendingTag
().
toLowerCase
();
const
curVal
=
pendingTag
().
toLowerCase
();
const
suggestedTag
=
item
.
toLowerCase
();
const
suggestedTag
=
item
.
toLowerCase
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment