caroso1222 / notyf

👻 A minimalistic, responsive, vanilla JavaScript library to show toast notifications.
https://carlosroso.com/notyf/
MIT License
2.66k stars 197 forks source link

[Feature Request] Config option for a custom node to attach toast notifications #117

Open calebbergman opened 2 years ago

calebbergman commented 2 years ago

I have a vite project that utilizes postcss-scopify to scope styles to the application. This has the side effect of scoping 3rd party node_module package styles as well, such as notyf, which is generally ok. However, due to the fact that notyf defaults attaching the toast 'view' directly to the body, this leaves the toasts document fragment non-targetable by the postcss-scopify modified stylesheet selectors.

https://github.com/caroso1222/notyf/blob/d19f0abf35d3b785e8fec6c81a08e18087feac56/src/notyf.view.ts#L40

One solution would be to have postcss-scopify ignore certain files, which I would love to be able to do, but alas I'm not that smart to figure out how to accomplish such a feat using the postcss.config.js file. One may be able to by some other means (such as in vite.config.ts) but I have not discovered this quite yet. See vite issue 4653

Another option would be to be allowed to manage where the toasts get attached to in the DOM, as this feature request suggests, either by passing a querySelector string or DOM Node. This would ensure that toasts get attached as a child to where they've been scoped to by postcss-scopify.

calebbergman commented 2 years ago

As a work around you can take advantage of dynamic module imports to re-create the original stylesheet at runtime. Not ideal, but works - while debugging, that is. See next comment for better solution.

import { Notyf } from 'notyf'
// import 'notyf/notyf.min.css'

import('notyf/notyf.min.css').then((module) => {
  // Remove scoped stylesheet from DOM
  const children = Array.from(document.head.childNodes)
  const scopedCss = children.reverse().find(c => c.textContent?.match(/notyf/))
  if (scopedCss)
    document.head.removeChild(scopedCss)

  // Remove scope from css
  const pattern = new RegExp('#my-custom-app-scope', 'gi')
  const css = module.default.replace(pattern, '')
  // Append updated stylesheet to DOM
  const style = document.createElement('style')
  style.setAttribute('type', 'text/css')
  style.setAttribute('id', 'notyf-stylesheet')
  style.innerText = css
  document.head.appendChild(style)
})

export default new Notyf({
  dismissible: true,
  duration: 5000,
})
calebbergman commented 2 years ago

The above worked fine while debugging. Post build, when everything was bundled up, not-so-much. After some more Rollup/Vite learning, I came up with the following plugin to selectively undo postcss-scopify's affects.

// rollup-plugin-postcss-unscopify.js
export default function postCssUnscopify(options = {}) {
  const { patterns, scope } = options
  return {
    name: 'postcss-unscopify',

    transform(src, id) {
      const match = patterns.reduce((result, cur) => result || !!cur.test(id), false)
      if (match) {
        const regex = new RegExp(scope, 'gi')
        return {
          code: src.replace(regex, ''),
          map: null
        }
      }
    }
  }
}
// vite.config.js
import postCssUnscopify from './rollup-plugin-postcss-unscopify'
export default defineConfig({
  plugins: [
    postCssUnscopify({
      scope: '#my-custom-scope',
      patterns: [/.*ignore-me.*css/]
    })
  ]
})