Skip to content

Commit

Permalink
Add initial global search setup
Browse files Browse the repository at this point in the history
Add search functionality and result data structures
Add preview of found items, highlighted style, the rows of found items
Add note preview
Add selection of items in search
Add text highlight in editor preview
Add editor preview focus
Add editor preview while clicking on found item
Add better ID management and state update fix
Add some stability updates (max searched content, max result line length)
Add double click on search item to lead to focused item in editor
Add focusLine number for editor to route params
Add better colors across themes for search
  • Loading branch information
Komediruzecki committed Dec 5, 2020
1 parent ca2ebab commit 8739912
Show file tree
Hide file tree
Showing 19 changed files with 569 additions and 60 deletions.
24 changes: 19 additions & 5 deletions src/components/PreferencesModal/styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
tableStyle,
disabledUiTextColor,
PrimaryTextColor,
searchMatchHighlightStyle,
} from '../../lib/styled/styleFunctions'

export const Section = styled.section`
Expand Down Expand Up @@ -43,7 +44,7 @@ export const SectionControl = styled.div`
`

export const SectionSelect = styled.select`
${selectStyle}
${selectStyle};
padding: 0 16px;
width: 200px;
height: 40px;
Expand All @@ -56,7 +57,7 @@ export const SectionSelect = styled.select`
`

export const SectionPrimaryButton = styled.button`
${primaryButtonStyle}
${primaryButtonStyle};
padding: 0 16px;
height: 40px;
border-radius: 2px;
Expand All @@ -66,7 +67,7 @@ export const SectionPrimaryButton = styled.button`
`

export const SectionSecondaryButton = styled.button`
${secondaryButtonStyle}
${secondaryButtonStyle};
padding: 0 16px;
height: 40px;
border-radius: 2px;
Expand All @@ -75,7 +76,7 @@ export const SectionSecondaryButton = styled.button`
`

