Skip to content

Commit

Permalink
feat #5246: add styleContainer option for PrimeReactContext (#5566)
Browse files Browse the repository at this point in the history
* feat #5246: add styleContainer option for useStyle

* Pass `context.styleContainer` to `DomHandler.createInlineStyle`

* Add docs for StyleContainer
  • Loading branch information
mondaychen authored Jan 5, 2024
1 parent ed23cfc commit 3afafe7
Show file tree
Hide file tree
Showing 20 changed files with 97 additions and 19 deletions.
17 changes: 17 additions & 0 deletions components/doc/common/apidoc/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,13 @@
"type": "AppendToType",
"description": "This option allows components with overlays like dropdowns or popups to be mounted into either the component or any DOM element, such as document body and self."
},
{
"name": "styleContainer",
"optional": true,
"readonly": false,
"type": "StyleContainerType",
"description": "This option allows `useStyle` to insert dynamic CSS styles into a specific container. This is useful when styles need to be scoped such as in a Shadow DOM."
},
{
"name": "autoZIndex",
"optional": true,
Expand Down Expand Up @@ -787,6 +794,13 @@
"type": "Dispatch<SetStateAction<AppendToType>>",
"description": "Sets the \"appendTo\" state of the context."
},
{
"name": "setStyleContainer",
"optional": true,
"readonly": false,
"type": "Dispatch<SetStateAction<StyleContainerType>>",
"description": "Sets the \"styleContainer\" state of the context."
},
{
"name": "setAutoZIndex",
"optional": true,
Expand Down Expand Up @@ -3317,6 +3331,9 @@
},
"AppendToType": {
"values": "\"self\" | HTMLElement | undefined | null"
},
"StyleContainerType": {
"values": "ShadowRoot | HTMLElement | undefined | null"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion components/doc/configuration/appendtodoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function MyApp({ Component }) {
<DocSectionText {...props}>
<p>
For components with an overlay like a dropdown, popups can be mounted either into the component or DOM element instance using this option. Valid values are any DOM Element like document body and <i>self</i>. By default all popups
are append to document body via Portals.
are appended to document body via Portals.
</p>
</DocSectionText>
<DocSectionCode code={code} hideToggleCode import hideCodeSandbox hideStackBlitz />
Expand Down
39 changes: 39 additions & 0 deletions components/doc/configuration/stylecontainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { DocSectionCode } from '@/components/doc/common/docsectioncode';
import { DocSectionText } from '@/components/doc/common/docsectiontext';

export function StyleContainer(props) {
const code = {
basic: `
//_app.js
import { PrimeReactProvider } from 'primereact/api';
root.attachShadow({ mode: 'open' }); // Open the shadowRoot
const mountHere = root.shadowRoot;
const options = { appendTo: mountHere, styleContainer: mountHere};
ReactDOM.createRoot(mountHere).render(
<React.StrictMode>
<PrimeReactProvider value={options}>
<App />
</PrimeReactProvider>
</React.StrictMode>
);
`
};

return (
<>
<DocSectionText {...props}>
<p>
This option allows <i>useStyle</i> to insert dynamic CSS styles into a specific container. This is useful when styles need to be scoped such as in a{' '}
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM" target="_blank">
Shadow DOM
</a>
. By default all dynamic styles are appended to <i>document.head</i>.
</p>
</DocSectionText>
<DocSectionCode code={code} hideToggleCode import hideCodeSandbox hideStackBlitz />
</>
);
}
3 changes: 3 additions & 0 deletions components/lib/api/PrimeReactContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const PrimeReactProvider = (props) => {
const [inputStyle, setInputStyle] = useState(propsValue.inputStyle || 'outlined');
const [locale, setLocale] = useState(propsValue.locale || 'en');
const [appendTo, setAppendTo] = useState(propsValue.appendTo || null);
const [styleContainer, setStyleContainer] = useState(propsValue.styleContainer || null);
const [cssTransition, setCssTransition] = useState(propsValue.cssTransition || true);
const [autoZIndex, setAutoZIndex] = useState(propsValue.autoZIndex || true);
const [hideOverlaysOnDocumentScrolling, setHideOverlaysOnDocumentScrolling] = useState(propsValue.hideOverlaysOnDocumentScrolling || false);
Expand Down Expand Up @@ -90,6 +91,8 @@ export const PrimeReactProvider = (props) => {
setLocale,
appendTo,
setAppendTo,
styleContainer,
setStyleContainer,
cssTransition,
setCssTransition,
autoZIndex,
Expand Down
11 changes: 11 additions & 0 deletions components/lib/api/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ export type InputStyleType = 'outlined' | 'filled';

export type AppendToType = 'self' | HTMLElement | undefined | null;

export type StyleContainerType = ShadowRoot | HTMLElement | undefined | null;

/**
* Filter match modes for DataTable filter menus.
*/
Expand All @@ -157,6 +159,11 @@ export interface APIOptions {
* This option allows components with overlays like dropdowns or popups to be mounted into either the component or any DOM element, such as document body and self.
*/
appendTo?: AppendToType;
/**
* This option allows `useStyle` to insert dynamic CSS styles into a specific container. This is useful when styles need to be scoped such as in a Shadow DOM.
* @defaultValue document.head
*/
styleContainer?: StyleContainerType;
/**
* ZIndexes are managed automatically to make sure layering of overlay components work seamlessly when combining multiple components. When autoZIndex is false, each group increments its zIndex within itself.
*/
Expand Down Expand Up @@ -229,6 +236,10 @@ export interface APIOptions {
* Sets the "appendTo" state of the context.
*/
setAppendTo?: Dispatch<SetStateAction<AppendToType>>;
/**
* Sets the "styleContainer" state of the context.
*/
setStyleContainer?: Dispatch<SetStateAction<StyleContainerType>>;
/**
* Sets the "autoZIndex" state of the context.
*/
Expand Down
2 changes: 1 addition & 1 deletion components/lib/carousel/Carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export const Carousel = React.memo(

const createStyle = () => {
if (!carouselStyle.current) {
carouselStyle.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
carouselStyle.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);
}

let innerHTML = `
Expand Down
2 changes: 1 addition & 1 deletion components/lib/cascadeselect/CascadeSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export const CascadeSelect = React.memo(

const createStyle = () => {
if (!styleElementRef.current) {
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

const selector = `${attributeSelectorState}_panel`;
const innerHTML = `
Expand Down
2 changes: 1 addition & 1 deletion components/lib/contextmenu/ContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const ContextMenu = React.memo(

const createStyle = () => {
if (!styleElementRef.current) {
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

const selector = `${attributeSelectorState}`;
const innerHTML = `
Expand Down
4 changes: 2 additions & 2 deletions components/lib/datatable/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -791,12 +791,12 @@ export const DataTable = React.forwardRef((inProps, ref) => {
};

const createStyleElement = () => {
styleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);
};

const createResponsiveStyle = () => {
if (!responsiveStyleElement.current) {
responsiveStyleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
responsiveStyleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

let tableSelector = `.p-datatable-wrapper ${isVirtualScrollerDisabled() ? '' : '> .p-virtualscroller'} > .p-datatable-table`;
let selector = `.p-datatable[${attributeSelector.current}] > ${tableSelector}`;
Expand Down
2 changes: 1 addition & 1 deletion components/lib/dialog/Dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ export const Dialog = React.forwardRef((inProps, ref) => {
};

const createStyle = () => {
styleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

let innerHTML = '';

Expand Down
2 changes: 1 addition & 1 deletion components/lib/galleria/GalleriaThumbnails.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export const GalleriaThumbnails = React.memo(

const createStyle = () => {
if (!thumbnailsStyle.current) {
thumbnailsStyle.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
thumbnailsStyle.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);
}

let innerHTML = `
Expand Down
6 changes: 4 additions & 2 deletions components/lib/hooks/useStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ export const useStyle = (css, options = {}) => {
const load = () => {
if (!document) return;

styleRef.current = document.querySelector(`style[data-primereact-style-id="${name}"]`) || document.getElementById(id) || document.createElement('style');
const styleContainer = context?.styleContainer || document.head;

styleRef.current = styleContainer.querySelector(`style[data-primereact-style-id="${name}"]`) || document.getElementById(id) || document.createElement('style');

if (!styleRef.current.isConnected) {
styleRef.current.type = 'text/css';
id && (styleRef.current.id = id);
media && (styleRef.current.media = media);

DomHandler.addNonce(styleRef.current, (context && context.nonce) || PrimeReact.nonce);
document.head.appendChild(styleRef.current);
styleContainer.appendChild(styleRef.current);
name && styleRef.current.setAttribute('data-primereact-style-id', name);
}

Expand Down
2 changes: 1 addition & 1 deletion components/lib/megamenu/MegaMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ export const MegaMenu = React.memo(

const createStyle = () => {
if (!styleElementRef.current) {
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

const selector = `${attributeSelectorState}`;
const innerHTML = `
Expand Down
2 changes: 1 addition & 1 deletion components/lib/orderlist/OrderList.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export const OrderList = React.memo(

const createStyle = () => {
if (!styleElementRef.current) {
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

let innerHTML = `
@media screen and (max-width: ${props.breakpoint}) {
Expand Down
2 changes: 1 addition & 1 deletion components/lib/overlaypanel/OverlayPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export const OverlayPanel = React.forwardRef((inProps, ref) => {

const createStyle = () => {
if (!styleElement.current) {
styleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElement.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

let innerHTML = '';

Expand Down
2 changes: 1 addition & 1 deletion components/lib/picklist/PickList.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export const PickList = React.memo(

const createStyle = () => {
if (!styleElementRef.current) {
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

let innerHTML = `
@media screen and (max-width: ${props.breakpoint}) {
Expand Down
2 changes: 1 addition & 1 deletion components/lib/tieredmenu/TieredMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ export const TieredMenu = React.memo(

const createStyle = () => {
if (!styleElementRef.current) {
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce);
styleElementRef.current = DomHandler.createInlineStyle((context && context.nonce) || PrimeReact.nonce, context && context.styleContainer);

const selector = `${attributeSelectorState}`;
const innerHTML = `
Expand Down
6 changes: 3 additions & 3 deletions components/lib/utils/DomHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -1067,19 +1067,19 @@ export default class DomHandler {
return false;
}

static createInlineStyle(nonce) {
static createInlineStyle(nonce, styleContainer = document.head) {
let styleElement = document.createElement('style');

DomHandler.addNonce(styleElement, nonce);
document.head.appendChild(styleElement);
styleContainer.appendChild(styleElement);

return styleElement;
}

static removeInlineStyle(styleElement) {
if (this.isExist(styleElement)) {
try {
document.head.removeChild(styleElement);
styleElement.parentNode.removeChild(styleElement);
} catch (error) {
// style element may have already been removed in a fast refresh
}
Expand Down
2 changes: 1 addition & 1 deletion components/lib/utils/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export declare class DomHandler {
static applyStyle(el: HTMLElement, style: any): void;
static exportCSV(csv: any, filename: string): void;
static saveAs(file: { name: string; url: any }): boolean;
static createInlineStyle(nonce?: string): HTMLStyleElement;
static createInlineStyle(nonce?: string, styleContainer?: ShadowRoot | HTMLElement): HTMLStyleElement;
static removeInlineStyle(styleElement: HTMLStyleElement): HTMLStyleElement | null;
static getTargetElement(target: any): HTMLElement | null;
}
Expand Down
6 changes: 6 additions & 0 deletions pages/configuration/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DocComponent } from '@/components/doc/common/doccomponent';
import { AppendToDoc } from '@/components/doc/configuration/appendtodoc';
import { StyleContainer } from '@/components/doc/configuration/stylecontainer';
import { CSSTransitionDoc } from '@/components/doc/configuration/csstransitiondoc';
import { FilterMatchModeDoc } from '@/components/doc/configuration/filtermatchmodedoc';
import { HideOverlaysDoc } from '@/components/doc/configuration/hideoverlaysdoc';
Expand All @@ -20,6 +21,11 @@ const InstallationPage = () => {
label: 'AppendTo',
component: AppendToDoc
},
{
id: 'stylecontainer',
label: 'StyleContainer',
component: StyleContainer
},
{
id: 'csstransition',
label: 'CSS Transition',
Expand Down

0 comments on commit 3afafe7

Please sign in to comment.