@mui/material#Popover TypeScript Examples

The following examples show how to use @mui/material#Popover. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PlayBar.tsx    From rewind with MIT License 5 votes vote down vote up
function AudioButton() {
  const [anchorEl, setAnchorEl] = useState(null);
  const open = Boolean(anchorEl);
  const handlePopOverOpen = (event: any) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setAnchorEl(null);
  };
  const { volume, muted } = useAudioSettings();
  const service = useAudioSettingsService();

  const handleClick = () => {
    service.toggleMuted();
  };
  return (
    <>
      <IconButton onClick={handlePopOverOpen}>{muted ? <VolumeOff /> : <VolumeUp />}</IconButton>
      <Popover
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
      >
        <Box width={256}>
          <BaseAudioSettingsPanel
            master={volume.master}
            music={volume.music}
            /* Currently it's disabled */
            effects={0}
            onMutedChange={(x) => service.setMuted(x)}
            onMasterChange={(x) => service.setMasterVolume(x)}
            onMusicChange={(x) => service.setMusicVolume(x)}
            onEffectsChange={(x) => service.setEffectsVolume(x)}
          />
        </Box>
      </Popover>
    </>
  );
}
Example #2
Source File: EntityCollectionView.tsx    From firecms with MIT License 4 votes vote down vote up
/**
 * This component is in charge of binding a datasource path with an {@link EntityCollection}
 * where it's configuration is defined. It includes an infinite scrolling table,
 * 'Add' new entities button,
 *
 * This component is the default one used for displaying entity collections
 * and is in charge of generating all the specific actions and customization
 * of the lower level {@link CollectionTable}
 *
 * Please **note** that you only need to use this component if you are building
 * a custom view. If you just need to create a default view you can do it
 * exclusively with config options.
 *
 * If you need a lower level implementation with more granular options, you
 * can use {@link CollectionTable}.
 *
 * If you need a table that is not bound to the datasource or entities and
 * properties at all, you can check {@link Table}
 *
 * @param path
 * @param collection
 * @constructor
 * @category Components
 */