export const SectionInput = styled.input`
${inputStyle}
${inputStyle};
padding: 0 16px;
width: 200px;
height: 40px;
Expand All @@ -96,11 +97,24 @@ export const TopMargin = styled.div`
`

export const DeleteStorageButton = styled.button`
${secondaryButtonStyle}
${secondaryButtonStyle};
padding: 0 16px;
height: 40px;
border-radius: 2px;
cursor: pointer;
vertical-align: middle;
align-items: center;
`

export const SectionListSelect = styled.div`
${selectStyle};
padding: 0 16px;
width: 200px;
height: 40px;
border-radius: 2px;
font-size: 14px;
`

export const SearchMatchHighlight = styled.span`
${searchMatchHighlightStyle}
`
157 changes: 138 additions & 19 deletions src/components/molecules/SearchModalNoteResultItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,121 @@ import {
borderBottom,
textOverflow,
} from '../../lib/styled/styleFunctions'
import {
getSearchResultKey,
MAX_SEARCH_PREVIEW_LINE_LENGTH,
SearchResult,
} from '../../lib/search/search'
import { isColorBright } from '../../lib/colors'
import { SearchMatchHighlight } from '../PreferencesModal/styled'

interface SearchModalNoteResultItemProps {
note: NoteDoc
selectedItemId: string
searchResults: SearchResult[]
navigateToNote: (noteId: string) => void
updateSelectedItem: (note: NoteDoc, selectedId: string) => void
navigateToEditorFocused: (noteId: string, lineNum: number) => void
}

const SearchModalNoteResultItem = ({
note,
searchResults,
navigateToNote,
selectedItemId,
updateSelectedItem,
navigateToEditorFocused,
}: SearchModalNoteResultItemProps) => {
const navigate = useCallback(() => {
navigateToNote(note._id)
}, [navigateToNote, note._id])

const highlightMatchedTerm = useCallback((line, matchStr) => {
const parts = line.split(new RegExp(`(${matchStr})`, 'gi'))
return (
<span>
{parts.map((part: string, i: number) =>
part.toLowerCase() === matchStr.toLowerCase() ? (
<SearchMatchHighlight key={i}>{matchStr}</SearchMatchHighlight>
) : (
part
)
)}
</span>
)
}, [])
const beautifyPreviewLine = useCallback(
(line, matchStr) => {
const beautifiedLine =
line.substring(0, MAX_SEARCH_PREVIEW_LINE_LENGTH) +
(line.length > MAX_SEARCH_PREVIEW_LINE_LENGTH ? '...' : '')
return highlightMatchedTerm(beautifiedLine, matchStr)
},
[highlightMatchedTerm]
)

return (
<Container onClick={navigate}>
<div className='header'>
<div className='icon'>
<Icon path={mdiTextBoxOutline} />
</div>
<div className='title'>{note.title}</div>
</div>
<div className='meta'>
<div className='folderPathname'>
<Icon className='icon' path={mdiFolder} />
{note.folderPathname}
<Container>
<MetaContainer onClick={navigate}>
<div className='header'>
<div className='icon'>
<Icon path={mdiTextBoxOutline} />
</div>
<div className='title'>{note.title}</div>
</div>
{note.tags.length > 0 && (
<div className='tags'>
<Icon className='icon' path={mdiTagMultiple} />{' '}
{note.tags.map((tag) => tag).join(', ')}
<div className='meta'>
<div className='folderPathname'>
<Icon className='icon' path={mdiFolder} />
{note.folderPathname}
</div>
)}
</div>
{note.tags.length > 0 && (
<div className='tags'>
<Icon className='icon' path={mdiTagMultiple} />{' '}
{note.tags.map((tag) => tag).join(', ')}
</div>
)}
</div>
</MetaContainer>

<SearchResultContainer>
{searchResults.length > 0 &&
searchResults.map((result) => (
<SearchResultItem
className={
selectedItemId == result.id ? 'search-result-selected' : ''
}
key={getSearchResultKey(note._id, result.id)}
onClick={() => updateSelectedItem(note, result.id)}
onDoubleClick={() =>
navigateToEditorFocused(note._id, result.lineNum - 1)
}
>
<SearchResultLeft title={result.lineStr}>
{beautifyPreviewLine(result.lineStr, result.matchStr)}
</SearchResultLeft>
<SearchResultRight>{result.lineNum}</SearchResultRight>
</SearchResultItem>
))}
</SearchResultContainer>
</Container>
)
}

export default SearchModalNoteResultItem

const Container = styled.div`
const Container = styled.div``

const SearchResultContainer = styled.div`
padding: 10px;
cursor: pointer;
${borderBottom};
user-select: none;
`

const MetaContainer = styled.div`
padding: 10px;
cursor: pointer;
${borderBottom}
${borderBottom};
user-select: none;
&:hover {
Expand All @@ -60,6 +131,7 @@ const Container = styled.div`
&:hover:active {
background-color: ${({ theme }) => theme.navItemHoverActiveBackgroundColor};
}
& > .header {
font-size: 18px;
display: flex;
Expand Down Expand Up @@ -109,3 +181,50 @@ const Container = styled.div`
border-bottom: none;
}
`

const SearchResultItem = styled.div`
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
justify-content: space-between;
overflow: hidden;
margin-top: 0.3em;
&.search-result-selected {
border-radius: 4px;
padding: 2px;
background-color: ${({ theme }) =>
theme.searchItemSelectionBackgroundColor};
filter: brightness(
${({ theme }) => (isColorBright(theme.activeBackgroundColor) ? 85 : 115)}%
);
}
}
&:hover {
border-radius: 4px;
background-color: ${({ theme }) =>
theme.secondaryButtonHoverBackgroundColor};
filter: brightness(
${({ theme }) =>
isColorBright(theme.secondaryButtonHoverBackgroundColor) ? 85 : 115}%
);
}
`

const SearchResultLeft = styled.div`
align-self: flex-start;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
&:before {
content: attr(content);
}
`

const SearchResultRight = styled.div`
align-self: flex-end;
`
15 changes: 11 additions & 4 deletions src/components/organisms/NoteDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type NoteDetailProps = {
props: Partial<NoteDocEditibleProps>
) => Promise<void | NoteDoc>
viewMode: ViewModeType
initialCursorPos: EditorPosition
addAttachments(storageId: string, files: File[]): Promise<Attachment[]>
}

Expand Down Expand Up @@ -73,6 +74,12 @@ class NoteDetail extends React.Component<NoteDetailProps, NoteDetailState> {
codeMirror?: CodeMirror.EditorFromTextArea
codeMirrorRef = (codeMirror: CodeMirror.EditorFromTextArea) => {
this.codeMirror = codeMirror

// Update cursor if needed
if (this.props.initialCursorPos) {
this.codeMirror.focus()
this.codeMirror.setCursor(this.props.initialCursorPos)
}
}

static getDerivedStateFromProps(
Expand All @@ -86,8 +93,8 @@ class NoteDetail extends React.Component<NoteDetailProps, NoteDetailState> {
prevNoteId: note._id,
content: note.content,
currentCursor: {
line: 0,
ch: 0,
line: props.initialCursorPos ? props.initialCursorPos.line : 0,
ch: props.initialCursorPos ? props.initialCursorPos.ch : 0,
},
currentSelections: [
{
Expand Down Expand Up @@ -285,13 +292,13 @@ class NoteDetail extends React.Component<NoteDetailProps, NoteDetailState> {
}

render() {
const { note, storage, viewMode } = this.props
const { note, storage, viewMode, initialCursorPos } = this.props
const { currentCursor, currentSelections } = this.state

const codeEditor = (
<CustomizedCodeEditor
className='editor'
key={note._id}
key={note._id + initialCursorPos.line}
codeMirrorRef={this.codeMirrorRef}
value={this.state.content}
onChange={this.updateContent}
Expand Down
12 changes: 5 additions & 7 deletions src/components/organisms/NoteStorageNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ const NoteStorageNavigator = ({ storage }: NoteStorageNavigatorProps) => {
const { toggleShowSearchModal } = useSearchModal()

useGlobalKeyDownHandler((event) => {
if (isWithGeneralCtrlKey(event) && event.key === 'p') {
if (isWithGeneralCtrlKey(event) && event.key.toLowerCase() === 'p') {
toggleShowSearchModal()
}
})
Expand Down Expand Up @@ -300,7 +300,6 @@ const TopButton = styled.button`
const SearchButton = styled.button`
margin: 0 8px;
height: 34px;
padding: 0;
color: ${({ theme }) => theme.secondaryButtonLabelColor};
background-color: ${({ theme }) => theme.secondaryButtonBackgroundColor};
border: none;
Expand All @@ -321,7 +320,7 @@ const SearchButton = styled.button`
& > .icon {
width: 24px;
height: 24px;
${flexCenter}
${flexCenter};
flex-shrink: 0;
}
& > .label {
Expand All @@ -333,7 +332,7 @@ const SearchButton = styled.button`
display: none;
font-size: 12px;
margin-left: 5px;
${textOverflow}
${textOverflow};
align-items: center;
flex-shrink: 0;
}
Expand All @@ -342,7 +341,6 @@ const SearchButton = styled.button`
const NewNoteButton = styled.button`
margin: 8px 8px;
height: 34px;
padding: 0;
color: ${({ theme }) => theme.primaryButtonLabelColor};
background-color: ${({ theme }) => theme.primaryButtonBackgroundColor};
border: none;
Expand All @@ -363,7 +361,7 @@ const NewNoteButton = styled.button`
& > .icon {
width: 24px;
height: 24px;
${flexCenter}
${flexCenter};
flex-shrink: 0;
}
& > .label {
Expand All @@ -374,7 +372,7 @@ const NewNoteButton = styled.button`
display: none;
font-size: 12px;
margin-left: 5px;
${textOverflow}
${textOverflow};
align-items: center;
& > .icon {
flex-shrink: 0;
Expand Down
Loading

0 comments on commit 8739912

Please sign in to comment.