PlasmoHQ / plasmo

🧩 The Browser Extension Framework
https://www.plasmo.com
MIT License
10.31k stars 358 forks source link

Implement a way to insert content script in the middle of a page [RFC] #1010

Open mfissehaye opened 3 months ago

mfissehaye commented 3 months ago

How do you envision this feature/change to look/work like?

The framework should allow react developer to be able to insert React components in the middle of the page instead of just being able to create an overlay

What is the purpose of this change/feature? Why?

Sometimes I want to be able to add the content after a button or below a div to improve user experience.

(OPTIONAL) Example implementations

For example adding more context to a specific portion of the page in a facebook feed, add a component (UI styled with tailwind let's say) after every other post.

(OPTIONAL) Contribution

Verify canary release

Code of Conduct

muzammil0011 commented 2 months ago

Currently this feature is not supported in Plasmo, But you can achieve this functionality using Mutation Observer to observe the element and then add whatever you want before or after that element.

Here is an example of the code that uses Arrive.js (provides events to watch for DOM elements creation and removal) that helped me:

import cssText from "data-text:~/contents/style.css"
import type { PlasmoCSConfig, PlasmoGetShadowHostId } from "plasmo"

import "arrive"

import { useEffect } from "react"
import { createRoot } from "react-dom/client"

export const config: PlasmoCSConfig = {
  matches: ["*://*.example.com/*"]
}

const HOST_ID = "engage-csui"

export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID

export const getStyle = () => {
  const style = document.createElement("style")
  style.textContent = cssText
  return style
}

export default () => {
  useEffect(() => {
    const elementSelector = "#selected-element-selector"
    // Wait for the element to be added to the DOM
    document.arrive(elementSelector, function (element) {
      const buttonContainer = document.createElement("div")
      createRoot(buttonContainer).render(<>{/* <AnyReactComponent/> */}</>)
      element.insertBefore(buttonContainer, element.firstChild)
    })

    return () => {
      document.unbindArrive(elementSelector)
    }
  }, [])
  return <>{/* Element you want to add as overlay */}</>
}

You also have to declare arrive in globals.d.ts

declare global {
    interface Document {
      arrive(
        selector: string,
        options: any,
        callback: (element: Element) => void
      ): void
      arrive(selector: string, callback: (element: Element) => void): void
      unbindArrive(selector: string): void
    }
  }

  export {}