dictionary-modal-content.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { useCallback, useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import useAsync from '../../../shared/hooks/use-async'
  4. import { postJSON } from '../../../infrastructure/fetch-json'
  5. import { debugConsole } from '@/utils/debugging'
  6. import {
  7. OLModalBody,
  8. OLModalFooter,
  9. OLModalHeader,
  10. OLModalTitle,
  11. } from '@/features/ui/components/ol/ol-modal'
  12. import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
  13. import OLNotification from '@/features/ui/components/ol/ol-notification'
  14. import OLButton from '@/features/ui/components/ol/ol-button'
  15. import OLIconButton from '@/features/ui/components/ol/ol-icon-button'
  16. import { bsVersion } from '@/features/utils/bootstrap-5'
  17. import { learnedWords as initialLearnedWords } from '@/features/source-editor/extensions/spelling/learned-words'
  18. type DictionaryModalContentProps = {
  19. handleHide: () => void
  20. }
  21. const wordsSortFunction = (a: string, b: string) => a.localeCompare(b)
  22. export default function DictionaryModalContent({
  23. handleHide,
  24. }: DictionaryModalContentProps) {
  25. const { t } = useTranslation()
  26. const [learnedWords, setLearnedWords] = useState<Set<string>>(
  27. initialLearnedWords.global
  28. )
  29. const { isError, runAsync } = useAsync()
  30. const handleRemove = useCallback(
  31. word => {
  32. runAsync(postJSON('/spelling/unlearn', { body: { word } }))
  33. .then(() => {
  34. setLearnedWords(prevLearnedWords => {
  35. const learnedWords = new Set(prevLearnedWords)
  36. learnedWords.delete(word)
  37. return learnedWords
  38. })
  39. window.dispatchEvent(
  40. new CustomEvent('editor:remove-learned-word', { detail: word })
  41. )
  42. })
  43. .catch(debugConsole.error)
  44. },
  45. [runAsync]
  46. )
  47. return (
  48. <>
  49. <OLModalHeader closeButton>
  50. <OLModalTitle>{t('edit_dictionary')}</OLModalTitle>
  51. </OLModalHeader>
  52. <OLModalBody>
  53. {isError ? (
  54. <OLNotification
  55. type="error"
  56. content={t('generic_something_went_wrong')}
  57. />
  58. ) : null}
  59. {learnedWords.size > 0 ? (
  60. <ul className="list-unstyled dictionary-entries-list">
  61. {[...learnedWords].sort(wordsSortFunction).map(learnedWord => (
  62. <li key={learnedWord} className="dictionary-entry">
  63. <span className="dictionary-entry-name">{learnedWord}</span>
  64. <OLTooltip
  65. id={`tooltip-remove-learned-word-${learnedWord}`}
  66. description={t('edit_dictionary_remove')}
  67. overlayProps={{ delay: 0 }}
  68. >
  69. <OLIconButton
  70. variant="danger"
  71. size="sm"
  72. onClick={() => handleRemove(learnedWord)}
  73. bs3Props={{ bsSize: 'xsmall' }}
  74. icon={
  75. bsVersion({
  76. bs5: 'delete',
  77. bs3: 'trash-o',
  78. }) as string
  79. }
  80. accessibilityLabel={t('edit_dictionary_remove')}
  81. />
  82. </OLTooltip>
  83. </li>
  84. ))}
  85. </ul>
  86. ) : (
  87. <p className="dictionary-empty-body text-center">
  88. <i>{t('edit_dictionary_empty')}</i>
  89. </p>
  90. )}
  91. </OLModalBody>
  92. <OLModalFooter>
  93. <OLButton variant="secondary" onClick={handleHide}>
  94. {t('close')}
  95. </OLButton>
  96. </OLModalFooter>
  97. </>
  98. )
  99. }