export function EntityCollectionView<M extends { [Key: string]: any }>({
                                                                           path,
                                                                           collection: baseCollection
                                                                       }: EntityCollectionViewProps<M>
) {

    const sideEntityController = useSideEntityController();
    const context = useFireCMSContext();
    const authController = useAuthController();
    const navigationContext = useNavigation();

    const theme = useTheme();
    const largeLayout = useMediaQuery(theme.breakpoints.up("md"));

    const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
    const collectionResolver = navigationContext.getCollectionResolver<M>(path);
    if (!collectionResolver) {
        throw Error(`Couldn't find the corresponding collection view for the path: ${path}`);
    }

    const onCollectionModifiedForUser = useCallback((partialCollection: PartialEntityCollection<any>) => {
        navigationContext.onCollectionModifiedForUser(path, partialCollection);
    }, [path]);

    const collection: EntityCollection<M> = collectionResolver ?? baseCollection;

    const { schemaResolver } = collectionResolver;

    const exportable = collection.exportable === undefined || collection.exportable;

    const selectionEnabled = collection.selectionEnabled === undefined || collection.selectionEnabled;
    const hoverRow = collection.inlineEditing !== undefined && !collection.inlineEditing;

    const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);

    const selectionController = useSelectionController<M>();
    const usedSelectionController = collection.selectionController ?? selectionController;
    const {
        selectedEntities,
        toggleEntitySelection,
        isEntitySelected,
        setSelectedEntities
    } = usedSelectionController;

    useEffect(() => {
        setDeleteEntityClicked(undefined);
    }, [selectedEntities]);

    const onEntityClick = useCallback((entity: Entity<M>) => {
        return sideEntityController.open({
            entityId: entity.id,
            path,
            permissions: collection.permissions,
            schema: collection.schema,
            subcollections: collection.subcollections,
            callbacks: collection.callbacks,
            overrideSchemaRegistry: false
        });
    }, [path, collection, sideEntityController]);

    const onNewClick = useCallback((e: React.MouseEvent) => {
        e.stopPropagation();
        return sideEntityController.open({
            path,
            permissions: collection.permissions,
            schema: collection.schema,
            subcollections: collection.subcollections,
            callbacks: collection.callbacks,
            overrideSchemaRegistry: false
        });
    }, [path, collection, sideEntityController]);

    const internalOnEntityDelete = useCallback((_path: string, entity: Entity<M>) => {
        setSelectedEntities(selectedEntities.filter((e) => e.id !== entity.id));
    }, [selectedEntities, setSelectedEntities]);

    const internalOnMultipleEntitiesDelete = useCallback((_path: string, entities: Entity<M>[]) => {
        setSelectedEntities([]);
        setDeleteEntityClicked(undefined);
    }, [setSelectedEntities]);

    const checkInlineEditing = useCallback((entity: Entity<any>) => {
        if (!canEdit(collection.permissions, entity, authController, path, context)) {
            return false;
        }
        return collection.inlineEditing === undefined || collection.inlineEditing;
    }, [collection.inlineEditing, collection.permissions, path]);

    const onColumnResize = useCallback(({
                                            width,
                                            key
                                        }: OnColumnResizeParams) => {
        // Only for property columns
        if (!collection.schema.properties[key]) return;
        const property: Partial<AnyProperty> = { columnWidth: width };
        const updatedFields: PartialEntityCollection<any> = { schema: { properties: { [key as keyof M]: property } } };
        if (onCollectionModifiedForUser)
            onCollectionModifiedForUser(updatedFields)
    }, [collection.schema.properties, onCollectionModifiedForUser]);

    const onSizeChanged = useCallback((size: CollectionSize) => {
        if (onCollectionModifiedForUser)
            onCollectionModifiedForUser({ defaultSize: size })
    }, [onCollectionModifiedForUser]);

    const open = anchorEl != null;
    const title = useMemo(() => (
        <div style={{
            padding: "4px"
        }}>

            <Typography
                variant="h6"
                style={{
                    lineHeight: "1.0",
                    textOverflow: "ellipsis",
                    whiteSpace: "nowrap",
                    overflow: "hidden",
                    maxWidth: "160px",
                    cursor: collection.description ? "pointer" : "inherit"
                }}
                onClick={collection.description
                    ? (e) => {
                        setAnchorEl(e.currentTarget);
                        e.stopPropagation();
                    }
                    : undefined}
            >
                {`${collection.name}`}
            </Typography>
            <Typography
                style={{
                    display: "block",
                    textOverflow: "ellipsis",
                    whiteSpace: "nowrap",
                    overflow: "hidden",
                    maxWidth: "160px",
                    direction: "rtl",
                    textAlign: "left"
                }}
                variant={"caption"}
                color={"textSecondary"}>
                {`/${path}`}
            </Typography>

            {collection.description &&
            <Popover
                id={"info-dialog"}
                open={open}
                anchorEl={anchorEl}
                elevation={1}
                onClose={() => {
                    setAnchorEl(null);
                }}
                anchorOrigin={{
                    vertical: "bottom",
                    horizontal: "center"
                }}
                transformOrigin={{
                    vertical: "top",
                    horizontal: "center"
                }}
            >

                <Box m={2}>
                    <Markdown source={collection.description}/>
                </Box>

            </Popover>
            }

        </div>
    ), [collection.description, collection.name, path, open, anchorEl]);

    const tableRowActionsBuilder = useCallback(({
                                                    entity,
                                                    size
                                                }: { entity: Entity<any>, size: CollectionSize }) => {

        const isSelected = isEntitySelected(entity);

        const createEnabled = canCreate(collection.permissions, authController, path, context);
        const editEnabled = canEdit(collection.permissions, entity, authController, path, context);
        const deleteEnabled = canDelete(collection.permissions, entity, authController, path, context);

        const onCopyClicked = (clickedEntity: Entity<M>) => sideEntityController.open({
            entityId: clickedEntity.id,
            path,
            copy: true,
            permissions: {
                edit: editEnabled,
                create: createEnabled,
                delete: deleteEnabled
            },
            schema: collection.schema,
            subcollections: collection.subcollections,
            callbacks: collection.callbacks,
            overrideSchemaRegistry: false
        });

        const onEditClicked = (clickedEntity: Entity<M>) => sideEntityController.open({
            entityId: clickedEntity.id,
            path,
            permissions: {
                edit: editEnabled,
                create: createEnabled,
                delete: deleteEnabled
            },
            schema: collection.schema,
            subcollections: collection.subcollections,
            callbacks: collection.callbacks,
            overrideSchemaRegistry: false
        });

        return (
            <CollectionRowActions
                entity={entity}
                isSelected={isSelected}
                selectionEnabled={selectionEnabled}
                size={size}
                toggleEntitySelection={toggleEntitySelection}
                onEditClicked={onEditClicked}
                onCopyClicked={createEnabled ? onCopyClicked : undefined}
                onDeleteClicked={deleteEnabled ? setDeleteEntityClicked : undefined}
            />
        );

    }, [usedSelectionController, sideEntityController, collection.permissions, authController, path]);

    const toolbarActionsBuilder = useCallback((_: { size: CollectionSize, data: Entity<any>[] }) => {

        const addButton = canCreate(collection.permissions, authController, path, context) && onNewClick && (largeLayout
            ? <Button
                onClick={onNewClick}
                startIcon={<Add/>}
                size="large"
                variant="contained"
                color="primary">
                Add {collection.schema.name}
            </Button>
            : <Button
                onClick={onNewClick}
                size="medium"
                variant="contained"
                color="primary"
            >
                <Add/>
            </Button>);

        const multipleDeleteEnabled = selectedEntities.every((entity) => canDelete(collection.permissions, entity, authController, path, context));
        const onMultipleDeleteClick = (event: React.MouseEvent) => {
            event.stopPropagation();
            setDeleteEntityClicked(selectedEntities);
        };
        const multipleDeleteButton = selectionEnabled &&

            <Tooltip
                title={multipleDeleteEnabled ? "Multiple delete" : "You have selected one entity you cannot delete"}>
                <span>
                    {largeLayout && <Button
                        disabled={!(selectedEntities?.length) || !multipleDeleteEnabled}
                        startIcon={<Delete/>}
                        onClick={onMultipleDeleteClick}
                        color={"primary"}
                    >
                        <p style={{ minWidth: 24 }}>({selectedEntities?.length})</p>
                    </Button>}

                    {!largeLayout &&
                    <IconButton
                        color={"primary"}
                        disabled={!(selectedEntities?.length) || !multipleDeleteEnabled}
                        onClick={onMultipleDeleteClick}
                        size="large">
                        <Delete/>
                    </IconButton>}
                </span>
            </Tooltip>;

        const extraActions = collection.extraActions
            ? collection.extraActions({
                path,
                collection,
                selectionController: usedSelectionController,
                context
            })
            : undefined;

        const exportButton = exportable &&
            <ExportButton schema={collection.schema}
                          schemaResolver={schemaResolver}
                          exportConfig={typeof collection.exportable === "object" ? collection.exportable : undefined}
                          path={path}/>;

        return (
            <>
                {extraActions}
                {multipleDeleteButton}
                {exportButton}
                {addButton}
            </>
        );
    }, [usedSelectionController, path, collection, largeLayout]);

    return (
        <>

            <CollectionTable
                key={`collection_table_${path}`}
                title={title}
                path={path}
                collection={collection}
                schemaResolver={schemaResolver}
                onSizeChanged={onSizeChanged}
                inlineEditing={checkInlineEditing}
                onEntityClick={onEntityClick}
                onColumnResize={onColumnResize}
                tableRowActionsBuilder={tableRowActionsBuilder}
                toolbarActionsBuilder={toolbarActionsBuilder}
                hoverRow={hoverRow}
            />

            {deleteEntityClicked && <DeleteEntityDialog entityOrEntitiesToDelete={deleteEntityClicked}
                                 path={path}
                                 schema={collection.schema}
                                 schemaResolver={schemaResolver}
                                 callbacks={collection.callbacks}
                                 open={!!deleteEntityClicked}
                                 onEntityDelete={internalOnEntityDelete}
                                 onMultipleEntitiesDelete={internalOnMultipleEntitiesDelete}
                                 onClose={() => setDeleteEntityClicked(undefined)}/>}
        </>
    );
}
Example #3
Source File: TableHeader.tsx    From firecms with MIT License 4 votes vote down vote up
function TableHeaderInternal<M extends { [Key: string]: any }>({
                                                                   sort,
                                                                   onColumnSort,
                                                                   onFilterUpdate,
                                                                   filter,
                                                                   column
                                                               }: TableHeaderProps<M>) {

    const [onHover, setOnHover] = useState(false);
    const ref = useRef<HTMLDivElement>(null);

    const classes = useStyles({ onHover, align: column.align });

    const [open, setOpen] = React.useState(false);

    const handleSettingsClick = useCallback((event: any) => {
        setOpen(true);
    }, []);

    const handleClose = useCallback(() => {
        setOpen(false);
    }, []);

    const update = useCallback((filterForProperty?: [TableWhereFilterOp, any]) => {
        onFilterUpdate(filterForProperty);
        setOpen(false);
    }, []);

    return (
        <ErrorBoundary>
            <Grid
                className={classes.header}
                ref={ref}
                wrap={"nowrap"}
                alignItems={"center"}
                onMouseEnter={() => setOnHover(true)}
                onMouseMove={() => setOnHover(true)}
                onMouseLeave={() => setOnHover(false)}
                container>

                <Grid item xs={true} className={classes.headerTitle}>
                    <div className={classes.headerInternal}>
                        <div className={classes.headerIcon}>
                            {column.icon && column.icon(onHover || open)}
                        </div>
                        <div className={classes.headerTitleInternal}>
                            {column.label}
                        </div>
                    </div>
                </Grid>

                {column.sortable && (sort || onHover || open) &&
                <Grid item>
                    <Badge color="secondary"
                           variant="dot"
                           overlap="circular"
                           invisible={!sort}>
                        <IconButton
                            size={"small"}
                            className={classes.headerIconButton}
                            onClick={() => {
                                onColumnSort(column.key as Extract<keyof M, string>);
                            }}
                        >
                            {!sort && <ArrowUpwardIcon fontSize={"small"}/>}
                            {sort === "asc" &&
                            <ArrowUpwardIcon fontSize={"small"}/>}
                            {sort === "desc" &&
                            <ArrowDownwardIcon fontSize={"small"}/>}
                        </IconButton>
                    </Badge>
                </Grid>
                }

                {column.filter && <Grid item>
                    <Badge color="secondary"
                           variant="dot"
                           overlap="circular"
                           invisible={!filter}>
                        <IconButton
                            className={classes.headerIconButton}
                            size={"small"}
                            onClick={handleSettingsClick}>
                            <ArrowDropDownCircleIcon fontSize={"small"}
                                                     color={onHover || open ? undefined : "disabled"}/>
                        </IconButton>

                    </Badge>
                </Grid>}
            </Grid>

            {column.sortable && <Popover
                id={open ? `popover_${column.key}` : undefined}
                open={open}
                elevation={2}
                anchorEl={ref.current}
                onClose={handleClose}
                anchorOrigin={{
                    vertical: "bottom",
                    horizontal: "right"
                }}
                transformOrigin={{
                    vertical: "top",
                    horizontal: "right"
                }}
            >
                <FilterForm column={column}
                            filter={filter}
                            onFilterUpdate={update}/>
            </Popover>}

        </ErrorBoundary>
    );
}
Example #4
Source File: ColorPicker.tsx    From Cromwell with MIT License 4 votes vote down vote up
export function ColorPicker(props: {
    label?: string;
    value?: string;
    className?: string;
    style?: React.CSSProperties;
    onChange?: (color: string) => void;
}) {
    const colorRef = useRef<string | null>(null);
    const prevValue = useRef<string | null>(null);
    const inputAnchorRef = useRef<HTMLDivElement | null>(null);
    const [open, setOpen] = useState(false);
    const forceUpdate = useForceUpdate();

    if (props.value !== prevValue.current) {
        prevValue.current = props.value;
        colorRef.current = props.value;
    }

    const handleChange = (color: { hex: string; rgb: { r: number; g: number; b: number; a: number } }) => {
        const colorStr = color.rgb.a === 1 ? color.hex : `rgba(${color.rgb.r}, ${color.rgb.g}, ${color.rgb.b}, ${color.rgb.a})`;
        colorRef.current = colorStr;
        forceUpdate();
    }

    const handleClose = () => {
        handleApply();
        setOpen(false);
    }

    const handleApply = () => {
        props.onChange?.(colorRef.current);
    }

    const handleInputChange = (event) => {
        colorRef.current = event.target.value;
        forceUpdate();
        handleApply();
    }

    return (
        <>
            <TextField
                InputProps={{
                    startAdornment: (
                        <InputAdornment position="start">
                            <div style={{ backgroundColor: colorRef.current, width: '20px', height: '20px', borderRadius: '100%' }}></div>
                        </InputAdornment>
                    ),
                }}
                variant="standard"
                className={props.className}
                label={props.label}
                fullWidth
                value={colorRef.current}
                ref={inputAnchorRef}
                onChange={handleInputChange}
                onClick={() => setOpen(true)}
                style={props.style}
            />
            <Popover
                open={open}
                anchorEl={inputAnchorRef.current}
                onClose={handleClose}
                anchorOrigin={{
                    vertical: 'bottom',
                    horizontal: 'left',
                }}
                transformOrigin={{
                    vertical: 'top',
                    horizontal: 'left',
                }}
            >
                <div>
                    <SketchPicker
                        color={colorRef.current ?? '#000'}
                        onChangeComplete={handleChange}
                    />
                </div>
            </Popover>
        </>
    )
}
Example #5
Source File: EntityTable.tsx    From Cromwell with MIT License 4 votes vote down vote up
render() {
        const tableColumns = this.getColumns();
        return (
            <div className={styles.EntityTable}>
                <div className={styles.header}>
                    <div style={{ display: 'flex', alignItems: 'center' }}>
                        <h1 className={styles.pageTitle}>{this.props.listLabel}</h1>
                        {this.props.customElements?.listLeftActions}
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center' }}>
                        {this.props.customElements?.listRightActions}
                        {!!(this.filters?.length || this.sortBy?.column
                            || this.props.isFilterActive?.()) && (
                                <Tooltip title="Clear filters">
                                    <IconButton className={styles.iconButton}
                                        onClick={this.clearAllFilters}>
                                        <ClearAllIcon />
                                    </IconButton>
                                </Tooltip>
                            )}
                        <DeleteSelectedButton
                            style={{ marginRight: '10px' }}
                            onClick={this.handleDeleteSelected}
                            totalElements={this.totalElements}
                        />
                        {this.props.entityBaseRoute && !this.props.hideAddNew && (
                            <Button variant="contained"
                                size="small"
                                onClick={this.handleCreate}
                            >Add new</Button>
                        )}
                    </div>
                </div>
                <div className={styles.tableHeader}>
                    <div className={commonStyles.center}>
                        <Tooltip title="Select all">
                            <Checkbox
                                checked={this.props.allSelected ?? false}
                                onChange={this.handleToggleSelectAll}
                            />
                        </Tooltip>
                    </div>
                    <div className={styles.tableColumnNames}>
                        {tableColumns.map(col => {
                            if (!col.visible) return null;
                            const columnFilter = this.filters?.find(filter => filter.key === col.name);
                            let searchQuery = columnFilter?.value !== null && columnFilter?.value !== undefined &&
                                columnFilter.value !== '' ? columnFilter.value : null;

                            if (col.searchOptions) {
                                const autocompleteVal = this.getAutocompleteValueFromSearch(searchQuery, col);
                                if (Array.isArray(autocompleteVal)) {
                                    searchQuery = autocompleteVal.map(val => val.label).join(', ');
                                } else if (autocompleteVal) {
                                    searchQuery = autocompleteVal.label;
                                }
                            }

                            return (
                                <div key={col.name}
                                    id={`column_${col.name}`}
                                    className={clsx(styles.columnName)}
                                    style={this.getColumnStyles(col, tableColumns)}
                                >
                                    <div style={{ display: 'flex', alignItems: 'center' }}>
                                        <Tooltip title="Open search" placement="top" enterDelay={500}>
                                            <p onClick={() => this.openColumnSearch(col)}
                                                className={clsx(styles.ellipsis, styles.columnNameText)}>{col.label}</p>
                                        </Tooltip>
                                        {!col.disableSort && (
                                            <Tooltip title="Toggle sort" placement="top" enterDelay={500}>
                                                <div onClick={() => this.toggleOrderBy(col)}
                                                    className={styles.orderButton}>
                                                    <ArrowDropDownIcon
                                                        style={{
                                                            transform: this.sortBy?.column?.name === col.name
                                                                && this.sortBy?.sort === 'ASC' ? 'rotate(180deg)' : undefined,
                                                            transition: '0.3s',
                                                            color: this.sortBy?.column?.name !== col.name ? '#aaa' : '#9747d3',
                                                            fill: this.sortBy?.column?.name !== col.name ? '#aaa' : '#9747d3',
                                                            fontSize: this.sortBy?.column?.name === col.name ? '26px' : undefined,
                                                        }}
                                                    />
                                                </div>
                                            </Tooltip>
                                        )}
                                    </div>
                                    {searchQuery && (
                                        <div style={{ display: 'flex', alignItems: 'center' }}>
                                            <p className={clsx(styles.searchQuery, styles.ellipsis)}>{searchQuery}</p>
                                            <Tooltip title="Clear search" enterDelay={500}>
                                                <div onClick={() => this.clearColumnSearch(col)}
                                                    style={{ cursor: 'pointer' }}>
                                                    <CloseIcon style={{
                                                        fontSize: '14px',
                                                        color: '#555'
                                                    }} />
                                                </div>
                                            </Tooltip>
                                        </div>
                                    )}
                                </div>
                            )
                        })}
                    </div>
                    <div className={clsx(commonStyles.center, styles.headerSettings)}>
                        <Tooltip title="Configure columns">
                            <IconButton
                                onClick={this.toggleConfigureColumns}
                                ref={this.configureColumnsButtonRef}
                                disabled={!tableColumns?.length}
                            >
                                <SettingsIcon />
                            </IconButton>
                        </Tooltip>
                        <Popover
                            open={!!this.state?.configureColumnsOpen}
                            anchorEl={this.configureColumnsButtonRef.current}
                            onClose={this.toggleConfigureColumns}
                            classes={{ paper: styles.popoverPaper }}
                            anchorOrigin={{
                                vertical: 'bottom',
                                horizontal: 'right',
                            }}
                            transformOrigin={{
                                vertical: 'top',
                                horizontal: 'right',
                            }}
                            elevation={0}
                        >
                            <div className={styles.columnsConfigure}>
                                <Button size="small" variant="outlined"
                                    onClick={this.resetConfiguredColumns}
                                    style={{ margin: '10px 0 0 10px' }}
                                >Reset</Button>
                                <DraggableList<TColumnConfigureItemData>
                                    data={tableColumns.map(col => ({
                                        id: col.name,
                                        column: col,
                                        sortedColumns: this.sortedColumns,
                                    }))}
                                    onChange={this.changeColumnsOrder}
                                    component={ColumnConfigureItem}
                                />
                            </div>
                        </Popover>
                        <Popover
                            open={!!this.state?.columnSearch}
                            anchorEl={() => document.getElementById(`column_${this.state?.columnSearch?.name}`)}
                            onClose={this.closeColumnSearch}
                            classes={{ paper: styles.popoverPaper }}
                            anchorOrigin={{
                                vertical: 'bottom',
                                horizontal: 'left',
                            }}
                            transformOrigin={{
                                vertical: 'top',
                                horizontal: 'left',
                            }}
                            elevation={0}
                        >
                            <div className={styles.columnsConfigure}
                                style={{ padding: '10px 15px' }}>
                                {this.state?.columnSearch?.searchOptions ? (
                                    <Autocomplete
                                        multiple={this.state.columnSearch.multipleOptions}
                                        options={this.state.columnSearch.searchOptions}
                                        getOptionLabel={(option: any) => option?.label ?? ''}
                                        defaultValue={this.getAutocompleteValueFromSearch(this.currentSearch,
                                            this.state.columnSearch)}
                                        className={styles.filterItem}
                                        onChange={(event, newVal) => {
                                            if (Array.isArray(newVal)) newVal = JSON.stringify(newVal.map(val => typeof val === 'object' ? val?.value : val));
                                            this.currentSearch = typeof newVal === 'object' ? newVal?.value : newVal
                                        }}
                                        classes={{ popper: styles.autocompletePopper }}
                                        renderInput={(params) => <TextField
                                            {...params}
                                            variant="standard"
                                            fullWidth
                                            label={`Search ${this.state?.columnSearch?.label ?? ''}`}
                                        />}
                                    />
                                ) : (
                                    <TextField
                                        fullWidth
                                        onChange={(event) => this.currentSearch = event.target.value}
                                        variant="standard"
                                        label={`Search ${this.state?.columnSearch?.label ?? ''}`}
                                        defaultValue={this.currentSearch}
                                    />
                                )}
                            </div>
                        </Popover>
                    </div>
                </div>
                <CList<TEntityType, TListItemProps<TEntityType, TFilterType>>
                    className={styles.listWrapper}
                    id={this.listId}
                    ListItem={EntityTableItem}
                    useAutoLoading
                    usePagination
                    listItemProps={{
                        handleDeleteBtnClick: this.handleDeleteItem,
                        toggleSelection: this.handleToggleItemSelection,
                        tableProps: this.props,
                        getColumns: this.getColumns,
                        getColumnStyles: this.getColumnStyles,
                    }}
                    useQueryPagination
                    loader={this.getManyFilteredItems}
                    cssClasses={{
                        scrollBox: styles.list,
                        contentWrapper: styles.listContent,
                    }}
                    elements={{
                        pagination: Pagination,
                        preloader: listPreloader
                    }}
                />
            </div >
        )
    }
