diff --git a/package.json b/package.json index c6d8373..6d1dc40 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "@twind/preset-autoprefix": "^1.0.7", "@twind/preset-tailwind": "^1.1.4", "beautiful-react-hooks": "^5.0.0", - "css-selector-generator": "^3.6.4", "fast-deep-equal": "^3.1.3", "i18next": "^23.5.1", "i18next-browser-languagedetector": "^7.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12d9e7e..e759e95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,9 +25,6 @@ dependencies: beautiful-react-hooks: specifier: ^5.0.0 version: 5.0.0(react-dom@18.2.0)(react-router-dom@6.16.0)(react@18.2.0)(rxjs@7.5.7) - css-selector-generator: - specifier: ^3.6.4 - version: 3.6.4 fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -3106,10 +3103,6 @@ packages: nth-check: 2.1.1 dev: true - /css-selector-generator@3.6.4: - resolution: {integrity: sha512-/VDUW5KAoxGXJPNcn+5Y0DLJMQumLk9zNMWl2NChuTwGLEFeqfnddCpRhY6uZ77ar0uyM7W7ilvIWsk7DpjKxQ==} - dev: false - /css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} diff --git a/src/content/Content.tsx b/src/content/Content.tsx index d6addb7..692eeb1 100644 --- a/src/content/Content.tsx +++ b/src/content/Content.tsx @@ -1,33 +1,50 @@ /* eslint-disable @typescript-eslint/strict-boolean-expressions */ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useReadability } from '../shared/hooks/useReadability'; import { useMessaging } from './hooks/useMessaging'; -import { useSelectorGenerator } from './hooks/useSelectorGenerator'; + +const CLIPPER_MARK_HOVERED = 'tiddlywikiClipperHovered'; export function Content() { const { t } = useTranslation(); const [isSelecting, setIsSelecting] = useState(false); const { parseReadability } = useReadability(); const [selectedElement, setSelectedElement] = useState(null); - useMessaging({ setIsSelecting, parseReadability, selectedElement }); - const { updateSelector } = useSelectorGenerator(); + const previousHoveredElementReference = useRef(null); + const previousHoveredElementOutlineReference = useRef(''); + // TODO: get selector and enable KBD to move selection. + // const { updateSelector } = useSelectorGenerator(); const handleMouseMove = useCallback((event: MouseEvent) => { const element = event.target as HTMLElement | null; if (element === null) return; + if (previousHoveredElementReference.current === element) return; + // restore previous element + if (previousHoveredElementReference.current?.dataset?.[CLIPPER_MARK_HOVERED]) { + previousHoveredElementReference.current.style.outline = previousHoveredElementOutlineReference.current; + } + // backup current element + previousHoveredElementOutlineReference.current = element.style.outline ?? ''; + previousHoveredElementReference.current = element; + // highlight current element element.style.outline = '2px solid blue'; // Highlight element - updateSelector(element); // Update the CSS selector - }, [updateSelector]); + element.dataset[CLIPPER_MARK_HOVERED] = 'true'; + // updateSelector(element); // Update the CSS selector + }, []); const handleElementSelection = useCallback((event: MouseEvent) => { const element = event.target as HTMLElement | null; + if (element === null) return; setSelectedElement(element); - document.removeEventListener('mousemove', handleMouseMove); // Stop highlighting on mouse move + element.style.outline = '2px solid green'; // Highlight selected element + // Stop highlighting on mouse move, but still allow click on other element to fix the selection. + document.removeEventListener('mousemove', handleMouseMove); }, [handleMouseMove]); const cleanUp = useCallback(() => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('click', handleElementSelection); }, [handleElementSelection, handleMouseMove]); + useMessaging({ setIsSelecting, parseReadability, selectedElement, cleanUp }); const handleCancelSelecting = useCallback(() => { setIsSelecting(false); diff --git a/src/content/hooks/useMessaging.ts b/src/content/hooks/useMessaging.ts index 3565bee..5e7872d 100644 --- a/src/content/hooks/useMessaging.ts +++ b/src/content/hooks/useMessaging.ts @@ -3,6 +3,7 @@ import { IGetReadabilityMessageResponse, IStartClippingResponseMessage, ITabActi export function useMessaging( parameter: { + cleanUp: () => void; parseReadability: () => IGetReadabilityMessageResponse['article']; selectedElement: HTMLElement | null; setIsSelecting: Dispatch>; @@ -15,7 +16,13 @@ export function useMessaging( parameter.setIsSelecting(true); break; } + /** + * we will try this when user click on extension icon. (whenever first-time or second-time, just try it) + * + * If `parameter.selectedElement` exists, means this is second-time (user open popup before and choose the "select manually", which is the first time.). + */ case ITabActions.startClipping: { + parameter.cleanUp(); parameter.setIsSelecting(false); if (parameter.selectedElement === null) return; const text = parameter.selectedElement.textContent ?? ''; diff --git a/src/content/hooks/useSelectorGenerator.ts b/src/content/hooks/useSelectorGenerator.ts deleted file mode 100644 index f74218d..0000000 --- a/src/content/hooks/useSelectorGenerator.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getCssSelector } from 'css-selector-generator'; -import { useState } from 'react'; - -export function useSelectorGenerator() { - const [selector, setSelector] = useState(null); - - const updateSelector = (element: HTMLElement) => { - const newSelector = getCssSelector(element); - setSelector(newSelector); - }; - - return { selector, updateSelector }; -} diff --git a/src/shared/hooks/useReadability.ts b/src/shared/hooks/useReadability.ts index d3e0320..ae36713 100644 --- a/src/shared/hooks/useReadability.ts +++ b/src/shared/hooks/useReadability.ts @@ -1,20 +1,20 @@ import { Readability } from '@mozilla/readability'; -import { useCallback, useState } from 'react'; -import { IGetReadabilityMessageResponse } from '../message'; +import { useCallback } from 'react'; +// import { IGetReadabilityMessageResponse } from '../message'; export function useReadability() { - const [article, setArticle] = useState(null); + // const [article, setArticle] = useState(null); const parseReadability = useCallback(() => { const documentClone = document.cloneNode(true) as Document; const reader = new Readability(documentClone); const article = reader.parse(); - if (article !== null) { - setArticle(article); - } + // if (article !== null) { + // setArticle(article); + // } return article; }, []); return { - article, + // article, parseReadability, }; }