Skip to content

Commit

Permalink
feat: Add notification for addTarget and improve accuracy of selection
Browse files Browse the repository at this point in the history
ronparkdev committed Jun 19, 2024
1 parent d68308c commit 1fdc541
Showing 4 changed files with 65 additions and 29 deletions.
50 changes: 31 additions & 19 deletions src/content.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { TargetConfig } from 'services/config'

void (async () => {
let lastElement: HTMLElement | null = null
let lastElement: Element | null = null
let lastTargets: TargetConfig[] = []
let mode: 'standby' | 'addTarget' = 'standby'

@@ -63,42 +63,50 @@ void (async () => {

// Low priority functions (for setting)

const [{ default: getXPath }, { DomHighlightService }] = await Promise.all([
import('get-xpath'),
import('services/domHighlight'),
])
const [
{ default: getXPath },
{ DomHighlightService },
{ NOTIFICATION_CLASSNAME, showAddTargetToast, cancelAddTarget },
] = await Promise.all([import('get-xpath'), import('services/domHighlight'), import('notification')])

document.addEventListener('mousemove', event => {
if (mode === 'addTarget') {
const element = DomService.getElementFromPoint(event.clientX, event.clientY)

const clickableElement = element !== null ? DomService.findVisibleClickableAndSufficientSizeParent(element) : null
let clickableElement = element !== null ? DomService.findVisibleClickableAndSufficientSizeParent(element) : null

if (clickableElement !== null) {
DomHighlightService.highlight(clickableElement)
if (clickableElement?.closest(`.${NOTIFICATION_CLASSNAME}`)) {
clickableElement = null
}

DomHighlightService.highlight(clickableElement)

lastElement = clickableElement
}
})

document.addEventListener('click', event => {
if (mode === 'addTarget') {
event.preventDefault()
document.addEventListener(
'click',
event => {
if (mode === 'addTarget') {
event.preventDefault()
event.stopPropagation()

if (lastElement !== null) {
showDialog({ element: lastElement })
}
if (lastElement !== null) {
showDialog({ element: lastElement })
}

DomHighlightService.highlight(null)
cancelAddTarget()

mode = 'standby'
}
})
mode = 'standby'
}
},
{ capture: true },
)

DomHighlightService.injectStyle()

const showDialog = async ({ element }: { element: HTMLElement }) => {
const showDialog = async ({ element }: { element: Element }) => {
const selector = getXPath(element)
const defaultUrl = UrlUtils.getCurrentUrl()
void render({ visible: true, defaultSelector: selector, defaultUrl })
@@ -156,6 +164,10 @@ void (async () => {

if (request.action === 'addTarget') {
mode = 'addTarget'
showAddTargetToast(() => {
mode = 'standby'
cancelAddTarget()
})
}
})
})()
25 changes: 25 additions & 0 deletions src/notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CloseCircleOutlined } from '@ant-design/icons'
import { Button, message } from 'antd'
import React from 'react'

export const NOTIFICATION_CLASSNAME = '__shortclick_extension_notification__'

const MESSAGE_KEY = 'addTarget'

export const showAddTargetToast = (onClose: () => void) => {
message.open({
className: NOTIFICATION_CLASSNAME,
content: (
<div style={{ display: 'flex', alignItems: 'center' }}>
<span style={{ flex: 1 }}>Click the component you want to add</span>
<Button type="link" onClick={onClose} icon={<CloseCircleOutlined />} />
</div>
),
key: MESSAGE_KEY,
duration: 0, // duration 0 to make the message stay until it's closed
})
}

export const cancelAddTarget = () => {
message.destroy(MESSAGE_KEY)
}
15 changes: 7 additions & 8 deletions src/services/dom.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
const ROOT_ID = '_shortcut_click_extension_root_'

export const getElementFromPoint = (x: number, y: number): HTMLElement | null => {
const element = document.elementFromPoint(x, y)
return element instanceof HTMLElement ? element : null
export const getElementFromPoint = (x: number, y: number): Element | null => {
return document.elementFromPoint(x, y)
}

const isElementVisible = (element: HTMLElement | null): boolean => {
const isElementVisible = (element: Element | null): boolean => {
if (!element) return false

const style = getComputedStyle(element)
return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'
}

const isElementClickable = (element: HTMLElement | null): boolean => {
const isElementClickable = (element: Element | null): boolean => {
if (!element) return false

const style = getComputedStyle(element)
return style.pointerEvents !== 'none'
}

const isElementSizeSufficient = (element: HTMLElement | null): boolean => {
const isElementSizeSufficient = (element: Element | null): boolean => {
if (!element) return false

const rect = element.getBoundingClientRect()
return rect.width * rect.height > 8
}

const findVisibleClickableAndSufficientSizeParent = (element: HTMLElement | null): HTMLElement | null => {
const findVisibleClickableAndSufficientSizeParent = (element: Element | null): Element | null => {
let currentElement = element

while (currentElement) {
@@ -37,7 +36,7 @@ const findVisibleClickableAndSufficientSizeParent = (element: HTMLElement | null
) {
return currentElement
}
currentElement = currentElement.parentElement as HTMLElement | null
currentElement = currentElement.parentElement as Element | null
}

return null
4 changes: 2 additions & 2 deletions src/services/domHighlight.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { DomService } from './dom'

const highlight = (target: string | HTMLElement | null) => {
const highlight = (target: string | Element | null) => {
// Remove any existing highlights
Array.from(document.querySelectorAll('.element-highlight')).forEach(el => el.classList.remove('element-highlight'))

if (target !== null) {
if (target instanceof HTMLElement) {
if (target instanceof Element) {
target.classList.add('element-highlight')
} else {
DomService.findElementsByXPath(target).forEach(

0 comments on commit 1fdc541

Please sign in to comment.