Example #6
Source File: GalleryPicker.tsx    From Cromwell with MIT License 4 votes vote down vote up
render() {
        const images = (this.props.images ?? this.uncontrolledInput ?? []).map((image, index) => {
            if (!image.id) image.id = image.src + index;
            return image;
        });

        return (
            <div className={clsx(styles.GalleryPicker, this.props.className)} style={this.props.style}>
                {this.props.label && (
                    <p className={styles.label}>{this.props.label}</p>
                )}
                <ResponsiveGridLayout
                    margin={[0, 0]}
                    isResizable={false}
                    breakpoints={{ xs: 480, xxs: 0 }}
                    rowHeight={64}
                    layouts={this.getGridLayout(images)}
                    onLayoutChange={this.onLayoutChange(images)}
                    cols={{ xs: 1, xxs: 1 }}
                    draggableHandle='.draggableHandle'
                >
                    {images.map((image, index) => {
                        return (<div
                            key={(image?.id ?? index) + ''}
                            className={styles.imageItem}
                        >
                            <IconButton style={{ cursor: 'move', marginRight: '10px' }}
                                className="draggableHandle">
                                <DragIndicatorIcon />
                            </IconButton>
                            <ImageItem
                                draggableHandleClass="draggableHandle"
                                key={(image?.id ?? index) + ''}
                                data={image}
                                hideSrc={this.props.hideSrc}
                                itemProps={{
                                    onImageChange: this.onImageChange,
                                    classes: this.props.classes?.imagePicker,
                                    allImages: images,
                                }}
                            />
                            {this.props.editLink && (
                                <IconButton style={{ cursor: 'pointer', marginLeft: '10px' }}
                                    onClick={(event) => this.handleShowLink(index, event)}
                                >
                                    <LinkIcon />
                                </IconButton>
                            )}
                            <IconButton style={{ cursor: 'pointer', marginLeft: '10px' }}
                                onClick={() => this.handleRemoveImage(index)}
                            >
                                <DeleteOutlineIcon />
                            </IconButton>
                            {this.props.editLink && this.state?.editableLink === index && (
                                <Popover
                                    elevation={0}
                                    classes={{ paper: styles.popover }}
                                    open={this.state?.editableLink === index}
                                    anchorEl={this.editableLinkRef}
                                    onClose={this.handleCloseLinkEdit}
                                    anchorOrigin={{
                                        vertical: 'bottom',
                                        horizontal: 'left',
                                    }}
                                    transformOrigin={{
                                        vertical: 'top',
                                        horizontal: 'left',
                                    }}
                                >
                                    <TextField
                                        fullWidth
                                        onChange={(e) => this.handleChangeImageLink(e.target.value)}
                                        value={this.state?.editableLinkText ?? ''}
                                        label="Link"
                                        variant="standard"
                                    />
                                </Popover>
                            )}
                        </div>)
                    })}
                </ResponsiveGridLayout>
                <div className={styles.actions}>
                    <Tooltip title="Add image">
                        <IconButton
                            className={styles.galleryAddImageBtn}
                            aria-label="add image"
                            onClick={this.handleAddImage}
                        >
                            <AddIcon />
                        </IconButton>
                    </Tooltip>
                    <Tooltip title="Clear all images">
                        <IconButton
                            className={styles.galleryAddImageBtn}
                            aria-label="clear image"
                            onClick={this.handleDeleteAllImages}
                        >
                            <DeleteForeverIcon />
                        </IconButton>
                    </Tooltip>
                </div>
            </div>
        );
    }
