Как смонтировать стили внутри теневого корня с помощью cssinjs / jss

Я пытаюсь использовать компоненты https://material-ui.com/ внутри теневого домена, и мне нужен способ внедрить эти стили в теневой дом. по умолчанию material-ui, который использует jss под капотом, вводит стили в заголовок страницы.

Это вообще возможно? Кто-нибудь может привести пример?


person bboydflo    schedule 29.01.2019    source источник
comment
Я не думаю, что внедрение React и всего этого на страницу - хорошая идея, мне это определенно не кажется. Обычный подход - вставить только один iframe, который указывает на html-файл в вашем расширении (перечисленном в web_accessible_resources), который является стандартной страницей, которая может делать все, что захочет, и загружать любые материальные компоненты пользовательского интерфейса внутри. Я видел по крайней мере один полный пример / учебник по этому поводу в сети.   -  person wOxxOm    schedule 30.01.2019
comment
Вы хотите, чтобы стили компонента были в его теневой DOM, или вы могли бы поместить все стили в одну теневую DOM?   -  person epsilon    schedule 30.01.2019
comment
@wOxxOm Я полностью согласен, но подход iframe, по моему опыту, немного сложен из-за нескольких вещей, таких как фокус (когда вы щелкаете внутри iframe, как будто находитесь на другой странице) и изменение размера iframe (у меня были проблемы с этим когда, например, я помещаю раскрывающийся список внутри iframe, мне нужно указать внешней странице изменить размер iframe, чтобы содержимое было видимым). веб-компонент определенно будет более родным. epsilon то, что я хотел бы сделать, - это создать единый веб-компонент, используя несколько виджетов material-ui, и их стиль должен быть внедрен в теневой корень.   -  person bboydflo    schedule 31.01.2019
comment
Даже если вы сможете заархивировать свою цель, у вас могут возникнуть проблемы с отображением всплывающих материалов изнутри вашего теневого корня. Поскольку кажется, что библиотека материалов вставляет всплывающие элементы dom непосредственно в тело html-страницы, которое в вашем случае будет без стиля.   -  person Andrey Luzinov    schedule 02.04.2020


Ответы (2)


Так выглядит мой веб-компонент. Это веб-компонент, который отображает приложение React, содержащее стили material-ui.

import * as React from 'react';
import { render } from 'react-dom';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { create } from 'jss';

import { App } from '@myApp/core';

class MyWebComponent extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'open' });
    const mountPoint = document.createElement('span');
    const reactRoot = shadowRoot.appendChild(mountPoint);
    const jss = create({
      ...jssPreset(),
      insertionPoint: reactRoot
    });

    render(
      <StylesProvider jss={jss}>
        <App />
      </StylesProvider>,
      mountPoint
    );
  }
}
customElements.define('my-web-commponent', MyWebComponent);

Установка insertionPoint в jss на фактический корень реакции внутри теневого корня укажет jss на вставку этих стилей в этот теневой корень.

person Shawn Mclean    schedule 09.06.2019
comment
Это работает со многими компонентами, однако, если вы используете Dialog, его теги помещаются в тело, и этот трюк не работает. - person AGPX; 05.08.2020

Используя https://github.com/Wildhoney/ReactShadow для создания теневого домена (вы также можете сделать это с помощью hand, как показано в предыдущем ответе), я создал небольшой компонент WrapperComponent, который инкапсулирует логику.

import root from 'react-shadow';
import {jssPreset, StylesProvider} from "@material-ui/styles";
import {create} from 'jss';
import React, {useState} from "react"

const WrappedJssComponent = ({children}) => {
  const [jss, setJss] = useState(null);
  
  function setRefAndCreateJss(headRef) {
    if (headRef && !jss) {
      const createdJssWithRef = create({...jssPreset(), insertionPoint: headRef})
      setJss(createdJssWithRef)
    }
  }
  
  return (
    <root.div>
      <head>
        <style ref={setRefAndCreateJss}></style>
      </head>
      {jss &&
      <StylesProvider jss={jss}>
        {children}
      </StylesProvider>
      }
    
    </root.div>
  )
}

export default WrappedJssComponent

Затем вам просто нужно обернуть ваше приложение или часть вашего приложения, которую вы хотите затенять, внутри <WrappedJssComponenent><YourComponent></YourComponent></WrappedJssComponenent>.

Будьте осторожны, некоторые компоненты пользовательского интерфейса материала не будут работать как обычно (у меня были проблемы с

  • ClickAwayListener, возможно, потому, что он использует родительский дом, не исследовал больше, если честно.
  • Popper и все, что будет пытаться использовать document.body в качестве контейнера, не будет иметь доступа к jss, определенному в теневом узле. Вы должны указать элемент внутри теневого дома в качестве контейнера.
person Robin Leclerc    schedule 26.07.2019
comment
Это круто! Вы меня полностью спасли! Спасибо! - person zeckdude; 17.03.2020
comment
Вы должны указать элемент внутри теневого дома в качестве контейнера. - Как это может быть сделано? - person Valu3; 03.11.2020
comment
@ Valu3 Вы можете передать свойство контейнера Popper (и нескольким другим элементам) со ссылкой на элемент контейнера - этот элемент также может находиться внутри теневого корня. - person Grzegorz Judas; 30.04.2021