Merge branch 'release' of https://github.com/appsmithorg/appsmith into release
This commit is contained in:
commit
902fd2dcc7
3
app/client/src/assets/icons/widget/video.svg
Normal file
3
app/client/src/assets/icons/widget/video.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="17" height="14" viewBox="0 0 17 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.8682 7.35302L6.52617 9.1177V5.23541L10.8682 7.35302ZM14.4211 2.41192H2.57929L2.57891 11.5883H14.4211V2.41192ZM2.97404 1.00016C1.78945 0.987885 0.999229 1.68148 1 2.75257V11.2353C0.999615 12.2941 1.78945 13 2.97363 13H14.0264C15.2105 13 16 12.2579 16 11.2353L15.9996 2.76485C15.9996 1.70604 15.2102 1.00016 14.026 1.00016H2.97404Z" fill="#C4C4C4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 504 B |
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverInteractionKind,
|
||||||
|
PopoverPosition,
|
||||||
|
} from "@blueprintjs/core";
|
||||||
|
import { Colors } from "constants/Colors";
|
||||||
|
import VideoComponent, { VideoComponentProps } from "./VideoComponent";
|
||||||
|
import styled, { AnyStyledComponent } from "styled-components";
|
||||||
|
import { ControlIcons } from "icons/ControlIcons";
|
||||||
|
const PlayIcon = styled(ControlIcons.PLAY_VIDEO as AnyStyledComponent)`
|
||||||
|
position: relative;
|
||||||
|
top: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
svg {
|
||||||
|
path {
|
||||||
|
fill: ${Colors.POMEGRANATE};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PlayerWrapper = styled.div` import React, { Ref } from "react";
|
||||||
|
width: 600px;
|
||||||
|
height: 400px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PopoverVideo = (props: VideoComponentProps) => {
|
||||||
|
return (
|
||||||
|
<div onClick={e => e.stopPropagation()}>
|
||||||
|
<Popover
|
||||||
|
position={PopoverPosition.AUTO}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
minimal
|
||||||
|
usePortal
|
||||||
|
enforceFocus={false}
|
||||||
|
lazy={true}
|
||||||
|
modifiers={{
|
||||||
|
flip: {
|
||||||
|
behavior: ["right", "left", "bottom", "top"],
|
||||||
|
},
|
||||||
|
keepTogether: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
arrow: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
preventOverflow: {
|
||||||
|
enabled: true,
|
||||||
|
boundariesElement: "viewport",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlayIcon></PlayIcon>
|
||||||
|
<PlayerWrapper>
|
||||||
|
<VideoComponent url={props.url} />
|
||||||
|
</PlayerWrapper>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PopoverVideo;
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
Condition,
|
Condition,
|
||||||
} from "widgets/TableWidget";
|
} from "widgets/TableWidget";
|
||||||
import { isString } from "lodash";
|
import { isString } from "lodash";
|
||||||
import VideoComponent from "components/designSystems/appsmith/VideoComponent";
|
import PopoverVideo from "components/designSystems/appsmith/PopoverVideo";
|
||||||
import Button from "components/editorComponents/Button";
|
import Button from "components/editorComponents/Button";
|
||||||
import AutoToolTipComponent from "components/designSystems/appsmith/AutoToolTipComponent";
|
import AutoToolTipComponent from "components/designSystems/appsmith/AutoToolTipComponent";
|
||||||
import TableColumnMenuPopup from "./TableColumnMenu";
|
import TableColumnMenuPopup from "./TableColumnMenu";
|
||||||
|
|
@ -505,7 +505,7 @@ export const renderCell = (
|
||||||
} else if (isString(value) && youtubeRegex.test(value)) {
|
} else if (isString(value) && youtubeRegex.test(value)) {
|
||||||
return (
|
return (
|
||||||
<CellWrapper isHidden={isHidden} className="video-cell">
|
<CellWrapper isHidden={isHidden} className="video-cell">
|
||||||
<VideoComponent url={value} />
|
<PopoverVideo url={value} />
|
||||||
</CellWrapper>
|
</CellWrapper>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,72 @@
|
||||||
import React from "react";
|
|
||||||
import ReactPlayer from "react-player";
|
import ReactPlayer from "react-player";
|
||||||
import {
|
import React, { Ref } from "react";
|
||||||
Popover,
|
import styled from "styled-components";
|
||||||
PopoverInteractionKind,
|
import { ENTER_VIDEO_URL } from "constants/messages";
|
||||||
PopoverPosition,
|
export interface VideoComponentProps {
|
||||||
} from "@blueprintjs/core";
|
url?: string;
|
||||||
import { ControlIcons } from "icons/ControlIcons";
|
autoplay?: boolean;
|
||||||
import styled, { AnyStyledComponent } from "styled-components";
|
controls?: boolean;
|
||||||
import { Colors } from "constants/Colors";
|
onStart?: () => void;
|
||||||
|
onPlay?: () => void;
|
||||||
const PlayerWrapper = styled.div`
|
onPause?: () => void;
|
||||||
width: 600px;
|
onEnded?: () => void;
|
||||||
height: 400px;
|
onReady?: () => void;
|
||||||
`;
|
onProgress?: () => void;
|
||||||
|
onSeek?: () => void;
|
||||||
const PlayIcon = styled(ControlIcons.PLAY_VIDEO as AnyStyledComponent)`
|
onError?: () => void;
|
||||||
position: relative;
|
player?: Ref<ReactPlayer>;
|
||||||
top: 10px;
|
|
||||||
&:hover {
|
|
||||||
svg {
|
|
||||||
path {
|
|
||||||
fill: ${Colors.POMEGRANATE};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface VideoComponentProps {
|
|
||||||
url: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoComponent = (props: VideoComponentProps) => {
|
const ErrorContainer = styled.div`
|
||||||
return (
|
display: flex;
|
||||||
<div onClick={e => e.stopPropagation()}>
|
align-items: center;
|
||||||
<Popover
|
justify-content: center;
|
||||||
position={PopoverPosition.AUTO}
|
width: 100%;
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
height: 100%;
|
||||||
minimal
|
`;
|
||||||
usePortal
|
|
||||||
enforceFocus={false}
|
|
||||||
lazy={true}
|
|
||||||
modifiers={{
|
|
||||||
flip: {
|
|
||||||
behavior: ["right", "left", "bottom", "top"],
|
|
||||||
},
|
|
||||||
keepTogether: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
arrow: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
preventOverflow: {
|
|
||||||
enabled: true,
|
|
||||||
boundariesElement: "viewport",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlayIcon width="80" height="52" color="black" />
|
|
||||||
<PlayerWrapper>
|
|
||||||
<ReactPlayer
|
|
||||||
playing={true}
|
|
||||||
url={props.url}
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
/>
|
|
||||||
</PlayerWrapper>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VideoComponent;
|
const Error = styled.span``;
|
||||||
|
|
||||||
|
export default function VideoComponent(props: VideoComponentProps) {
|
||||||
|
const {
|
||||||
|
url,
|
||||||
|
autoplay,
|
||||||
|
controls,
|
||||||
|
onStart,
|
||||||
|
onPlay,
|
||||||
|
onPause,
|
||||||
|
onEnded,
|
||||||
|
onReady,
|
||||||
|
onProgress,
|
||||||
|
onSeek,
|
||||||
|
onError,
|
||||||
|
player,
|
||||||
|
} = props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{url ? (
|
||||||
|
<ReactPlayer
|
||||||
|
url={url}
|
||||||
|
ref={player}
|
||||||
|
playing={autoplay}
|
||||||
|
controls={controls || true}
|
||||||
|
onStart={onStart}
|
||||||
|
onPlay={onPlay}
|
||||||
|
onPause={onPause}
|
||||||
|
onEnded={onEnded}
|
||||||
|
onReady={onReady}
|
||||||
|
onProgress={onProgress}
|
||||||
|
onSeek={onSeek}
|
||||||
|
onError={onError}
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
pip={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ErrorContainer>
|
||||||
|
<Error>{ENTER_VIDEO_URL}</Error>
|
||||||
|
</ErrorContainer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ export enum EventType {
|
||||||
ON_MARKER_CLICK = "ON_MARKER_CLICK",
|
ON_MARKER_CLICK = "ON_MARKER_CLICK",
|
||||||
ON_CREATE_MARKER = "ON_CREATE_MARKER",
|
ON_CREATE_MARKER = "ON_CREATE_MARKER",
|
||||||
ON_TAB_CHANGE = "ON_TAB_CHANGE",
|
ON_TAB_CHANGE = "ON_TAB_CHANGE",
|
||||||
|
ON_VIDEO_START = "ON_VIDEO_START",
|
||||||
|
ON_VIDEO_END = "ON_VIDEO_END",
|
||||||
|
ON_VIDEO_PLAY = "ON_VIDEO_PLAY",
|
||||||
|
ON_VIDEO_PAUSE = "ON_VIDEO_PAUSE",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ActionType =
|
export type ActionType =
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ const FIELD_VALUES: Record<
|
||||||
// onRowSelected: "Function Call",
|
// onRowSelected: "Function Call",
|
||||||
// onPageChange: "Function Call",
|
// onPageChange: "Function Call",
|
||||||
},
|
},
|
||||||
|
VIDEO_WIDGET: {
|
||||||
|
url: "string",
|
||||||
|
autoPlay: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
},
|
||||||
IMAGE_WIDGET: {
|
IMAGE_WIDGET: {
|
||||||
image: "string",
|
image: "string",
|
||||||
defaultImage: "string",
|
defaultImage: "string",
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,10 @@ export const HelpMap = {
|
||||||
path: "/widget-reference/table",
|
path: "/widget-reference/table",
|
||||||
searchKey: "Table",
|
searchKey: "Table",
|
||||||
},
|
},
|
||||||
|
VIDEO_WIDGET: {
|
||||||
|
path: "/widget-reference/video",
|
||||||
|
searchKey: "Video",
|
||||||
|
},
|
||||||
DROP_DOWN_WIDGET: {
|
DROP_DOWN_WIDGET: {
|
||||||
path: "/widget-reference/dropdown",
|
path: "/widget-reference/dropdown",
|
||||||
searchKey: "Dropdown",
|
searchKey: "Dropdown",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ export enum WidgetTypes {
|
||||||
CANVAS_WIDGET = "CANVAS_WIDGET",
|
CANVAS_WIDGET = "CANVAS_WIDGET",
|
||||||
ICON_WIDGET = "ICON_WIDGET",
|
ICON_WIDGET = "ICON_WIDGET",
|
||||||
FILE_PICKER_WIDGET = "FILE_PICKER_WIDGET",
|
FILE_PICKER_WIDGET = "FILE_PICKER_WIDGET",
|
||||||
|
VIDEO_WIDGET = "VIDEO_WIDGET",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WidgetType = keyof typeof WidgetTypes;
|
export type WidgetType = keyof typeof WidgetTypes;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ export const NAME_SPACE_ERROR = "Name must not have spaces";
|
||||||
export const FORM_VALIDATION_EMPTY_EMAIL = "Please enter an email";
|
export const FORM_VALIDATION_EMPTY_EMAIL = "Please enter an email";
|
||||||
export const FORM_VALIDATION_INVALID_EMAIL =
|
export const FORM_VALIDATION_INVALID_EMAIL =
|
||||||
"Please provide a valid email address";
|
"Please provide a valid email address";
|
||||||
|
export const ENTER_VIDEO_URL = "Please provide a valid url";
|
||||||
|
|
||||||
export const FORM_VALIDATION_EMPTY_PASSWORD = "Please enter the password";
|
export const FORM_VALIDATION_EMPTY_PASSWORD = "Please enter the password";
|
||||||
export const FORM_VALIDATION_PASSWORD_RULE =
|
export const FORM_VALIDATION_PASSWORD_RULE =
|
||||||
"Please provide a password with a minimum of 6 characters";
|
"Please provide a password with a minimum of 6 characters";
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { ReactComponent as CollapseIcon } from "assets/icons/widget/collapse.svg
|
||||||
import { ReactComponent as ContainerIcon } from "assets/icons/widget/container.svg";
|
import { ReactComponent as ContainerIcon } from "assets/icons/widget/container.svg";
|
||||||
import { ReactComponent as DatePickerIcon } from "assets/icons/widget/datepicker.svg";
|
import { ReactComponent as DatePickerIcon } from "assets/icons/widget/datepicker.svg";
|
||||||
import { ReactComponent as TableIcon } from "assets/icons/widget/table.svg";
|
import { ReactComponent as TableIcon } from "assets/icons/widget/table.svg";
|
||||||
|
import { ReactComponent as VideoIcon } from "assets/icons/widget/video.svg";
|
||||||
import { ReactComponent as DropDownIcon } from "assets/icons/widget/dropdown.svg";
|
import { ReactComponent as DropDownIcon } from "assets/icons/widget/dropdown.svg";
|
||||||
import { ReactComponent as CheckboxIcon } from "assets/icons/widget/checkbox.svg";
|
import { ReactComponent as CheckboxIcon } from "assets/icons/widget/checkbox.svg";
|
||||||
import { ReactComponent as RadioGroupIcon } from "assets/icons/widget/radio.svg";
|
import { ReactComponent as RadioGroupIcon } from "assets/icons/widget/radio.svg";
|
||||||
|
|
@ -60,6 +61,11 @@ export const WidgetIcons: {
|
||||||
<TableIcon />
|
<TableIcon />
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
),
|
),
|
||||||
|
VIDEO_WIDGET: (props: IconProps) => (
|
||||||
|
<IconWrapper {...props}>
|
||||||
|
<VideoIcon />
|
||||||
|
</IconWrapper>
|
||||||
|
),
|
||||||
DROP_DOWN_WIDGET: (props: IconProps) => (
|
DROP_DOWN_WIDGET: (props: IconProps) => (
|
||||||
<IconWrapper {...props}>
|
<IconWrapper {...props}>
|
||||||
<DropDownIcon />
|
<DropDownIcon />
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,76 @@ const PropertyPaneConfigResponse: PropertyPaneConfigsResponse["data"] = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
VIDEO_WIDGET: [
|
||||||
|
{
|
||||||
|
id: "17.1",
|
||||||
|
sectionName: "General",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: "17.1.1",
|
||||||
|
propertyName: "url",
|
||||||
|
label: "Url",
|
||||||
|
controlType: "INPUT_TEXT",
|
||||||
|
placeholderText: "Enter url",
|
||||||
|
inputType: "TEXT",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "17.1.1",
|
||||||
|
propertyName: "autoPlay",
|
||||||
|
label: "autoPlay",
|
||||||
|
helpText: "Video will be automatically played",
|
||||||
|
controlType: "SWITCH",
|
||||||
|
isJSConvertible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "17.1.2",
|
||||||
|
helpText: "Controls the visibility of the widget",
|
||||||
|
propertyName: "isVisible",
|
||||||
|
label: "Visible",
|
||||||
|
controlType: "SWITCH",
|
||||||
|
isJSConvertible: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "17.2",
|
||||||
|
sectionName: "Actions",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: "17.2.1",
|
||||||
|
helpText: "Triggers an action when the video starts playing",
|
||||||
|
propertyName: "onStart",
|
||||||
|
label: "onStart",
|
||||||
|
controlType: "ACTION_SELECTOR",
|
||||||
|
isJSConvertible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "17.2.2",
|
||||||
|
helpText: "Triggers an action when the video ends",
|
||||||
|
propertyName: "onEnd",
|
||||||
|
label: "onEnd",
|
||||||
|
controlType: "ACTION_SELECTOR",
|
||||||
|
isJSConvertible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "17.2.3",
|
||||||
|
helpText: "Triggers an action when the video is played",
|
||||||
|
propertyName: "onPlay",
|
||||||
|
label: "onPlay",
|
||||||
|
controlType: "ACTION_SELECTOR",
|
||||||
|
isJSConvertible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "17.2.4",
|
||||||
|
helpText: "Triggers an action when the video is paused",
|
||||||
|
propertyName: "onPause",
|
||||||
|
label: "onPause",
|
||||||
|
controlType: "ACTION_SELECTOR",
|
||||||
|
isJSConvertible: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
TABLE_WIDGET: [
|
TABLE_WIDGET: [
|
||||||
{
|
{
|
||||||
id: "7.1",
|
id: "7.1",
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,14 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
widgetName: "DatePicker",
|
widgetName: "DatePicker",
|
||||||
defaultDate: moment().format("DD/MM/YYYY HH:mm"),
|
defaultDate: moment().format("DD/MM/YYYY HH:mm"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
VIDEO_WIDGET: {
|
||||||
|
rows: 7,
|
||||||
|
columns: 7,
|
||||||
|
widgetName: "Video",
|
||||||
|
url: "https://www.youtube.com/watch?v=mzqK0QIZRLs",
|
||||||
|
autoPlay: false,
|
||||||
|
},
|
||||||
TABLE_WIDGET: {
|
TABLE_WIDGET: {
|
||||||
rows: 7,
|
rows: 7,
|
||||||
columns: 8,
|
columns: 8,
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,11 @@ const WidgetSidebarResponse: {
|
||||||
widgetCardName: "Table",
|
widgetCardName: "Table",
|
||||||
key: generateReactKey(),
|
key: generateReactKey(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "VIDEO_WIDGET",
|
||||||
|
widgetCardName: "Video",
|
||||||
|
key: generateReactKey(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "MAP_WIDGET",
|
type: "MAP_WIDGET",
|
||||||
widgetCardName: "Map",
|
widgetCardName: "Map",
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import { FormButtonWidgetProps } from "widgets/FormButtonWidget";
|
||||||
import { MapWidgetProps } from "widgets/MapWidget";
|
import { MapWidgetProps } from "widgets/MapWidget";
|
||||||
import { ModalWidgetProps } from "widgets/ModalWidget";
|
import { ModalWidgetProps } from "widgets/ModalWidget";
|
||||||
import { IconWidgetProps } from "widgets/IconWidget";
|
import { IconWidgetProps } from "widgets/IconWidget";
|
||||||
|
import { VideoWidgetProps } from "widgets/VideoWidget";
|
||||||
|
|
||||||
const initialState: WidgetConfigReducerState = WidgetConfigResponse;
|
const initialState: WidgetConfigReducerState = WidgetConfigResponse;
|
||||||
|
|
||||||
|
|
@ -57,6 +58,7 @@ export interface WidgetConfigReducerState {
|
||||||
WidgetConfigProps;
|
WidgetConfigProps;
|
||||||
DATE_PICKER_WIDGET: Partial<DatePickerWidgetProps> & WidgetConfigProps;
|
DATE_PICKER_WIDGET: Partial<DatePickerWidgetProps> & WidgetConfigProps;
|
||||||
TABLE_WIDGET: Partial<TableWidgetProps> & WidgetConfigProps;
|
TABLE_WIDGET: Partial<TableWidgetProps> & WidgetConfigProps;
|
||||||
|
VIDEO_WIDGET: Partial<VideoWidgetProps> & WidgetConfigProps;
|
||||||
DROP_DOWN_WIDGET: Partial<DropdownWidgetProps> & WidgetConfigProps;
|
DROP_DOWN_WIDGET: Partial<DropdownWidgetProps> & WidgetConfigProps;
|
||||||
CHECKBOX_WIDGET: Partial<CheckboxWidgetProps> & WidgetConfigProps;
|
CHECKBOX_WIDGET: Partial<CheckboxWidgetProps> & WidgetConfigProps;
|
||||||
RADIO_GROUP_WIDGET: Partial<RadioGroupWidgetProps> & WidgetConfigProps;
|
RADIO_GROUP_WIDGET: Partial<RadioGroupWidgetProps> & WidgetConfigProps;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,10 @@ import TableWidget, {
|
||||||
TableWidgetProps,
|
TableWidgetProps,
|
||||||
ProfiledTableWidget,
|
ProfiledTableWidget,
|
||||||
} from "widgets/TableWidget";
|
} from "widgets/TableWidget";
|
||||||
|
import VideoWidget, {
|
||||||
|
VideoWidgetProps,
|
||||||
|
ProfiledVideoWidget,
|
||||||
|
} from "widgets/VideoWidget";
|
||||||
import TabsWidget, {
|
import TabsWidget, {
|
||||||
TabsWidgetProps,
|
TabsWidgetProps,
|
||||||
TabContainerWidgetProps,
|
TabContainerWidgetProps,
|
||||||
|
|
@ -204,6 +208,21 @@ export default class WidgetBuilderRegistry {
|
||||||
TableWidget.getDefaultPropertiesMap(),
|
TableWidget.getDefaultPropertiesMap(),
|
||||||
TableWidget.getMetaPropertiesMap(),
|
TableWidget.getMetaPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
"VIDEO_WIDGET",
|
||||||
|
{
|
||||||
|
buildWidget(widgetData: VideoWidgetProps): JSX.Element {
|
||||||
|
return <ProfiledVideoWidget {...widgetData} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
VideoWidget.getPropertyValidationMap(),
|
||||||
|
VideoWidget.getDerivedPropertiesMap(),
|
||||||
|
VideoWidget.getTriggerPropertyMap(),
|
||||||
|
VideoWidget.getDefaultPropertiesMap(),
|
||||||
|
VideoWidget.getMetaPropertiesMap(),
|
||||||
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
"FILE_PICKER_WIDGET",
|
"FILE_PICKER_WIDGET",
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,13 @@ export const entityDefinitions = {
|
||||||
isVisible: isVisible,
|
isVisible: isVisible,
|
||||||
searchText: "string",
|
searchText: "string",
|
||||||
}),
|
}),
|
||||||
|
VIDEO_WIDGET: (widget: any) => ({
|
||||||
|
"!doc":
|
||||||
|
"Video widget can be used for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable, Vimeo, Wistia, Mixcloud, and DailyMotion.",
|
||||||
|
"!url": "https://docs.appsmith.com/widget-reference/video",
|
||||||
|
playState: "number",
|
||||||
|
autoPlay: "bool",
|
||||||
|
}),
|
||||||
DROP_DOWN_WIDGET: {
|
DROP_DOWN_WIDGET: {
|
||||||
"!doc":
|
"!doc":
|
||||||
"Dropdown is used to capture user input/s from a specified list of permitted inputs. A Dropdown can capture a single choice as well as multiple choices",
|
"Dropdown is used to capture user input/s from a specified list of permitted inputs. A Dropdown can capture a single choice as well as multiple choices",
|
||||||
|
|
|
||||||
135
app/client/src/widgets/VideoWidget.tsx
Normal file
135
app/client/src/widgets/VideoWidget.tsx
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import React, { Suspense, lazy } from "react";
|
||||||
|
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||||
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
|
import { EventType } from "constants/ActionConstants";
|
||||||
|
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||||
|
import {
|
||||||
|
WidgetPropertyValidationType,
|
||||||
|
BASE_WIDGET_VALIDATION,
|
||||||
|
} from "utils/ValidationFactory";
|
||||||
|
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||||
|
import Skeleton from "components/utils/Skeleton";
|
||||||
|
import * as Sentry from "@sentry/react";
|
||||||
|
import { retryPromise } from "utils/AppsmithUtils";
|
||||||
|
import ReactPlayer from "react-player";
|
||||||
|
|
||||||
|
const VideoComponent = lazy(() =>
|
||||||
|
retryPromise(() =>
|
||||||
|
import("components/designSystems/appsmith/VideoComponent"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export enum PlayState {
|
||||||
|
NOT_STARTED = "NOT_STARTED",
|
||||||
|
PAUSED = "PAUSED",
|
||||||
|
ENDED = "ENDED",
|
||||||
|
PLAYING = "PLAYING",
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoWidget extends BaseWidget<VideoWidgetProps, WidgetState> {
|
||||||
|
private _player = React.createRef<ReactPlayer>();
|
||||||
|
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
||||||
|
return {
|
||||||
|
...BASE_WIDGET_VALIDATION,
|
||||||
|
url: VALIDATION_TYPES.TEXT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getMetaPropertiesMap(): Record<string, any> {
|
||||||
|
return {
|
||||||
|
playState: PlayState.NOT_STARTED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDefaultPropertiesMap(): Record<string, string> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getTriggerPropertyMap(): TriggerPropertiesMap {
|
||||||
|
return {
|
||||||
|
onStart: true,
|
||||||
|
onEnd: true,
|
||||||
|
onPlay: true,
|
||||||
|
onPause: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps: VideoWidgetProps) {
|
||||||
|
return nextProps.url !== this.props.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPageView() {
|
||||||
|
const { url, autoPlay, onStart, onEnd, onPause, onPlay } = this.props;
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<Skeleton />}>
|
||||||
|
<VideoComponent
|
||||||
|
player={this._player}
|
||||||
|
url={url}
|
||||||
|
autoplay={autoPlay}
|
||||||
|
controls={true}
|
||||||
|
onStart={() => {
|
||||||
|
this.updateWidgetMetaProperty("playState", PlayState.PLAYING);
|
||||||
|
if (onStart) {
|
||||||
|
super.executeAction({
|
||||||
|
dynamicString: onStart,
|
||||||
|
event: {
|
||||||
|
type: EventType.ON_VIDEO_START,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onPlay={() => {
|
||||||
|
this.updateWidgetMetaProperty("playState", PlayState.PLAYING);
|
||||||
|
if (onPlay) {
|
||||||
|
super.executeAction({
|
||||||
|
dynamicString: onPlay,
|
||||||
|
event: {
|
||||||
|
type: EventType.ON_VIDEO_PLAY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onPause={() => {
|
||||||
|
//TODO: We do not want the pause event for onSeek or onEnd.
|
||||||
|
this.updateWidgetMetaProperty("playState", PlayState.PAUSED);
|
||||||
|
if (onPause) {
|
||||||
|
super.executeAction({
|
||||||
|
dynamicString: onPause,
|
||||||
|
event: {
|
||||||
|
type: EventType.ON_VIDEO_PAUSE,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onEnded={() => {
|
||||||
|
this.updateWidgetMetaProperty("playState", PlayState.ENDED);
|
||||||
|
if (onEnd) {
|
||||||
|
super.executeAction({
|
||||||
|
dynamicString: onEnd,
|
||||||
|
event: {
|
||||||
|
type: EventType.ON_VIDEO_END,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidgetType(): WidgetType {
|
||||||
|
return "VIDEO_WIDGET";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VideoWidgetProps extends WidgetProps {
|
||||||
|
url: string;
|
||||||
|
autoPlay: boolean;
|
||||||
|
onStart?: string;
|
||||||
|
onPause?: string;
|
||||||
|
onPlay?: string;
|
||||||
|
onEnd?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VideoWidget;
|
||||||
|
export const ProfiledVideoWidget = Sentry.withProfiler(VideoWidget);
|
||||||
Loading…
Reference in New Issue
Block a user