Example #7
Source File: NotificationCenter.tsx    From Cromwell with MIT License 4 votes vote down vote up
function NotificationCenter(props: TPropsType) {
    const [open, setOpen] = useState(false);
    const popperAnchorEl = useRef<HTMLDivElement | null>(null);
    const client = getRestApiClient();

    let NotificationIcon = NotificationsNoneIcon;
    let tipText = '';
    if (props?.status?.updateAvailable) {
        NotificationIcon = NotificationImportantIcon;
        tipText = 'Update available';
    }

    const updateInfo = props.status?.updateInfo;
    const notifications = props.status?.notifications;

    const handleOpen = () => {
        if (!notifications?.length && !updateInfo) return;
        setOpen(true)
    }

    const handleStartUpdate = async () => {
        store.setStateProp({
            prop: 'status',
            payload: {
                ...store.getState().status,
                isUpdating: true,
            }
        });

        let success = false;
        try {
            success = await client.launchCmsUpdate();
        } catch (error) {
            console.error(error);
        }
        await updateStatus();

        if (success) {
            toast.success('CMS updated');
            const confirm = await askConfirmation({
                title: `CMS has been updated. Please reload this page to apply changes`,
            });
            if (confirm) {
                window.location.reload();
            }
        }
        else toast.error('Failed to update CMS');

    }


    return (
        <div ref={popperAnchorEl}>
            <Tooltip title={tipText}>
                <IconButton
                    onClick={handleOpen}
                    style={{
                        cursor: notifications?.length ? 'pointer' : 'initial',
                        opacity: notifications?.length ? '1' : '0.6',
                    }}
                >
                    <NotificationIcon htmlColor={props.color} />
                </IconButton>
            </Tooltip>
            <Popover open={open} anchorEl={popperAnchorEl.current}
                style={{ zIndex: 9999 }}
                onClose={() => setOpen(false)}
                anchorOrigin={{
                    vertical: 'bottom',
                    horizontal: 'right',
                }}
                transformOrigin={{
                    vertical: 'top',
                    horizontal: 'right',
                }}
            >
                <Grid container className={styles.list}>
                    {props.status?.isUpdating && (
                        <Grid item container xs={12} className={clsx(styles.update, styles.updating)}>
                            <h3 className={styles.updateTitle}>
                                <UpdateIcon style={{ marginRight: '7px' }} />
                                Update in progress...</h3>
                            <LinearProgress className={styles.updateProgress} />
                        </Grid>
                    )}
                    {props.status?.updateAvailable && updateInfo && !props.status?.isUpdating && (
                        <UpdateInfoCard
                            updateInfo={updateInfo}
                            currentVersion={props.status?.currentVersion}
                            onStartUpdate={handleStartUpdate}
                        />
                    )}
                    {notifications && (
                        notifications.map((note, index) => {
                            let severity: AlertProps['severity'] = 'info';
                            if (note.type === 'warning') severity = 'warning';
                            if (note.type === 'error') severity = 'error';
                            return (
                                <Grid key={index} item container xs={12} className={styles.item}>
                                    <Alert severity={severity} className={styles.alert}
                                        classes={{ message: styles.message }}>
                                        <p>{note.message}</p>
                                        {note.documentationLink && (
                                            <Tooltip title="Documentation">
                                                <IconButton
                                                    onClick={() => window.open(note.documentationLink, '_blank')}>
                                                    <HelpOutlineIcon />
                                                </IconButton>
                                            </Tooltip>
                                        )}
                                    </Alert>
                                </Grid>
                            )
                        })
                    )}
                </Grid>
            </Popover>
        </div>
    )
}
Example #8
Source File: Sidebar.tsx    From Cromwell with MIT License 4 votes vote down vote up
export default function Sidebar() {
    const pageInfos = getPageInfos();
    const currentInfo = pageInfos.find(i => i.route === window.location.pathname.replace('/admin', ''));
    const currentLink = getLinkByInfo(currentInfo);
    const [expanded, setExpanded] = useState<string | false>(currentLink?.parentId ?? false);
    const [optionsOpen, setOptionsOpen] = useState<boolean>(false);
    const [mobileOpen, setMobileOpen] = useState(false);
    const [cmsInfoOpen, setCmsInfoOpen] = useState(false);
    const [systemMonitorOpen, setSystemMonitorOpen] = useState(false);
    const popperAnchorEl = useRef<HTMLDivElement | null>(null);
    const history = useHistory?.();
    const forceUpdate = useForceUpdate();

    const userInfo: TUser | undefined = getStoreItem('userInfo');
    const toggleSubMenu = (panel: string) => (event: React.ChangeEvent<any>, isExpanded: boolean) => {
        setExpanded(isExpanded ? panel : false);
    };
    const handleCloseMenu = () => {
        setMobileOpen(false);
    }
    const handleOpenMenu = () => {
        setMobileOpen(true);
    }

    useEffect(() => {
        onStoreChange('userInfo', () => {
            setTimeout(forceUpdate, 100);
        });
        history?.listen(() => {
            const currentInfo = pageInfos.find(i => i.route === window.location.pathname.replace('/admin', ''));
            const newCurrentLink = getLinkByInfo(currentInfo);
            if (newCurrentLink && newCurrentLink !== currentLink) {
                // setActiveId(newCurrentLink.id);
                if (newCurrentLink.parentId) setExpanded(newCurrentLink.parentId)
            }
            setTimeout(forceUpdate, 100);
        });

        store.setStateProp({
            prop: 'forceUpdateSidebar',
            payload: forceUpdate,
        });
    }, []);

    const handleLogout = async () => {
        setOptionsOpen(false);
        await getRestApiClient()?.logOut();
        forceUpdate();
        history?.push(loginPageInfo.route);
    }

    const handleOptionsToggle = () => {
        setOptionsOpen(!optionsOpen);
    }

    const openFileManager = () => {
        getFileManager()?.open();
    }

    const openCmsInfo = () => {
        setCmsInfoOpen(true);
    }

    const openDocs = () => {
        window.open('https://cromwellcms.com/docs/overview/intro', '_blank')
    }

    // check for disabled sidebar
    if (currentInfo?.disableSidebar) return <></>;

    const sidebarContent = (
        <div className={styles.sidebarContent}>
            <div className={styles.sidebarTop}>
                <div className={styles.sidebarHeader}>
                    <div className={styles.logo}
                        style={{ backgroundImage: `url("/admin/static/logo_small_white.svg")` }}
                    ></div>
                    {/* <p className={commonStyles.text} style={{ color: '#fff', opacity: 0.7 }}>Admin Panel</p> */}
                    <div>
                        <NotificationCenter color="#fff" />
                    </div>
                    <div className={styles.sidebarMobileActions}>
                        <IconButton onClick={handleCloseMenu} >
                            <CloseIcon htmlColor="#999" />
                        </IconButton>
                    </div>
                </div>
                {getSideBarLinks().map(link => <SidebarLink data={link}
                    key={link.id}
                    toggleSubMenu={toggleSubMenu}
                    expanded={expanded}
                    forceUpdate={forceUpdate}
                    activeId={currentLink?.id}
                    userInfo={userInfo}
                />)}
            </div>
            <div className={styles.sidebarBottom}>
                <div className={styles.bottomBlock} style={{ overflow: 'hidden' }}>
                    {(userInfo?.avatar && userInfo?.avatar !== '') ? (
                        <div className={styles.avatar} style={{ backgroundImage: `url(${userInfo.avatar})` }}></div>
                    ) : <AccountCircleIcon className={styles.avatar} />}
                    <div className={styles.textBlock}>
                        <p className={styles.nameText}>{userInfo?.fullName ?? ''}</p>
                        <p className={styles.emailText}>{userInfo?.email ?? ''}</p>
                    </div>
                </div>
                <div className={styles.bottomBlock}
                    style={{ marginRight: '-10px' }}
                    ref={popperAnchorEl}>
                    <Tooltip title="Options">
                        <IconButton
                            style={{ marginLeft: '-10px' }}
                            onClick={handleOptionsToggle}
                            className={styles.actionBtn}
                            aria-label="Options"
                        >
                            <MoreVertOutlinedIcon />
                        </IconButton>
                    </Tooltip>
                    <Popover open={optionsOpen} anchorEl={popperAnchorEl.current}
                        style={{ zIndex: 9999 }}
                        onClose={() => setOptionsOpen(false)}
                        anchorOrigin={{
                            vertical: 'top',
                            horizontal: 'right',
                        }}
                        transformOrigin={{
                            vertical: 'bottom',
                            horizontal: 'right',
                        }}
                    >
                        <div onClick={() => setOptionsOpen(false)}>
                            <Link to={`${userPageInfo.baseRoute}/${userInfo?.id}`}>
                                <MenuItem className={styles.optionsItem}>
                                    <AccountCircleOutlinedIcon />
                                    <p>Your profile</p>
                                </MenuItem>
                            </Link>
                            <MenuItem className={styles.optionsItem} onClick={openFileManager}>
                                <PermMediaOutlinedIcon />
                                <p>Media</p>
                            </MenuItem>
                            <MenuItem className={styles.optionsItem} onClick={() => setSystemMonitorOpen(true)}>
                                <DnsRoundedIcon />
                                <p>System monitor</p>
                            </MenuItem>
                            <MenuItem className={styles.optionsItem} onClick={openCmsInfo}>
                                <InfoOutlinedIcon />
                                <p>CMS specs</p>
                            </MenuItem>
                            <MenuItem className={styles.optionsItem} onClick={openDocs}>
                                <HelpOutlineIcon />
                                <p>Documentation</p>
                            </MenuItem>
                            <MenuItem onClick={handleLogout} className={styles.optionsItem}>
                                <ExitToAppIcon />
                                <p>Log out</p>
                            </MenuItem>
                        </div>
                    </Popover>
                </div>
            </div>
        </div>
    )

    return (
        <div className={styles.Sidebar}>
            <div className={styles.mobileContent}>
                <HideOnScroll>
                    <AppBar
                        className={styles.appBar}
                        color="transparent"
                    >
                        <Toolbar
                            className={styles.toolbar}
                        >
                            <div className={styles.sidebarMobileHeader}>
                                <div className={styles.logoMobile}
                                    style={{ backgroundImage: `url("/admin/static/logo_small_white.svg")` }}
                                ></div>
                                {/* <p className={commonStyles.text} style={{ color: '#fff', opacity: 0.7 }}>Admin Panel</p> */}
                                <div className={styles.mobileActions}>
                                    <NotificationCenter />
                                    <IconButton onClick={handleOpenMenu}>
                                        <MenuIcon />
                                    </IconButton>
                                </div>
                            </div>
                        </Toolbar>
                    </AppBar>
                </HideOnScroll>
                <SwipeableDrawer
                    open={mobileOpen}
                    onClose={handleCloseMenu}
                    onOpen={handleOpenMenu}
                >
                    <div className={styles.drawer}>{sidebarContent}</div>
                </SwipeableDrawer>
            </div>
            <CmsInfo
                open={cmsInfoOpen}
                onClose={() => setCmsInfoOpen(false)}
            />
            <SystemMonitor
                open={systemMonitorOpen}
                onClose={() => setSystemMonitorOpen(false)}
            />
            <div className={styles.desktopContent}>{sidebarContent}</div>
        </div>
    )
}
Example #9
Source File: PostSettings.tsx    From Cromwell with MIT License 4 votes vote down vote up
PostSettings = (props: {
    postData?: TPost;
    isSettingsOpen: boolean;
    anchorEl: Element;
    allTags?: TTag[] | null;
    onClose: (newData: Partial<TPost>) => void;
    isSaving?: boolean;
    handleUnpublish: () => void;
    refetchMeta: () => Promise<Record<string, string> | undefined>;
}) => {
    const { postData, refetchMeta } = props;
    const [title, setTitle] = useState<string | undefined>(postData?.title ?? null);
    const [mainImage, setMainImage] = useState<string | undefined>(postData?.mainImage ?? null);
    const [pageDescription, setPageDescription] = useState<string | undefined>(postData?.pageDescription ?? null);
    const [pageKeywords, setPageKeywords] = useState<string[] | undefined>(postData?.meta?.keywords ?? null);
    const [pageTitle, setPageTitle] = useState<string | undefined>(postData?.pageTitle ?? null);
    const [slug, setSlug] = useState<string | undefined>(postData?.slug ?? null);
    const [tags, setTags] = useState<TTag[] | undefined>(postData?.tags ?? []);
    const [publishDate, setPublishDate] = useState<Date | undefined | null>(postData?.publishDate ?? null);
    const [featured, setFeatured] = useState<boolean | undefined | null>(postData?.featured ?? null);

    const handleChangeTags = (event: any, newValue: TTag[]) => {
        setTags(newValue);
    }
    const handleChangeKeywords = (event: any, newValue: string[]) => {
        setPageKeywords(newValue);
    }

    const handleClose = async () => {
        const newData = Object.assign({}, postData);
        newData.title = title;
        newData.mainImage = mainImage;
        newData.pageDescription = pageDescription;
        newData.pageTitle = pageTitle;
        newData.slug = slug;
        newData.tags = tags;
        newData.publishDate = publishDate;
        newData.featured = featured;
        if (pageKeywords) {
            if (!newData.meta) newData.meta = {};
            newData.meta.keywords = pageKeywords;
        }
        newData.customMeta = Object.assign({}, postData.customMeta, await getCustomMetaFor(EDBEntity.Post));
        props.onClose(newData);
    }

    let pageFullUrl;
    if (slug) {
        pageFullUrl = serviceLocator.getFrontendUrl() + resolvePageRoute('post', { slug: slug ?? postData.id + '' });
    }

    return (
        <Popover
            disableEnforceFocus
            open={props.isSettingsOpen}
            elevation={0}
            anchorEl={props.anchorEl}
            onClose={handleClose}
            anchorOrigin={{
                vertical: 'bottom',
                horizontal: 'center',
            }}
            classes={{ paper: styles.popover }}
            transformOrigin={{
                vertical: 'top',
                horizontal: 'center',
            }}
        >
            <div className={styles.PostSettings}>
                <p className={styles.headerText}>Post meta</p>
                <IconButton className={styles.closeBtn}
                    id="post-settings-close-btn"
                    onClick={handleClose}>
                    <CloseIcon />
                </IconButton>
                <TextField
                    label="Title"
                    value={title ?? ''}
                    fullWidth
                    className={styles.settingItem}
                    variant="standard"
                    onChange={e => setTitle(e.target.value)}
                />
                <TextField
                    label="Page URL"
                    className={styles.settingItem}
                    fullWidth
                    value={slug ?? ''}
                    onChange={e => setSlug(e.target.value)}
                    variant="standard"
                    helperText={pageFullUrl}
                />
                <ImagePicker
                    label="Main image"
                    onChange={(val) => setMainImage(val)}
                    value={mainImage}
                    className={styles.imageBox}
                    backgroundSize='cover'
                    showRemove
                />
                <Autocomplete
                    multiple
                    options={props.allTags ?? []}
                    defaultValue={tags?.map(tag => (props.allTags ?? []).find(allTag => allTag.name === tag.name)) ?? []}
                    getOptionLabel={(option) => option.name}
                    onChange={handleChangeTags}
                    renderInput={(params) => (
                        <TextField
                            {...params}
                            className={styles.settingItem}
                            variant="standard"
                            label="Tags"
                        />
                    )}
                />
                <LocalizationProvider dateAdapter={AdapterDateFns}>
                    <DatePicker
                        label="Publish date"
                        value={publishDate}
                        onChange={(newValue) => {
                            if (!newValue) {
                                setPublishDate(null);
                                return;
                            }
                            const date = new Date(newValue);
                            if (isNaN(date.getTime())) {
                                setPublishDate(null);
                                return;
                            }
                            setPublishDate(date);
                        }}
                        renderInput={(params) => <TextField
                            variant="standard"
                            fullWidth
                            {...params} />}
                    />
                </LocalizationProvider>
                <FormControlLabel
                    control={
                        <Checkbox
                            checked={featured}
                            onChange={() => setFeatured(!featured)}
                            color="primary"
                        />
                    }
                    style={{ margin: '10px 0' }}
                    className={styles.settingItem}
                    label="Featured post"
                />
                <TextField
                    label="Meta title"
                    className={styles.settingItem}
                    fullWidth
                    variant="standard"
                    value={pageTitle ?? ''}
                    onChange={e => setPageTitle(e.target.value)}
                />
                <TextField
                    label="Meta description"
                    className={styles.settingItem}
                    fullWidth
                    variant="standard"
                    value={pageDescription ?? ''}
                    onChange={e => setPageDescription(e.target.value)}
                />
                <Autocomplete
                    multiple
                    freeSolo
                    options={[]}
                    value={(pageKeywords ?? []) as any}
                    getOptionLabel={(option) => option}
                    onChange={handleChangeKeywords}
                    renderInput={(params) => (
                        <Tooltip title="Press ENTER to add">
                            <TextField
                                {...params}
                                className={styles.settingItem}
                                variant="standard"
                                label="Meta keywords"
                            />
                        </Tooltip>
                    )}
                />
                {postData?.published && (
                    <Tooltip title="Remove post from publication">
                        <Button variant="contained" color="primary"
                            className={styles.publishBtn}
                            size="small"
                            disabled={props.isSaving}
                            onClick={props.handleUnpublish}
                        >Unpublish</Button>
                    </Tooltip>
                )}
                <div style={{ marginBottom: '15px' }}></div>
                {postData && (
                    <RenderCustomFields
                        entityType={EDBEntity.Post}
                        entityData={postData}
                        refetchMeta={refetchMeta}
                    />
                )}
            </div>
        </Popover>
    )
}
Example #10
Source File: CustomData.tsx    From Cromwell with MIT License 4 votes vote down vote up
CustomFieldSettings = (props: {
    data: TCustomFieldSettingsData;
}) => {
    const data = props.data;
    const { settings, changeSettings } = data;
    const fieldData = data.field;
    const [selectOptionsOpen, setSelectOptionsOpen] = useState(false);
    const selectOptionsButtonRef = useRef();

    const changeFieldValue = (key: keyof TAdminCustomField, value: any) => {
        const customFields = (settings?.customFields ?? []).map(field => {
            if (field.id === props.data.id) {
                (field as any)[key] = value;
            }
            return field;
        })
        changeSettings('customFields', customFields)
    }

    const deleteField = () => {
        changeSettings('customFields', (settings?.customFields ?? [])
            .filter(field => field.id !== fieldData.id));

        unregisterCustomField(fieldData.entityType, fieldData.key);
    }

    const toggleSelectOptions = () => {
        setSelectOptionsOpen(!selectOptionsOpen);
    }

    const addSelectOption = () => {
        changeSettings('customFields', (settings?.customFields ?? [])
            .map(field => {
                if (field.id === fieldData.id) {
                    return {
                        ...fieldData,
                        options: [...(fieldData.options ?? []), ''],
                    }
                }
                return field;
            }));
    }

    const deleteSelectOption = (option: string) => {
        changeSettings('customFields', (settings?.customFields ?? [])
            .map(field => {
                if (field.id === fieldData.id) {
                    return {
                        ...fieldData,
                        options: (fieldData.options ?? []).filter(opt => opt !== option),
                    }
                }
                return field;
            }));
    }

    const changeSelectOption = (index: number, value: string) => {
        changeSettings('customFields', (settings?.customFields ?? [])
            .map(field => {
                if (field.id === fieldData.id) {
                    return {
                        ...fieldData,
                        options: (fieldData.options ?? []).map((opt, idx) => idx === index ? value : opt),
                    }
                }
                return field;
            }));
    }

    return (
        <div className={styles.customFieldItem}>
            <TextField
                label="Key"
                value={fieldData.key}
                onChange={e => changeFieldValue('key', e.target.value.replace(/\W/g, '_'))}
                size="small"
                className={styles.customFieldItemField}
            />
            <TextField
                label="Label"
                value={fieldData.label}
                onChange={e => changeFieldValue('label', e.target.value)}
                size="small"
                className={styles.customFieldItemField}
            />
            <Select
                className={styles.customFieldItemField}
                value={fieldData.fieldType}
                onChange={e => changeFieldValue('fieldType', e.target.value)}
                size="small"
                variant="outlined"
                label="Type"
                options={['Simple text', 'Text editor', 'Select', 'Image', 'Gallery', 'Color'] as TAdminCustomField['fieldType'][]}
            />
            {fieldData?.fieldType === 'Select' && (
                <Tooltip title="Select options">
                    <IconButton ref={selectOptionsButtonRef} onClick={toggleSelectOptions}>
                        <FormatListBulletedIcon />
                    </IconButton>
                </Tooltip>
            )}
            <Tooltip title="Delete field">
                <IconButton onClick={deleteField}>
                    <DeleteIcon />
                </IconButton>
            </Tooltip>
            {fieldData?.fieldType === 'Select' && (
                <Popover
                    open={selectOptionsOpen}
                    anchorEl={selectOptionsButtonRef.current}
                    onClose={toggleSelectOptions}
                    anchorOrigin={{
                        vertical: 'bottom',
                        horizontal: 'left',
                    }}
                    transformOrigin={{
                        vertical: 'top',
                        horizontal: 'left',
                    }}
                >
                    <div className={styles.selectOptions}>
                        {fieldData?.options?.map((option, idx) => (
                            <div key={idx}
                                style={{ display: 'flex', alignItems: 'center' }}
                            >
                                <TextField
                                    value={option}
                                    onChange={e => changeSelectOption(idx, e.target.value)}
                                    size="small"
                                    variant="standard"
                                />
                                <IconButton onClick={() => deleteSelectOption(option)}>
                                    <DeleteIcon />
                                </IconButton>
                            </div>
                        ))}
                        <IconButton onClick={addSelectOption}>
                            <AddIcon />
                        </IconButton>
                    </div>
                </Popover>
            )}
        </div>
    );
}
Example #11
Source File: BlockMenu.tsx    From Cromwell with MIT License 4 votes vote down vote up
render() {
        this.props.getInst(this);
        if (!this.selectedFrame) return <></>;

        const block = this.selectedBlock;
        const data = block?.getData();
        const bType = data?.type;
        const isConstant = data?.isConstant;
        const blockProps = this.props.createBlockProps(block);

        const addNewBlock = (bType: TCromwellBlockType) => () => {
            blockProps?.addNewBlockAfter?.(bType);
            this.setState({ addNewOpen: false });
        }

        let icon: JSX.Element;

        if (bType === 'text') {
            icon = <Tooltip title="Text block">
                <SubjectIcon />
            </Tooltip>
        }
        if (bType === 'plugin') {
            icon = <Tooltip title="Plugin block">
                <PluginIcon className={styles.customIcon} />
            </Tooltip>
        }
        if (bType === 'container') {
            icon = <Tooltip title="Container block">
                <WidgetsIcon />
            </Tooltip>
        }
        if (bType === 'HTML') {
            icon = <Tooltip title="HTML block">
                <CodeIcon />
            </Tooltip>
        }
        if (bType === 'image') {
            icon = <Tooltip title="Image block">
                <ImageIcon />
            </Tooltip>
        }
        if (bType === 'gallery') {
            icon = <Tooltip title="Gallery block">
                <PhotoLibraryIcon />
            </Tooltip>
        }
        if (bType === 'editor') {
            icon = <Tooltip title="Editor block">
                <EditOutlinedIcon />
            </Tooltip>
        }

        return ReactDom.createPortal(<>
            {!isConstant && (
                <div className={styles.actions}>
                    <div className={styles.typeIcon}>{icon}</div>
                    <Tooltip title="Delete block">
                        <MenuItem onClick={blockProps.deleteBlock}>
                            <DeleteForeverIcon />
                        </MenuItem>
                    </Tooltip>
                </div>
            )}
            <div className={styles.bottomActions} ref={this.addNewBtnEl}>
                <Tooltip title="Add block">
                    <IconButton onClick={this.handleOpenAddNew}>
                        <AddCircleOutlineIcon className={styles.addIcon} />
                    </IconButton>
                </Tooltip>
                <Popover
                    open={this.state.addNewOpen}
                    elevation={6}
                    anchorEl={this.addNewBtnEl.current}
                    onClose={this.handleCloseAddNew}
                    anchorOrigin={{
                        vertical: 'bottom',
                        horizontal: 'center',
                    }}
                    transformOrigin={{
                        vertical: 'top',
                        horizontal: 'center',
                    }}
                >
                    <div className={styles.widgetsContainer}>
                        <Grid container spacing={1} >
                            <Grid item xs={4} >
                                <MenuItem className={styles.widgetItem}
                                    onClick={addNewBlock('text')}
                                >
                                    <SubjectIcon />
                                    <p>Simple Text</p>
                                </MenuItem>
                            </Grid>
                            <Grid item xs={4} >
                                <MenuItem className={styles.widgetItem}
                                    onClick={addNewBlock('editor')}
                                >
                                    <EditOutlinedIcon />
                                    <p>Text Editor</p>
                                </MenuItem>
                            </Grid>
                            <Grid item xs={4} >
                                <MenuItem className={styles.widgetItem}
                                    onClick={addNewBlock('container')}
                                >
                                    <WidgetsIcon />
                                    <p>Container</p>
                                </MenuItem>
                            </Grid>
                            <Grid item xs={4} >
                                <MenuItem className={styles.widgetItem}
                                    onClick={addNewBlock('HTML')}
                                >
                                    <CodeIcon />
                                    <p>HTML</p>
                                </MenuItem>
                            </Grid>
                            <Grid item xs={4} >
                                <MenuItem className={styles.widgetItem}
                                    onClick={addNewBlock('plugin')}
                                >
                                    <PluginIcon className={styles.customIcon}
                                        style={{ filter: 'none' }} />
                                    <p>Plugin</p>
                                </MenuItem>
                            </Grid>
                            <Grid item xs={4} >
                                <MenuItem className={styles.widgetItem}
                                    onClick={addNewBlock('image')}
                                >
                                    <ImageIcon />
                                    <p>Image</p>
                                </MenuItem>
                            </Grid>
                            <Grid item xs={4} >
                                <MenuItem className={styles.widgetItem}
                                    onClick={addNewBlock('gallery')}
                                >
                                    <PhotoLibraryIcon />
                                    <p>Gallery</p>
                                </MenuItem>
                            </Grid>
                        </Grid>
                    </div>
                </Popover>
            </div>

        </>, this.selectedFrame)
    }
