Allow video, audio and iFrame in front components (#18200)
Allow video, audio and iFrame in front components + fix css style properties starting with `--`
This commit is contained in:
+10
@@ -82,6 +82,16 @@ export const componentRegistry: Map<string, ComponentRegistryValue> = new Map([
|
||||
['html-td', createRemoteComponentRenderer(createHtmlHostWrapper('td'))],
|
||||
['html-br', createRemoteComponentRenderer(createHtmlHostWrapper('br'))],
|
||||
['html-hr', createRemoteComponentRenderer(createHtmlHostWrapper('hr'))],
|
||||
[
|
||||
'html-iframe',
|
||||
createRemoteComponentRenderer(createHtmlHostWrapper('iframe')),
|
||||
],
|
||||
['html-video', createRemoteComponentRenderer(createHtmlHostWrapper('video'))],
|
||||
['html-audio', createRemoteComponentRenderer(createHtmlHostWrapper('audio'))],
|
||||
[
|
||||
'html-source',
|
||||
createRemoteComponentRenderer(createHtmlHostWrapper('source')),
|
||||
],
|
||||
['remote-style', createRemoteComponentRenderer(RemoteStyleRenderer)],
|
||||
['remote-fragment', RemoteFragmentRenderer],
|
||||
]);
|
||||
|
||||
+15
-5
@@ -45,10 +45,15 @@ const parseCssString = (
|
||||
const property = declaration.slice(0, colonIndex).trim();
|
||||
const value = declaration.slice(colonIndex + 1).trim();
|
||||
|
||||
const camelProperty = property.replace(/-([a-z])/g, (_, letter: string) =>
|
||||
letter.toUpperCase(),
|
||||
);
|
||||
style[camelProperty] = value;
|
||||
const isCssCustomProperty = property.startsWith('--');
|
||||
|
||||
const key = isCssCustomProperty
|
||||
? property
|
||||
: property.replace(/-([a-z])/g, (_, letter: string) =>
|
||||
letter.toUpperCase(),
|
||||
);
|
||||
|
||||
style[key] = value;
|
||||
}
|
||||
|
||||
return style;
|
||||
@@ -140,13 +145,18 @@ const filterProps = <T extends object>(props: T): T => {
|
||||
|
||||
type WrapperProps = { children?: React.ReactNode } & Record<string, unknown>;
|
||||
|
||||
const FORCED_PROPS_BY_TAG: Record<string, Record<string, unknown>> = {
|
||||
iframe: { sandbox: '' },
|
||||
};
|
||||
|
||||
export const createHtmlHostWrapper = (htmlTag: string) => {
|
||||
const isVoid = VOID_ELEMENTS.has(htmlTag);
|
||||
const forcedProps = FORCED_PROPS_BY_TAG[htmlTag];
|
||||
|
||||
return ({ children, ...props }: WrapperProps) =>
|
||||
React.createElement(
|
||||
htmlTag,
|
||||
filterProps(props),
|
||||
{ ...filterProps(props), ...forcedProps },
|
||||
isVoid ? undefined : children,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,7 +24,11 @@ export {
|
||||
HtmlH6,
|
||||
HtmlHeader,
|
||||
HtmlHr,
|
||||
HtmlIframe,
|
||||
HtmlAudio,
|
||||
HtmlImg,
|
||||
HtmlSource,
|
||||
HtmlVideo,
|
||||
HtmlInput,
|
||||
HtmlLabel,
|
||||
HtmlLi,
|
||||
@@ -69,7 +73,11 @@ export {
|
||||
HtmlH6Element,
|
||||
HtmlHeaderElement,
|
||||
HtmlHrElement,
|
||||
HtmlIframeElement,
|
||||
HtmlAudioElement,
|
||||
HtmlImgElement,
|
||||
HtmlSourceElement,
|
||||
HtmlVideoElement,
|
||||
HtmlInputElement,
|
||||
HtmlLabelElement,
|
||||
HtmlLiElement,
|
||||
@@ -102,7 +110,11 @@ export type {
|
||||
HtmlCommonEvents,
|
||||
HtmlCommonProperties,
|
||||
HtmlFormProperties,
|
||||
HtmlIframeProperties,
|
||||
HtmlAudioProperties,
|
||||
HtmlImgProperties,
|
||||
HtmlSourceProperties,
|
||||
HtmlVideoProperties,
|
||||
HtmlInputProperties,
|
||||
HtmlLabelProperties,
|
||||
HtmlOptionProperties,
|
||||
|
||||
+108
@@ -43,6 +43,10 @@ import {
|
||||
HtmlTdElement,
|
||||
HtmlBrElement,
|
||||
HtmlHrElement,
|
||||
HtmlIframeElement,
|
||||
HtmlVideoElement,
|
||||
HtmlAudioElement,
|
||||
HtmlSourceElement,
|
||||
RemoteStyleElement,
|
||||
} from './remote-elements';
|
||||
|
||||
@@ -1118,6 +1122,110 @@ export const HtmlHr = createRemoteComponent('html-hr', HtmlHrElement, {
|
||||
onDrag: { event: 'drag' },
|
||||
},
|
||||
});
|
||||
export const HtmlIframe = createRemoteComponent(
|
||||
'html-iframe',
|
||||
HtmlIframeElement,
|
||||
{
|
||||
eventProps: {
|
||||
onClick: { event: 'click' },
|
||||
onDoubleClick: { event: 'dblclick' },
|
||||
onMouseDown: { event: 'mousedown' },
|
||||
onMouseUp: { event: 'mouseup' },
|
||||
onMouseOver: { event: 'mouseover' },
|
||||
onMouseOut: { event: 'mouseout' },
|
||||
onMouseEnter: { event: 'mouseenter' },
|
||||
onMouseLeave: { event: 'mouseleave' },
|
||||
onKeyDown: { event: 'keydown' },
|
||||
onKeyUp: { event: 'keyup' },
|
||||
onKeyPress: { event: 'keypress' },
|
||||
onFocus: { event: 'focus' },
|
||||
onBlur: { event: 'blur' },
|
||||
onChange: { event: 'change' },
|
||||
onInput: { event: 'input' },
|
||||
onSubmit: { event: 'submit' },
|
||||
onScroll: { event: 'scroll' },
|
||||
onWheel: { event: 'wheel' },
|
||||
onContextMenu: { event: 'contextmenu' },
|
||||
onDrag: { event: 'drag' },
|
||||
},
|
||||
},
|
||||
);
|
||||
export const HtmlVideo = createRemoteComponent('html-video', HtmlVideoElement, {
|
||||
eventProps: {
|
||||
onClick: { event: 'click' },
|
||||
onDoubleClick: { event: 'dblclick' },
|
||||
onMouseDown: { event: 'mousedown' },
|
||||
onMouseUp: { event: 'mouseup' },
|
||||
onMouseOver: { event: 'mouseover' },
|
||||
onMouseOut: { event: 'mouseout' },
|
||||
onMouseEnter: { event: 'mouseenter' },
|
||||
onMouseLeave: { event: 'mouseleave' },
|
||||
onKeyDown: { event: 'keydown' },
|
||||
onKeyUp: { event: 'keyup' },
|
||||
onKeyPress: { event: 'keypress' },
|
||||
onFocus: { event: 'focus' },
|
||||
onBlur: { event: 'blur' },
|
||||
onChange: { event: 'change' },
|
||||
onInput: { event: 'input' },
|
||||
onSubmit: { event: 'submit' },
|
||||
onScroll: { event: 'scroll' },
|
||||
onWheel: { event: 'wheel' },
|
||||
onContextMenu: { event: 'contextmenu' },
|
||||
onDrag: { event: 'drag' },
|
||||
},
|
||||
});
|
||||
export const HtmlAudio = createRemoteComponent('html-audio', HtmlAudioElement, {
|
||||
eventProps: {
|
||||
onClick: { event: 'click' },
|
||||
onDoubleClick: { event: 'dblclick' },
|
||||
onMouseDown: { event: 'mousedown' },
|
||||
onMouseUp: { event: 'mouseup' },
|
||||
onMouseOver: { event: 'mouseover' },
|
||||
onMouseOut: { event: 'mouseout' },
|
||||
onMouseEnter: { event: 'mouseenter' },
|
||||
onMouseLeave: { event: 'mouseleave' },
|
||||
onKeyDown: { event: 'keydown' },
|
||||
onKeyUp: { event: 'keyup' },
|
||||
onKeyPress: { event: 'keypress' },
|
||||
onFocus: { event: 'focus' },
|
||||
onBlur: { event: 'blur' },
|
||||
onChange: { event: 'change' },
|
||||
onInput: { event: 'input' },
|
||||
onSubmit: { event: 'submit' },
|
||||
onScroll: { event: 'scroll' },
|
||||
onWheel: { event: 'wheel' },
|
||||
onContextMenu: { event: 'contextmenu' },
|
||||
onDrag: { event: 'drag' },
|
||||
},
|
||||
});
|
||||
export const HtmlSource = createRemoteComponent(
|
||||
'html-source',
|
||||
HtmlSourceElement,
|
||||
{
|
||||
eventProps: {
|
||||
onClick: { event: 'click' },
|
||||
onDoubleClick: { event: 'dblclick' },
|
||||
onMouseDown: { event: 'mousedown' },
|
||||
onMouseUp: { event: 'mouseup' },
|
||||
onMouseOver: { event: 'mouseover' },
|
||||
onMouseOut: { event: 'mouseout' },
|
||||
onMouseEnter: { event: 'mouseenter' },
|
||||
onMouseLeave: { event: 'mouseleave' },
|
||||
onKeyDown: { event: 'keydown' },
|
||||
onKeyUp: { event: 'keyup' },
|
||||
onKeyPress: { event: 'keypress' },
|
||||
onFocus: { event: 'focus' },
|
||||
onBlur: { event: 'blur' },
|
||||
onChange: { event: 'change' },
|
||||
onInput: { event: 'input' },
|
||||
onSubmit: { event: 'submit' },
|
||||
onScroll: { event: 'scroll' },
|
||||
onWheel: { event: 'wheel' },
|
||||
onContextMenu: { event: 'contextmenu' },
|
||||
onDrag: { event: 'drag' },
|
||||
},
|
||||
},
|
||||
);
|
||||
export const RemoteStyle = createRemoteComponent(
|
||||
'remote-style',
|
||||
RemoteStyleElement,
|
||||
|
||||
@@ -601,6 +601,120 @@ export const HtmlHrElement = createRemoteElement<
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlIframeProperties = HtmlCommonProperties & {
|
||||
src?: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
allow?: string;
|
||||
sandbox?: string;
|
||||
allowFullScreen?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlIframeElement = createRemoteElement<
|
||||
HtmlIframeProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
src: { type: String },
|
||||
width: { type: String },
|
||||
height: { type: String },
|
||||
allow: { type: String },
|
||||
sandbox: { type: String },
|
||||
allowFullScreen: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlVideoProperties = HtmlCommonProperties & {
|
||||
src?: string;
|
||||
poster?: string;
|
||||
controls?: boolean;
|
||||
autoPlay?: boolean;
|
||||
loop?: boolean;
|
||||
muted?: boolean;
|
||||
preload?: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
crossOrigin?: string;
|
||||
playsInline?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlVideoElement = createRemoteElement<
|
||||
HtmlVideoProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
src: { type: String },
|
||||
poster: { type: String },
|
||||
controls: { type: Boolean },
|
||||
autoPlay: { type: Boolean },
|
||||
loop: { type: Boolean },
|
||||
muted: { type: Boolean },
|
||||
preload: { type: String },
|
||||
width: { type: String },
|
||||
height: { type: String },
|
||||
crossOrigin: { type: String },
|
||||
playsInline: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlAudioProperties = HtmlCommonProperties & {
|
||||
src?: string;
|
||||
controls?: boolean;
|
||||
autoPlay?: boolean;
|
||||
loop?: boolean;
|
||||
muted?: boolean;
|
||||
preload?: string;
|
||||
crossOrigin?: string;
|
||||
};
|
||||
|
||||
export const HtmlAudioElement = createRemoteElement<
|
||||
HtmlAudioProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
src: { type: String },
|
||||
controls: { type: Boolean },
|
||||
autoPlay: { type: Boolean },
|
||||
loop: { type: Boolean },
|
||||
muted: { type: Boolean },
|
||||
preload: { type: String },
|
||||
crossOrigin: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlSourceProperties = HtmlCommonProperties & {
|
||||
src?: string;
|
||||
type?: string;
|
||||
media?: string;
|
||||
};
|
||||
|
||||
export const HtmlSourceElement = createRemoteElement<
|
||||
HtmlSourceProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
src: { type: String },
|
||||
type: { type: String },
|
||||
media: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type RemoteStyleProperties = {
|
||||
cssText?: string;
|
||||
styleKey?: string;
|
||||
@@ -660,6 +774,10 @@ customElements.define('html-th', HtmlThElement);
|
||||
customElements.define('html-td', HtmlTdElement);
|
||||
customElements.define('html-br', HtmlBrElement);
|
||||
customElements.define('html-hr', HtmlHrElement);
|
||||
customElements.define('html-iframe', HtmlIframeElement);
|
||||
customElements.define('html-video', HtmlVideoElement);
|
||||
customElements.define('html-audio', HtmlAudioElement);
|
||||
customElements.define('html-source', HtmlSourceElement);
|
||||
customElements.define('remote-style', RemoteStyleElement);
|
||||
customElements.define('remote-root', RemoteRootElement);
|
||||
customElements.define('remote-fragment', RemoteFragmentElement);
|
||||
@@ -709,6 +827,10 @@ declare global {
|
||||
'html-td': InstanceType<typeof HtmlTdElement>;
|
||||
'html-br': InstanceType<typeof HtmlBrElement>;
|
||||
'html-hr': InstanceType<typeof HtmlHrElement>;
|
||||
'html-iframe': InstanceType<typeof HtmlIframeElement>;
|
||||
'html-video': InstanceType<typeof HtmlVideoElement>;
|
||||
'html-audio': InstanceType<typeof HtmlAudioElement>;
|
||||
'html-source': InstanceType<typeof HtmlSourceElement>;
|
||||
'remote-style': InstanceType<typeof RemoteStyleElement>;
|
||||
'remote-root': InstanceType<typeof RemoteRootElement>;
|
||||
'remote-fragment': InstanceType<typeof RemoteFragmentElement>;
|
||||
|
||||
@@ -145,4 +145,56 @@ export const ALLOWED_HTML_ELEMENTS: AllowedHtmlElement[] = [
|
||||
},
|
||||
{ tag: 'html-br', name: 'HtmlBr', properties: {} },
|
||||
{ tag: 'html-hr', name: 'HtmlHr', properties: {} },
|
||||
{
|
||||
tag: 'html-iframe',
|
||||
name: 'HtmlIframe',
|
||||
properties: {
|
||||
src: { type: 'string', optional: true },
|
||||
title: { type: 'string', optional: true },
|
||||
width: { type: 'string', optional: true },
|
||||
height: { type: 'string', optional: true },
|
||||
allow: { type: 'string', optional: true },
|
||||
sandbox: { type: 'string', optional: true },
|
||||
allowFullScreen: { type: 'boolean', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-video',
|
||||
name: 'HtmlVideo',
|
||||
properties: {
|
||||
src: { type: 'string', optional: true },
|
||||
poster: { type: 'string', optional: true },
|
||||
controls: { type: 'boolean', optional: true },
|
||||
autoPlay: { type: 'boolean', optional: true },
|
||||
loop: { type: 'boolean', optional: true },
|
||||
muted: { type: 'boolean', optional: true },
|
||||
preload: { type: 'string', optional: true },
|
||||
width: { type: 'string', optional: true },
|
||||
height: { type: 'string', optional: true },
|
||||
crossOrigin: { type: 'string', optional: true },
|
||||
playsInline: { type: 'boolean', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-audio',
|
||||
name: 'HtmlAudio',
|
||||
properties: {
|
||||
src: { type: 'string', optional: true },
|
||||
controls: { type: 'boolean', optional: true },
|
||||
autoPlay: { type: 'boolean', optional: true },
|
||||
loop: { type: 'boolean', optional: true },
|
||||
muted: { type: 'boolean', optional: true },
|
||||
preload: { type: 'string', optional: true },
|
||||
crossOrigin: { type: 'string', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-source',
|
||||
name: 'HtmlSource',
|
||||
properties: {
|
||||
src: { type: 'string', optional: true },
|
||||
type: { type: 'string', optional: true },
|
||||
media: { type: 'string', optional: true },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user