Skip to content

Commit

Permalink
feat: allow import/export dictionary
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreBellas committed Jun 9, 2024
1 parent 9525da4 commit c3bc955
Show file tree
Hide file tree
Showing 12 changed files with 438 additions and 58 deletions.
7 changes: 6 additions & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"semi": false,
"singleQuote": true
"trailingComma": "all",
"singleQuote": true,
"plugins": [
"prettier-plugin-tailwindcss"
],
"tailwindConfig": "./tailwind.config.ts"
}
135 changes: 135 additions & 0 deletions package-lock.json

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
"lint": "next lint"
},
"dependencies": {
"lucide-react": "^0.390.0",
"next": "14.2.3",
"react": "^18",
"react-device-detect": "^2.2.3",
"react-dom": "^18"
},
"devDependencies": {
Expand All @@ -20,6 +22,7 @@
"eslint": "^8",
"eslint-config-next": "14.2.3",
"postcss": "^8",
"prettier-plugin-tailwindcss": "^0.6.2",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
Expand Down
34 changes: 34 additions & 0 deletions src/components/ButtonExportDictionary/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useWordService } from '@/hooks/useWordService'
import { ArrowDownToLine } from 'lucide-react'
import { useCallback } from 'react'

export default function ButtonExportDictionary() {
// #region Services
const wordService = useWordService()
// #endregion

// #region Callbacks
const onExport = useCallback(() => {
wordService.export().then((exportResponse) => {
const element = document.createElement('a')
const file = new Blob([exportResponse.data], { type: 'text/plain' })
element.href = URL.createObjectURL(file)
element.download = `${new Date().toISOString()}+export.txt`
document.body.appendChild(element)
element.click()
})
}, [wordService])

// #endregion

return (
<button
className="my-2 rounded-lg bg-white p-2 hover:bg-white/50"
onClick={() => {
onExport()
}}
>
<ArrowDownToLine className="text-gray-800" />
</button>
)
}
67 changes: 67 additions & 0 deletions src/components/ButtonImportDictionary/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useWordDispatch } from '@/hooks/useWord'
import { useWordService } from '@/hooks/useWordService'
import { ArrowUpFromLine } from 'lucide-react'
import { ChangeEvent, useCallback } from 'react'

export default function ButtonImportDictionary() {
// #region Contexts
const wordDispatch = useWordDispatch()
// #endregion

// #region Services
const wordService = useWordService()
// #endregion

// #region Callbacks
const onImport = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (!file) {
alert('No dictionary has been added.')
return
}

const reader = new FileReader()
reader.onload = function (event) {
const eventTarget = event.target
if (!eventTarget?.result || typeof eventTarget.result !== 'string') {
alert('It has not been possible to read the dictionary.')
return
}

wordService
.import({ data: eventTarget.result })
.then((importResponse) => {
if (!importResponse.isSuccessful) {
alert(importResponse.message)
return
}

wordDispatch({ type: 'clear' })
alert('New dictionary imported successfully.')
})
}

reader.readAsText(file)
},
[wordService, wordDispatch],
)
// #endregion

return (
<div className="my-2 rounded-lg p-2 hover:bg-gray-300">
<label htmlFor="input--import-data">
<ArrowUpFromLine className="text-gray-500" />
</label>
<input
id="input--import-data"
type="file"
className="hidden"
accept="text/plain"
onChange={(ev) => {
onImport(ev)
}}
></input>
</div>
)
}
32 changes: 19 additions & 13 deletions src/components/InputSearchWord/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
import { useWordDispatch } from '@/hooks/useWord'
import { useWord, useWordDispatch } from '@/hooks/useWord'
import { useCallback } from 'react'
import ButtonExportDictionary from '../ButtonExportDictionary'
import ButtonImportDictionary from '../ButtonImportDictionary'

export default function InputSearchWord() {
// #region Contexts
const { word } = useWord()
const wordDispatch = useWordDispatch()
// #endregion

// #region State

// #endregion

// #region Callbacks
const onChangeWord = useCallback(
(search: string) => {
wordDispatch({ type: 'set-word', word: search })
},
[wordDispatch]
[wordDispatch],
)
// #endregion

return (
<div className="h-[50vh] justify-center items-center bg-gradient-to-b from-gray-200 to-gray-400 flex">
<div className="rounded-full flex h-16 md:h-12 py-4 px-5 bg-white shadow-md">
<input
className="flex-1 w-[40vw] bg-transparent focus:outline-none"
placeholder="Type to add or search..."
onChange={(ev) => onChangeWord(ev.target.value)}
/>
<div className="flex h-[50vh] flex-col items-center justify-center bg-gradient-to-b from-gray-200 to-gray-400">
<div className="container flex justify-end">
<ButtonImportDictionary />
<ButtonExportDictionary />
</div>
<div className="flex flex-1 items-center justify-center">
<div className="flex h-16 rounded-full bg-white px-5 py-4 shadow-md md:h-12">
<input
className="w-[40vw] flex-1 bg-transparent focus:outline-none"
placeholder="Type to add or search..."
value={word ?? ''}
onChange={(ev) => onChangeWord(ev.target.value)}
/>
</div>
</div>
</div>
)
Expand Down
Loading

0 comments on commit c3bc955

Please sign in to comment.