Example #12
Source File: PageListItem.tsx    From Cromwell with MIT License 4 votes vote down vote up
PageListItem = (props: {
    page: TExtendedPageInfo;
    activePage?: TPageInfo;
    handleOpenPage: (page: TPageInfo) => void;
    handleDeletePage: (page: TPageInfo) => void;
    onPreviewChange: (url: string) => any;
}) => {
    const { page, handleOpenPage, handleDeletePage, activePage } = props;
    const [editingPreview, setEditingPreview] = useState(false);
    const [previewUrl, setPreviewUrl] = useState(page?.previewUrl ?? page?.route);
    const changeUrlBtn = useRef();
    const active = activePage && page && activePage.route === page.route && activePage.id === page.id;
    const isGeneric = page.route && (page.route.endsWith('[slug]') || page.route.endsWith('[id]'));

    const openEditingPreview = (event) => {
        event.stopPropagation();
        setEditingPreview(true)
    }
    const closeEditingPreview = (event) => {
        event.stopPropagation();
        setEditingPreview(false);
        props.onPreviewChange(previewUrl);
    }
    const handleChangeUrl = (val: string) => {
        const rootUrl = page.route.replace('[slug]', '').replace('[id]', '');
        if (!val) val = '';
        val = val.replace(rootUrl, '');
        val = val.replace(/\W/g, '-');
        val = rootUrl + val;
        setPreviewUrl(val);
    }

    if (!page) return null;

    return (
        <div ref={changeUrlBtn} onClick={e => e.stopPropagation()}>
            <MenuItem
                className={`${styles.pageItem} ${active ? styles.activeItem : ''}`}
                onClick={() => handleOpenPage(page)}
            >
                <p>{page.name}</p>
                <div className={styles.pageItemActions}>
                    {isGeneric && (
                        <>
                            <Tooltip title="Edit preview URL">
                                <IconButton
                                    onClick={openEditingPreview}
                                >
                                    <LinkIcon />
                                </IconButton>
                            </Tooltip>
                            <Popover open={editingPreview}
                                anchorEl={changeUrlBtn.current}
                                style={{ zIndex: 9999 }}
                                onClose={closeEditingPreview}
                                anchorOrigin={{
                                    vertical: 'bottom',
                                    horizontal: 'left',
                                }}
                                transformOrigin={{
                                    vertical: 'top',
                                    horizontal: 'left',
                                }}
                                elevation={5}
                            >
                                <div className={styles.previewChangeContainer} onClick={e => e.stopPropagation()}>
                                    <TextField
                                        onChange={(e) => handleChangeUrl(e.target.value)}
                                        fullWidth
                                        value={previewUrl ?? ''}
                                        className={styles.settingsInput}
                                        variant="standard"
                                        label="Preview URL" />
                                </div>
                            </Popover>
                        </>
                    )}
                    {page.isVirtual && (
                        <Tooltip title="Delete page">
                            <IconButton
                                aria-label="Delete page"
                                onClick={(e) => {
                                    e.stopPropagation();
                                    handleDeletePage(page)
                                }}
                            >
                                <DeleteForeverIcon />
                            </IconButton>
                        </Tooltip>
                    )}
                    <KeyboardArrowRightIcon className={styles.activeIcon} htmlColor="#fff" />
                </div>
            </MenuItem>
        </div>
    )
}
Example #13
Source File: ThemeEditActions.tsx    From Cromwell with MIT License 4 votes vote down vote up
render() {
        const { isSidebarOpen, themeConfig } = this.state;
        const editingPageConfig = this.getThemeEditor().getEditingPageConfig();
        const pageInfos = this.state.pageInfos?.map(p => {
            if (p.id === editingPageConfig?.id) {
                return Object.assign({}, p, editingPageConfig);
            }
            return p;
        });
        const defaultPages = pageInfos?.filter(p => !p.isVirtual);
        const customPages = pageInfos?.filter(p => p.isVirtual);

        if (!themeConfig) return null;

        return (<>
            <div className={styles.ThemeEditActions} ref={this.wrapperRef}>
                <div>
                    <Tooltip title="Pages">
                        <IconButton
                            onClick={this.handlePagesToggle}
                        >
                            <PagesIcon />
                        </IconButton>
                    </Tooltip>
                    <Tooltip title="Page meta info">
                        <IconButton
                            onClick={this.handleMetaToggle}
                        >
                            <InfoOutlinedIcon />
                        </IconButton>
                    </Tooltip>
                    <Popover open={this.state.pageMetaOpen}
                        anchorEl={this.wrapperRef.current}
                        style={{ zIndex: 9999 }}
                        onClose={this.handleMetaToggle}
                        anchorOrigin={{
                            vertical: 'bottom',
                            horizontal: 'left',
                        }}
                        transformOrigin={{
                            vertical: 'top',
                            horizontal: 'left',
                        }}
                    >
                        <PageSettings
                            themeConfig={themeConfig}
                            pageConfig={editingPageConfig}
                            handlePageInfoChange={this.handlePageInfoChange}
                        />
                    </Popover>
                    <Tooltip title="Undo">
                        <IconButton
                            ref={this.undoBtnRef}
                            onClick={this.undoModification}
                        >
                            <UndoIcon />
                        </IconButton>
                    </Tooltip>
                    <Tooltip title="Redo">
                        <IconButton
                            ref={this.redoBtnRef}
                            onClick={this.redoModification}
                        >
                            <RedoIcon />
                        </IconButton>
                    </Tooltip>
                </div>
                <div className={styles.bottomBlock} >
                    <Tooltip title="Options">
                        <IconButton
                            onClick={this.handleOptionsToggle}
                            className={styles.actionBtn}
                            aria-label="Options"
                            ref={this.optionsAnchorEl}
                        >
                            <MoreVertOutlinedIcon />
                        </IconButton>
                    </Tooltip>
                    <Popover open={this.state.pageOptionsOpen}
                        anchorEl={this.optionsAnchorEl.current}
                        style={{ zIndex: 9999 }}
                        onClose={this.handleOptionsToggle}
                        anchorOrigin={{
                            vertical: 'bottom',
                            horizontal: 'left',
                        }}
                        transformOrigin={{
                            vertical: 'top',
                            horizontal: 'left',
                        }}
                    >
                        <div>
                            <MenuItem
                                onClick={this.handleResetCurrentPage} className={styles.optionsItem}>
                                <SettingsBackupRestoreIcon />
                                <p>Reset to default</p>
                            </MenuItem>
                            <MenuItem
                                disabled={!editingPageConfig?.isVirtual}
                                onClick={this.handleDeleteCurrentPage} className={styles.optionsItem}>
                                <DeleteForeverIcon />
                                <p>Delete page</p>
                            </MenuItem>
                        </div>
                    </Popover>
                    <Button variant="contained" color="primary"
                        className={styles.saveBtn}
                        size="small"
                        onClick={this.handleSaveEditingPage}
                    >Save</Button>
                </div>
            </div>
            <LoadingStatus isActive={this.state.loadingStatus} />
            <Drawer
                classes={{ paper: styles.sidebarPaper }}
                variant="persistent"
                anchor={'left'}
                open={isSidebarOpen}
                onClick={(e) => e.stopPropagation()}
            >
                <div className={styles.sidebar}>
                    <div className={styles.pageList} key="_2_">
                        <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                            <p className={styles.pageListHeader}>Theme pages</p>
                            <div style={{ display: 'flex', alignItems: 'center' }}>
                                <Tooltip title="Theme settings">
                                    <IconButton
                                        className={styles.sidebarCloseBtn}
                                        onClick={this.handleThemeSettingsToggle}>
                                        <SettingsIcon />
                                    </IconButton>
                                </Tooltip>
                                <Tooltip title="Close">
                                    <IconButton
                                        className={styles.sidebarCloseBtn}
                                        onClick={this.handlePagesToggle}>
                                        <CloseIcon />
                                    </IconButton>
                                </Tooltip>
                            </div>
                        </div>
                        {defaultPages?.map(p => (
                            <PageListItem
                                activePage={editingPageConfig}
                                key={p.id + p.route}
                                page={p}
                                handleOpenPage={this.handleOpenPage}
                                handleDeletePage={this.handleDeletePage}
                                onPreviewChange={this.handlePreviewChange(p)}
                            />
                        ))}
                    </div>
                    {customPages && (
                        <div className={styles.pageList} key="_3_">
                            <p className={styles.pageListHeader}>Custom pages</p>
                            {customPages.map(p => (
                                <PageListItem
                                    activePage={editingPageConfig}
                                    key={p.id + p.route}
                                    page={p}
                                    handleOpenPage={this.handleOpenPage}
                                    handleDeletePage={this.handleDeletePage}
                                    onPreviewChange={this.handlePreviewChange(p)}
                                />

                            ))}
                            <Tooltip title="Add a new page">
                                <MenuItem
                                    className={clsx(styles.addPageItem, styles.navBarItem)}
                                >
                                    <IconButton
                                        aria-label="add page"
                                        onClick={this.handleAddCustomPage}
                                    >
                                        <AddCircleIcon />
                                    </IconButton>
                                </MenuItem>
                            </Tooltip>
                        </div>
                    )}
                </div>
                <Modal
                    open={this.state?.themeSettingsOpen}
                    blurSelector="#root"
                    className={commonStyles.center}
                    onClose={this.handleThemeSettingsToggle}
                >
                    <div className={styles.themeSettings}>
                        <div className={styles.themeSettingsItem}
                            style={{ justifyContent: 'space-between' }}
                        >
                            <h3 className={styles.themeSettingsTitle}>Theme settings</h3>
                            <IconButton
                                onClick={this.handleThemeSettingsToggle}>
                                <CloseIcon />
                            </IconButton>
                        </div>
                        <div className={styles.themeSettingsItem}>
                            <ModeSwitch
                                value={this.state?.themePalette?.mode ?? 'light'}
                                onToggle={() => {
                                    this.changedPalette = true;
                                    this.setState(prev => {
                                        const isLight = prev.themePalette?.mode !== 'dark';
                                        return {
                                            themePalette: Object.assign({}, prev.themePalette, {
                                                mode: isLight ? 'dark' : 'light'
                                            })
                                        }
                                    });
                                }}
                            />
                        </div>
                        <div className={styles.themeSettingsItem}>
                            <ColorPicker
                                label="Primary color"
                                value={this.state.themePalette?.primaryColor}
                                onChange={(color) => {
                                    this.changedPalette = true;
                                    this.setState(prev => {
                                        return {
                                            themePalette: Object.assign({}, prev.themePalette, {
                                                primaryColor: color,
                                            })
                                        }
                                    })
                                }}
                            />
                        </div>
                        <div className={styles.themeSettingsItem}>
                            <ColorPicker
                                label="Secondary color"
                                value={this.state.themePalette?.secondaryColor}
                                onChange={(color) => {
                                    this.changedPalette = true;
                                    this.setState(prev => {
                                        return {
                                            themePalette: Object.assign({}, prev.themePalette, {
                                                secondaryColor: color,
                                            })
                                        }
                                    })
                                }}
                            />
                        </div>
                    </div>
                </Modal>
            </Drawer>
        </>)
    }
