Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat #5246: add styleContainer option for PrimeReactContext #5566

Merged
merged 3 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading