import React, { useState } from "react";
import { MenuItem, Classes, Button as BButton } from "@blueprintjs/core";
import {
CellWrapper,
CellCheckboxWrapper,
CellCheckbox,
ActionWrapper,
SortIconWrapper,
DraggableHeaderWrapper,
} from "./TableStyledWrappers";
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
import {
ColumnTypes,
CellAlignmentTypes,
VerticalAlignmentTypes,
ColumnProperties,
CellLayoutProperties,
TableStyles,
} from "./Constants";
import { isString, isEmpty, findIndex } from "lodash";
import PopoverVideo from "widgets/VideoWidget/component/PopoverVideo";
import Button from "components/editorComponents/Button";
import AutoToolTipComponent from "widgets/TableWidget/component/AutoToolTipComponent";
import { ControlIcons } from "icons/ControlIcons";
import { AnyStyledComponent } from "styled-components";
import styled from "constants/DefaultTheme";
import { Colors } from "constants/Colors";
import { DropdownOption } from "widgets/DropdownWidget/constants";
import { IconName, IconNames } from "@blueprintjs/icons";
import { Select, IItemRendererProps } from "@blueprintjs/select";
import { FontStyleTypes, TextSizes } from "constants/WidgetConstants";
import { noop } from "utils/AppsmithUtils";
import {
ButtonStyleType,
ButtonVariant,
ButtonBoxShadow,
ButtonBorderRadius,
} from "components/constants";
//TODO(abstraction leak)
import { StyledButton } from "widgets/IconButtonWidget/component";
export const renderCell = (
value: any,
columnType: string,
isHidden: boolean,
cellProperties: CellLayoutProperties,
tableWidth: number,
isCellVisible: boolean,
onClick: () => void = noop,
isSelected?: boolean,
) => {
switch (columnType) {
case ColumnTypes.IMAGE:
if (!value) {
return (
);
} else if (!isString(value)) {
return (
Invalid Image
);
}
// better regex: /(?
{value
.toString()
// imageSplitRegex matched "," and char before it, so add space before ","
.replace(imageSplitRegex, (match) =>
match.length > 1 ? `${match.charAt(0)} ,` : " ,",
)
.split(imageSplitRegex)
.map((item: string, index: number) => {
if (imageUrlRegex.test(item) || base64ImageRegex.test(item)) {
return (
{
if (isSelected) {
e.stopPropagation();
}
onClick();
}}
>
);
} else {
return Invalid Image
;
}
})}
);
case ColumnTypes.VIDEO:
const youtubeRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=|\?v=)([^#&?]*).*/;
if (!value) {
return (
);
} else if (isString(value) && youtubeRegex.test(value)) {
return (
);
} else {
return (
Invalid Video Link
);
}
default:
return (
{value && columnType === ColumnTypes.URL && cellProperties.displayText
? cellProperties.displayText
: !!value
? value.toString()
: ""}
);
}
};
interface RenderIconButtonProps {
isSelected: boolean;
columnActions?: ColumnAction[];
iconName?: IconName;
buttonVariant: ButtonVariant;
buttonStyle: ButtonStyleType;
borderRadius: ButtonBorderRadius;
boxShadow: ButtonBoxShadow;
boxShadowColor: string;
onCommandClick: (dynamicTrigger: string, onComplete: () => void) => void;
isCellVisible: boolean;
}
export const renderIconButton = (
props: RenderIconButtonProps,
isHidden: boolean,
cellProperties: CellLayoutProperties,
) => {
if (!props.columnActions)
return ;
return (
{props.columnActions.map((action: ColumnAction, index: number) => {
return (
);
})}
);
};
function IconButton(props: {
iconName?: IconName;
onCommandClick: (dynamicTrigger: string, onComplete: () => void) => void;
isSelected: boolean;
action: ColumnAction;
buttonStyle: ButtonStyleType;
buttonVariant: ButtonVariant;
borderRadius: ButtonBorderRadius;
boxShadow: ButtonBoxShadow;
boxShadowColor: string;
}): JSX.Element {
const [loading, setLoading] = useState(false);
const onComplete = () => {
setLoading(false);
};
const handlePropagation = (
e: React.MouseEvent,
) => {
if (props.isSelected) {
e.stopPropagation();
}
};
const handleClick = () => {
if (props.action.dynamicTrigger) {
setLoading(true);
props.onCommandClick(props.action.dynamicTrigger, onComplete);
}
};
return (
);
}
interface RenderActionProps {
isSelected: boolean;
columnActions?: ColumnAction[];
backgroundColor: string;
buttonLabelColor: string;
isDisabled: boolean;
isCellVisible: boolean;
onCommandClick: (dynamicTrigger: string, onComplete: () => void) => void;
}
export const renderActions = (
props: RenderActionProps,
isHidden: boolean,
cellProperties: CellLayoutProperties,
) => {
if (!props.columnActions)
return (
);
return (
{props.columnActions.map((action: ColumnAction, index: number) => {
return (
);
})}
);
};
function TableAction(props: {
isSelected: boolean;
action: ColumnAction;
backgroundColor: string;
buttonLabelColor: string;
isDisabled: boolean;
isCellVisible: boolean;
onCommandClick: (dynamicTrigger: string, onComplete: () => void) => void;
}) {
const [loading, setLoading] = useState(false);
const onComplete = () => {
setLoading(false);
};
return (
{
if (props.isSelected) {
e.stopPropagation();
}
}}
>
{props.isCellVisible ? (
);
}
function CheckBoxLineIcon() {
return (
);
}
function CheckBoxCheckIcon() {
return (
);
}
export const renderCheckBoxCell = (isChecked: boolean) => (
{isChecked && }
);
export const renderCheckBoxHeaderCell = (
onClick: (e: React.MouseEvent) => void,
checkState: number | null,
) => (
{checkState === 1 && }
{checkState === 2 && }
);
export const renderEmptyRows = (
rowCount: number,
columns: any,
tableWidth: number,
page: any,
prepareRow: any,
multiRowSelection = false,
) => {
const rows: string[] = new Array(rowCount).fill("");
if (page.length) {
const row = page[0];
return rows.map((item: string, index: number) => {
prepareRow(row);
const rowProps = {
...row.getRowProps(),
style: { display: "flex" },
};
return (
{multiRowSelection && renderCheckBoxCell(false)}
{row.cells.map((cell: any, cellIndex: number) => {
const cellProps = cell.getCellProps();
if (columns[0]?.columnProperties?.cellBackground) {
cellProps.style.background =
columns[0].columnProperties.cellBackground;
}
return
;
})}
);
});
} else {
const tableColumns = columns.length
? columns
: new Array(3).fill({ width: tableWidth / 3, isHidden: false });
return (
<>
{rows.map((row: string, index: number) => {
return (
{multiRowSelection && renderCheckBoxCell(false)}
{tableColumns.map((column: any, colIndex: number) => {
return (
);
})}
);
})}
>
);
}
};
const AscendingIcon = styled(ControlIcons.SORT_CONTROL as AnyStyledComponent)`
padding: 0;
position: relative;
top: 12px;
cursor: pointer;
transform: rotate(180deg);
&& svg {
path {
fill: ${(props) => props.theme.colors.secondary};
}
}
`;
const DescendingIcon = styled(ControlIcons.SORT_CONTROL as AnyStyledComponent)`
padding: 0;
position: relative;
top: 3px;
cursor: pointer;
&& svg {
path {
fill: ${(props) => props.theme.colors.secondary};
}
}
`;
export function TableHeaderCell(props: {
columnName: string;
columnIndex: number;
isHidden: boolean;
isAscOrder?: boolean;
sortTableColumn: (columnIndex: number, asc: boolean) => void;
isResizingColumn: boolean;
column: any;
}) {
const { column } = props;
const handleSortColumn = () => {
if (props.isResizingColumn) return;
let columnIndex = props.columnIndex;
if (props.isAscOrder === true) {
columnIndex = -1;
}
const sortOrder =
props.isAscOrder === undefined ? false : !props.isAscOrder;
props.sortTableColumn(columnIndex, sortOrder);
};
return (
{props.isAscOrder !== undefined ? (
{props.isAscOrder ? (
) : (
)}
) : null}
{props.columnName}
) => {
e.preventDefault();
e.stopPropagation();
}}
/>
);
}
export function getDefaultColumnProperties(
accessor: string,
index: number,
widgetName: string,
isDerived?: boolean,
): ColumnProperties {
const columnProps = {
index: index,
width: 150,
id: accessor,
horizontalAlignment: CellAlignmentTypes.LEFT,
verticalAlignment: VerticalAlignmentTypes.CENTER,
columnType: ColumnTypes.TEXT,
textColor: Colors.THUNDER,
textSize: TextSizes.PARAGRAPH,
fontStyle: FontStyleTypes.REGULAR,
enableFilter: true,
enableSort: true,
isVisible: true,
isDisabled: false,
isCellVisible: true,
isDerived: !!isDerived,
label: accessor,
computedValue: isDerived
? ""
: `{{${widgetName}.sanitizedTableData.map((currentRow) => ( currentRow.${accessor}))}}`,
};
return columnProps;
}
export function getTableStyles(props: TableStyles) {
return {
textColor: props.textColor,
textSize: props.textSize,
fontStyle: props.fontStyle,
cellBackground: props.cellBackground,
verticalAlignment: props.verticalAlignment,
horizontalAlignment: props.horizontalAlignment,
};
}
const SingleDropDown = Select.ofType
();
const StyledSingleDropDown = styled(SingleDropDown)`
div {
padding: 0 10px;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
span {
width: 100%;
height: 100%;
position: relative;
}
.${Classes.BUTTON} {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
box-shadow: none;
background: transparent;
min-height: 32px;
}
.${Classes.BUTTON_TEXT} {
text-overflow: ellipsis;
text-align: left;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
&& {
.${Classes.ICON} {
width: fit-content;
color: ${Colors.SLATE_GRAY};
}
}
`;
export const renderDropdown = (props: {
options: DropdownOption[];
isCellVisible: boolean;
onItemSelect: (onOptionChange: string, item: DropdownOption) => void;
onOptionChange: string;
selectedIndex?: number;
}) => {
const isOptionSelected = (selectedOption: DropdownOption) => {
const optionIndex = findIndex(props.options, (option) => {
return option.value === selectedOption.value;
});
return optionIndex === props.selectedIndex;
};
const renderSingleSelectItem = (
option: DropdownOption,
itemProps: IItemRendererProps,
) => {
if (!itemProps.modifiers.matchesPredicate) {
return null;
}
if (!props.isCellVisible) {
return null;
}
const isSelected: boolean = isOptionSelected(option);
return (
);
};
return (
) => {
e.stopPropagation();
}}
style={{ height: "100%" }}
>
{
props.onItemSelect(props.onOptionChange, item);
}}
popoverProps={{
minimal: true,
usePortal: true,
popoverClassName: "select-popover-wrapper",
}}
>
-1
? props.options[props.selectedIndex].label
: "-- Select --"
}
/>
);
};