Example #14
Source File: Header.tsx    From Cromwell with MIT License 4 votes vote down vote up
Header = () => {
  const cmsSettings = getCmsSettings();
  const cart = useCart();
  const userInfo = useUserInfo();
  const [userOptionsOpen, setUserOptionsOpen] = useState<boolean>(false);
  const popperAnchorEl = useRef<HTMLDivElement | null>(null);
  const authClient = useAuthClient();

  const handleCartClick = () => {
    appState.isCartOpen = true;
  }

  const handleLogout = async () => {
    setUserOptionsOpen(false);
    authClient.signOut();
  }

  const handleOpenWishlist = () => {
    appState.isWishlistOpen = true;
  }

  const handleOpenWatched = () => {
    appState.isWatchedOpen = true;
  }

  const handleOpenSignIn = () => {
    appState.isSignInOpen = true;
  }

  return (
    <CContainer global id="header_1" className={`${styles.Header} ${commonStyles.text}`}>
      <CContainer id="header_21" className={styles.topPanel}>
        <CContainer id="header_22" className={`${commonStyles.content} ${styles.topPanelContent}`}>
          <CContainer className={styles.leftBlock} id="header_11">
            <CContainer id="header_01" className={styles.currencyOption}>
              <MuiCurrencySwitch />
            </CContainer>
            <CContainer id="header_51">
              <Tooltip title="Viewed items">
                <IconButton
                  aria-label="Open recently viewed items"
                  onClick={handleOpenWatched} style={{ margin: '-12px 0' }}>
                  <VisibilityIcon />
                </IconButton>
              </Tooltip>
              <Tooltip title="Wishlist">
                <IconButton
                  aria-label="Open wishlist"
                  onClick={handleOpenWishlist} style={{ margin: '-12px 0' }}>
                  <FavoriteIcon />
                </IconButton>
              </Tooltip>
            </CContainer>
            <CHTML id="header_02">
              <div className={styles.languageOption}>
              </div>
            </CHTML>
          </CContainer>

          <CContainer className={styles.rightBlock} id="header_12">
            <CContainer id="header_03" className={styles.welcomeMessage}>
              <CText id="header_35">Welcome message</CText>
            </CContainer>
            <CContainer id="header_04" className={styles.topPanelLinks}>
              <CText id="header_31" href="/pages/contact-us" className={clsx(commonStyles.link, styles.topPanelLink)}>Contact us</CText>
              {!userInfo && (
                <CText id="header_32" onClick={handleOpenSignIn} className={clsx(commonStyles.link, styles.topPanelLink)}>Sign in</CText>
              )}
              {userInfo && (
                <>
                  <div className={styles.userBox} ref={popperAnchorEl}
                    onClick={() => setUserOptionsOpen(true)}
                  >
                    {(userInfo?.avatar && userInfo?.avatar !== '') ? (
                      <div className={styles.avatar} style={{ backgroundImage: `url(${userInfo.avatar})` }}></div>
                    ) : <AccountCircleIcon className={styles.avatar} />}
                    <p className={clsx(styles.userName)}>{userInfo.fullName ?? ''}</p>
                  </div>
                  <Popover open={userOptionsOpen}
                    anchorEl={popperAnchorEl.current}
                    style={{ zIndex: 9999 }}
                    onClose={() => setUserOptionsOpen(false)}
                    anchorOrigin={{
                      vertical: 'top',
                      horizontal: 'right',
                    }}
                    transformOrigin={{
                      vertical: 'bottom',
                      horizontal: 'right',
                    }}
                  >
                    <div>
                      <Link href="/account">
                        <MenuItem className={styles.optionsItem} onClick={() => setUserOptionsOpen(false)}>
                          <AccountCircleOutlinedIcon />
                          <p>Your profile</p>
                        </MenuItem>
                      </Link>
                      <MenuItem onClick={handleLogout} className={styles.optionsItem}>
                        <ExitToAppIcon />
                        <p>Log out</p>
                      </MenuItem>
                    </div>
                  </Popover>
                </>
              )}
            </CContainer>
          </CContainer>
        </CContainer>
      </CContainer>

      <CContainer id="header_23" className={styles.mainPanel}>
        <CContainer id="header_41" className={`${commonStyles.content} ${styles.mainPanelContent}`}>
          <CContainer id="header_36" className={styles.logo}>
            <Link href="/">
              <img className={styles.logo} src={cmsSettings?.logo} alt="logo" />
            </Link>
          </CContainer>
          <CContainer id="header_37" className={styles.search}>
            <MuiProductSearch />
          </CContainer>
          <CContainer id="header_38" className={styles.phone}>
            <CText id="header_39" className={styles.phoneActionTip}>Call us now!</CText>
            <CText id="header_33" href={`tel:+123 (456) 78-90`} className={commonStyles.link}>+123 (456) 78-90</CText>
          </CContainer>
          <CContainer id="header_40">
            <ListItem button className={styles.cart} onClick={handleCartClick} >
              <div className={styles.cartIcon}></div>
              <div className={styles.cartExpandBlock}>
                <p className={styles.itemsInCart}>{cart?.length || 0}</p>
                <ExpandMoreIcon className={styles.cartExpandIcon} />
              </div>
            </ListItem>
          </CContainer>
        </CContainer>
      </CContainer>
      <CContainer id="header_24" className={styles.mainMenu}>
        <CContainer className={`${commonStyles.content} ${styles.mainMenuContent}`} id="header_13">
          <CPlugin id="header_main_menu" pluginName={"@cromwell/plugin-main-menu"} blockName="Main menu" />
        </CContainer>
      </CContainer>
      <div className={styles.mobileHeader}>
        <MobileHeader />
      </div>
    </CContainer>
  )
}
Example #15
Source File: TableWrapper.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
TableWrapper = ({
  itemActions,
  columns,
  onSelect,
  records,
  isLoading,
  loadingMessage = <Typography component="h3">Loading...</Typography>,
  entityName,
  selectedItems,
  idField,
  classes,
  radioSelection = false,
  customEmptyMessage = "",
  customPaperHeight = "",
  noBackground = false,
  columnsSelector = false,
  textSelectable = false,
  columnsShown = [],
  onColumnChange = (column: string, state: boolean) => {},
  infiniteScrollConfig,
  sortConfig,
  autoScrollToBottom = false,
  disabled = false,
  onSelectAll,
  rowStyle,
  parentClassName = "",
}: TableWrapperProps) => {
  const [columnSelectorOpen, setColumnSelectorOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = React.useState<any>(null);

  const findView = itemActions
    ? itemActions.find((el) => el.type === "view")
    : null;

  const clickAction = (rowItem: any) => {
    if (findView) {
      const valueClick = findView.sendOnlyId ? rowItem[idField] : rowItem;

      let disabled = false;

      if (findView.disableButtonFunction) {
        if (findView.disableButtonFunction(valueClick)) {
          disabled = true;
        }
      }

      if (findView.to && !disabled) {
        history.push(`${findView.to}/${valueClick}`);
        return;
      }

      if (findView.onClick && !disabled) {
        findView.onClick(valueClick);
      }
    }
  };

  const openColumnsSelector = (event: { currentTarget: any }) => {
    setColumnSelectorOpen(!columnSelectorOpen);
    setAnchorEl(event.currentTarget);
  };

  const closeColumnSelector = () => {
    setColumnSelectorOpen(false);
    setAnchorEl(null);
  };

  const columnsSelection = (columns: IColumns[]) => {
    return (
      <Fragment>
        <IconButton
          aria-describedby={"columnsSelector"}
          color="primary"
          onClick={openColumnsSelector}
          size="large"
        >
          <ViewColumnIcon fontSize="inherit" />
        </IconButton>
        <Popover
          anchorEl={anchorEl}
          id={"columnsSelector"}
          open={columnSelectorOpen}
          anchorOrigin={{
            vertical: "bottom",
            horizontal: "left",
          }}
          transformOrigin={{
            vertical: "top",
            horizontal: "left",
          }}
          onClose={closeColumnSelector}
        >
          <div className={classes.shownColumnsLabel}>Shown Columns</div>
          <div className={classes.popoverContent}>
            {columns.map((column: IColumns) => {
              return (
                <CheckboxWrapper
                  key={`tableColumns-${column.label}`}
                  label={column.label}
                  checked={columnsShown.includes(column.elementKey!)}
                  onChange={(e) => {
                    onColumnChange(column.elementKey!, e.target.checked);
                  }}
                  id={`chbox-${column.label}`}
                  name={`chbox-${column.label}`}
                  value={column.label}
                />
              );
            })}
          </div>
        </Popover>
      </Fragment>
    );
  };

  return (
    <Grid item xs={12} className={parentClassName}>
      <Paper
        className={`${classes.paper} ${noBackground ? classes.noBackground : ""}
        ${disabled ? classes.disabled : ""} 
        ${
          customPaperHeight !== ""
            ? customPaperHeight
            : classes.defaultPaperHeight
        }`}
      >
        {isLoading && (
          <Grid container className={classes.loadingBox}>
            <Grid item xs={12} style={{ textAlign: "center" }}>
              {loadingMessage}
            </Grid>
            <Grid item xs={12}>
              <LinearProgress />
            </Grid>
          </Grid>
        )}
        {columnsSelector && !isLoading && records.length > 0 && (
          <div className={classes.overlayColumnSelection}>
            {columnsSelection(columns)}
          </div>
        )}
        {records && !isLoading && records.length > 0 ? (
          // @ts-ignore
          <InfiniteLoader
            isRowLoaded={({ index }) => !!records[index]}
            loadMoreRows={
              infiniteScrollConfig
                ? infiniteScrollConfig.loadMoreRecords
                : () => new Promise(() => true)
            }
            rowCount={
              infiniteScrollConfig
                ? infiniteScrollConfig.recordsCount
                : records.length
            }
          >
            {({ onRowsRendered, registerChild }) => (
              // @ts-ignore
              <AutoSizer>
                {({ width, height }: any) => {
                  const optionsWidth = calculateOptionsSize(
                    width,
                    itemActions
                      ? itemActions.filter((el) => el.type !== "view").length
                      : 0
                  );
                  const hasSelect: boolean = !!(onSelect && selectedItems);
                  const hasOptions: boolean = !!(
                    (itemActions && itemActions.length > 1) ||
                    (itemActions &&
                      itemActions.length === 1 &&
                      itemActions[0].type !== "view")
                  );
                  return (
                    // @ts-ignore
                    <Table
                      ref={registerChild}
                      disableHeader={false}
                      headerClassName={"headerItem"}
                      headerHeight={40}
                      height={height}
                      noRowsRenderer={() => (
                        <Fragment>
                          {customEmptyMessage !== ""
                            ? customEmptyMessage
                            : `There are no ${entityName} yet.`}
                        </Fragment>
                      )}
                      overscanRowCount={10}
                      rowHeight={40}
                      width={width}
                      rowCount={records.length}
                      rowGetter={({ index }) => records[index]}
                      onRowClick={({ rowData }) => {
                        clickAction(rowData);
                      }}
                      rowClassName={`rowLine ${findView ? "canClick" : ""} ${
                        !findView && textSelectable ? "canSelectText" : ""
                      }`}
                      onRowsRendered={onRowsRendered}
                      sort={sortConfig ? sortConfig.triggerSort : undefined}
                      sortBy={sortConfig ? sortConfig.currentSort : undefined}
                      sortDirection={
                        sortConfig ? sortConfig.currentDirection : undefined
                      }
                      scrollToIndex={
                        autoScrollToBottom ? records.length - 1 : -1
                      }
                      rowStyle={(r) => {
                        if (rowStyle) {
                          const returnElement = rowStyle(r);

                          if (typeof returnElement === "string") {
                            return get(TableRowPredefStyles, returnElement, {});
                          }

                          return returnElement;
                        }

                        return {};
                      }}
                    >
                      {hasSelect && (
                        // @ts-ignore
                        <Column
                          headerRenderer={() => (
                            <Fragment>
                              {onSelectAll ? (
                                <div className={classes.checkAllWrapper}>
                                  <CheckboxWrapper
                                    label={""}
                                    onChange={onSelectAll}
                                    value="all"
                                    id={"selectAll"}
                                    name={"selectAll"}
                                    checked={
                                      selectedItems?.length === records.length
                                    }
                                  />
                                </div>
                              ) : (
                                <Fragment>Select</Fragment>
                              )}
                            </Fragment>
                          )}
                          dataKey={`select-${idField}`}
                          width={selectWidth}
                          disableSort
                          cellRenderer={({ rowData }) => {
                            const isSelected = selectedItems
                              ? selectedItems.includes(
                                  isString(rowData) ? rowData : rowData[idField]
                                )
                              : false;

                            return (
                              <Checkbox
                                value={
                                  isString(rowData) ? rowData : rowData[idField]
                                }
                                color="primary"
                                inputProps={{
                                  "aria-label": "secondary checkbox",
                                }}
                                className="TableCheckbox"
                                checked={isSelected}
                                onChange={onSelect}
                                onClick={(e) => {
                                  e.stopPropagation();
                                }}
                                checkedIcon={
                                  <span
                                    className={
                                      radioSelection
                                        ? classes.radioSelectedIcon
                                        : classes.checkedIcon
                                    }
                                  />
                                }
                                icon={
                                  <span
                                    className={
                                      radioSelection
                                        ? classes.radioUnselectedIcon
                                        : classes.unCheckedIcon
                                    }
                                  />
                                }
                              />
                            );
                          }}
                        />
                      )}
                      {generateColumnsMap(
                        columns,
                        width,
                        optionsWidth,
                        hasSelect,
                        hasOptions,
                        selectedItems || [],
                        idField,
                        columnsSelector,
                        columnsShown,
                        sortConfig ? sortConfig.currentSort : "",
                        sortConfig ? sortConfig.currentDirection : undefined
                      )}
                      {hasOptions && (
                        // @ts-ignore
                        <Column
                          dataKey={idField}
                          width={optionsWidth}
                          headerClassName="optionsAlignment"
                          className="optionsAlignment"
                          cellRenderer={({ rowData }) => {
                            const isSelected = selectedItems
                              ? selectedItems.includes(
                                  isString(rowData) ? rowData : rowData[idField]
                                )
                              : false;
                            return elementActions(
                              itemActions || [],
                              rowData,
                              isSelected,
                              idField
                            );
                          }}
                        />
                      )}
                    </Table>
                  );
                }}
              </AutoSizer>
            )}
          </InfiniteLoader>
        ) : (
          <Fragment>
            {!isLoading && (
              <div>
                {customEmptyMessage !== ""
                  ? customEmptyMessage
                  : `There are no ${entityName} yet.`}
              </div>
            )}
          </Fragment>
        )}
      </Paper>
    </Grid>
  );
}