diff --git a/yaba-web/src/components/bookmarks/bookmark.jsx b/yaba-web/src/components/bookmarks/bookmark.jsx index cc3c09d..af392d9 100644 --- a/yaba-web/src/components/bookmarks/bookmark.jsx +++ b/yaba-web/src/components/bookmarks/bookmark.jsx @@ -1,24 +1,28 @@ import React from "react"; import { Row, Col, Form, Button } from "../external"; import DateTimeHelper from "../../utils/dateTimeHelper"; -import "../../styles/component/bookmark.css"; +import { getHighlightedText } from "../../utils"; import { useNavigate } from "react-router-dom"; import { Tag } from "../../components"; export function Bookmark(props) { const navigate = useNavigate(); + const getTruncatedDescription = () => `${props.bookmark.description.substring(0, 97)}${props.bookmark.description.length >= 97 ? "..." : ""}`; + const getHighlightedTitle = () => getHighlightedText(props.bookmark.title, props.searchString); + const getHighlightedDescription = () => getHighlightedText(getTruncatedDescription(), props.searchString) + return
{props.onBookmarkSelected(e.target.checked)}} checked={props.bookmark.isSelected} /> - {props.bookmark.title} -
{`${props.bookmark.description.substring(0, 97)}${props.bookmark.description.length >= 97 ? "..." : ""}`}
+ { getHighlightedTitle() } +
{ getHighlightedDescription()}
{ - props.bookmark.tags.map((tag) => ) + props.bookmark.tags.map((tag) => ) }
diff --git a/yaba-web/src/components/tags/tag.jsx b/yaba-web/src/components/tags/tag.jsx index 8709194..d24e993 100644 --- a/yaba-web/src/components/tags/tag.jsx +++ b/yaba-web/src/components/tags/tag.jsx @@ -1,13 +1,16 @@ import React from "react"; import { Button } from "../external"; +import { getHighlightedText } from "../../utils"; export function Tag(props) { + const getHighlightedTagName = () => props.searchString ? getHighlightedText(props.tag.name, props.searchString) : props.tag.name + return ( ); }; \ No newline at end of file diff --git a/yaba-web/src/utils/index.js b/yaba-web/src/utils/index.js index 523bb57..31cce7c 100644 --- a/yaba-web/src/utils/index.js +++ b/yaba-web/src/utils/index.js @@ -2,11 +2,13 @@ import { isDev } from "./isDevHelper"; import { getTagGroups, flattenTagArrays } from "./tagsHelper"; import { isSubset } from "./arrayHelper"; import { containsSubstring } from "./bookmarkHelper"; +import { getHighlightedText } from "./stringsHelper"; export { isDev, getTagGroups, isSubset, containsSubstring, - flattenTagArrays + flattenTagArrays, + getHighlightedText, } \ No newline at end of file diff --git a/yaba-web/src/utils/stringsHelper.js b/yaba-web/src/utils/stringsHelper.js new file mode 100644 index 0000000..ef17ab5 --- /dev/null +++ b/yaba-web/src/utils/stringsHelper.js @@ -0,0 +1,10 @@ +// https://stackoverflow.com/questions/29652862/highlight-text-using-reactjs +export const getHighlightedText = (text, highlight) => { + // Split on highlight term and include term into parts, ignore case + const parts = text.split(new RegExp(`(${highlight})`, 'gi')); + return { parts.map((part, i) => + + { part } + ) + } ; +} \ No newline at end of file diff --git a/yaba-web/src/views/bookmarksListView.jsx b/yaba-web/src/views/bookmarksListView.jsx index 837e9d2..18afe94 100644 --- a/yaba-web/src/views/bookmarksListView.jsx +++ b/yaba-web/src/views/bookmarksListView.jsx @@ -323,6 +323,7 @@ export function BookmarksListView(props) { onBookmarkSelected={(selected) => onBookmarkSelected(selected, bookmark)} onDeleteClicked={() => handleDeleteBookmark(bookmark.id)} onHideClicked={() => handleHideBookmarks([bookmark.id])} + searchString={searchString} /> }) } diff --git a/yaba-web/src/views/tagsView.jsx b/yaba-web/src/views/tagsView.jsx index f35f1cd..a4cf506 100644 --- a/yaba-web/src/views/tagsView.jsx +++ b/yaba-web/src/views/tagsView.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useReducer, useState } from "react"; import { useAuth0 } from "@auth0/auth0-react"; -import { containsSubstring } from "../utils"; +import { containsSubstring, getHighlightedText } from "../utils"; import { SplashScreen, SearchForm, UpsertTagModal, InterceptModal } from "../components"; import { Alert, Button, Col, Container, Dropdown, DropdownButton, Form, Row, Table } from "../components/external"; import { getAllTags, createNewTag, updateTag, deleteTags, hideTags } from "../api"; @@ -9,24 +9,6 @@ export function TagsView(props) { const { getAccessTokenSilently } = useAuth0(); const [searchString, setSearchString] = useState(""); - const handleSearch = (e) => { - e.preventDefault(); - - if(!searchString) { - dispatchTagsState({type: "DISPLAY_ALL"}); - } else { - dispatchTagsState({type: "SEARCH"}); - } - }; - - const handleSearchStringChange = (e) => { - if(!e.target.value) { - dispatchTagsState({type: "DISPLAY_ALL"}); - } else { - setSearchString(e.target.value); - } - }; - const [isAllTagsSelected, setAllTagsSelected] = useState(false); const initialSplashScreenState = { show: false, message: null }; @@ -127,11 +109,7 @@ export function TagsView(props) { newState = state.map(x => ({...x, isDisplayed: true})); break; case "SEARCH": - if(!searchString) { - dispatchTagsState({type: "DISPLAY_ALL"}); - } - - newState = state.map(x => ({...x, isDisplayed: containsSubstring(x, searchString)})); + newState = state.map(x => ({...x, isDisplayed: !searchString || containsSubstring(x, searchString)})); break; case "TAG_UPDATE": newState = [...state]; @@ -314,6 +292,17 @@ export function TagsView(props) { } }; + const handleSearch = (e) => { + e.preventDefault(); + setSearchString(e.target[0].value); + dispatchTagsState({type: "SEARCH"}); + }; + + const handleSearchStringChange = (e) => { + setSearchString(e.target.value); + dispatchTagsState({type: "SEARCH"}); + }; + useEffect(() => { dispatchSplashScreenState({type: "SHOW_SPLASH_SCREEN", payload: {message: "Retrieving Tags..."}}); fetchTags(); @@ -409,7 +398,7 @@ export function TagsView(props) { checked={tag.isSelected} /> - {tag.name} + {getHighlightedText(tag.name, searchString)} { tag.isHidden ? "Yes" : "No" }