Skip to content

Commit

Permalink
feat(flat-components): add rename panel
Browse files Browse the repository at this point in the history
  • Loading branch information
crimx committed Mar 30, 2021
1 parent f539023 commit b20ec2a
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export interface CloudStorageFileListFileNameProps {
onItemMenuClick?: (fileUUID: string, menuKey: React.Key) => void;
/** When title is clicked */
onItemTitleClick?: (fileUUID: string) => void;
/** UUID of file that is under renaming */
renamingFileUUID?: string;
/** Rename file. Empty name for cancelling */
onRename?: (fileUUID: string, name: string) => void;
}

export const CloudStorageFileListFileName = React.memo<CloudStorageFileListFileNameProps>(
Expand All @@ -31,6 +35,8 @@ export const CloudStorageFileListFileName = React.memo<CloudStorageFileListFileN
fileMenus,
onItemMenuClick,
onItemTitleClick,
renamingFileUUID,
onRename,
}) {
const menuItems = fileMenus && fileMenus(file, index);

Expand All @@ -42,6 +48,8 @@ export const CloudStorageFileListFileName = React.memo<CloudStorageFileListFileN
titleClickable={titleClickable}
convertStatus={file.convert}
onTitleClick={onItemTitleClick}
renamingFileUUID={renamingFileUUID}
onRename={onRename}
/>
{menuItems && menuItems.length > 0 && (
<div className="cloud-storage-file-list-menu-btn-wrap">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import {
export interface CloudStorageFileListProps
extends Pick<
CloudStorageFileListFileNameProps,
"fileMenus" | "onItemMenuClick" | "titleClickable" | "onItemTitleClick"
| "fileMenus"
| "onItemMenuClick"
| "titleClickable"
| "onItemTitleClick"
| "renamingFileUUID"
| "onRename"
> {
/** Cloud Storage List items */
files: CloudStorageFile[];
Expand All @@ -36,6 +41,8 @@ export const CloudStorageFileList: React.FC<CloudStorageFileListProps> = ({
onItemMenuClick,
titleClickable = false,
onItemTitleClick,
renamingFileUUID,
onRename,
}) => {
const popupContainerRef = useRef<HTMLDivElement>(null);
const getPopupContainer = useCallback(() => popupContainerRef.current || document.body, []);
Expand Down Expand Up @@ -65,6 +72,8 @@ export const CloudStorageFileList: React.FC<CloudStorageFileListProps> = ({
titleClickable={titleClickable}
onItemMenuClick={onItemMenuClick}
onItemTitleClick={onItemTitleClick}
renamingFileUUID={renamingFileUUID}
onRename={onRename}
/>
);
},
Expand Down Expand Up @@ -92,7 +101,15 @@ export const CloudStorageFileList: React.FC<CloudStorageFileListProps> = ({
},
},
],
[fileMenus, getPopupContainer, onItemMenuClick, onItemTitleClick, titleClickable],
[
fileMenus,
getPopupContainer,
onItemMenuClick,
onItemTitleClick,
onRename,
renamingFileUUID,
titleClickable,
],
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
position: relative;
display: flex;
align-items: center;
height: 2em;
}

.cloud-storage-file-list-menu-btn-wrap {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ ConvertError.args = {
convertStatus: "error",
};

export const Rename: Story<CloudStorageFileTitleProps> = args => (
<CloudStorageFileTitle {...args} />
);
Rename.args = {
fileUUID: faker.random.uuid(),
fileName: faker.random.word() + ".doc",
convertStatus: "success",
};
Rename.args.renamingFileUUID = Rename.args.fileUUID;

export const FileTitles: Story<CloudStorageFileTitleProps> = ({ onTitleClick }) => {
const renderFileTitles = (fileType: string, exts: string[]): React.ReactElement => (
<div className="column is-one-quarter-tablet">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import checkSVG from "./icons/check.svg";
import crossSVG from "./icons/cross.svg";

import { Button, Input } from "antd";
import React, { useEffect, useRef, useState } from "react";

export interface CloudStorageFileTitleRenameProps {
fileUUID: string;
fileName: string;
/** Rename file. Empty name for cancelling */
onRename?: (fileUUID: string, name: string) => void;
}

export const CloudStorageFileTitleRename = React.memo<CloudStorageFileTitleRenameProps>(
function CloudStorageFileTitleRename({ fileUUID, fileName, onRename }) {
// Antd docs uses any
const inputRef = useRef<any>();
const [oldName, ext] = splitFileName(fileName);
const [newName, setText] = useState(oldName);

const onCancel = onRename && (() => onRename(fileUUID, ""));
const onConfirm =
onRename &&
(() => {
const newFileName = newName + ext;
onRename(fileUUID, newFileName === fileName ? "" : newFileName);
});

useEffect(() => {
const input: Input = inputRef.current;
if (input) {
input.focus();
input.select();
}
}, []);

return (
<>
<Input
ref={inputRef}
size="small"
className="cloud-storage-file-title-rename-input"
value={newName}
onChange={e => setText(e.currentTarget.value)}
onPressEnter={onConfirm}
/>
<Button
type="text"
shape="circle"
size="small"
className="cloud-storage-file-title-rename-btn"
onClick={onConfirm}
>
<img src={checkSVG} width={22} height={22} alt="confirm" />
</Button>
<Button
type="text"
shape="circle"
size="small"
className="cloud-storage-file-title-rename-btn"
onClick={onCancel}
>
<img src={crossSVG} width={22} height={22} alt="cancel" />
</Button>
</>
);
},
);

/** split filename to name and extension */
function splitFileName(fileName: string): [string, string] {
const dotIndex = fileName.lastIndexOf(".");
return [fileName.substr(0, dotIndex), fileName.slice(dotIndex)];
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import convertErrorSVG from "./icons/convert-error.svg";
import React from "react";
import classNames from "classnames";
import { CloudStorageConvertStatusType } from "../types";
import { CloudStorageFileTitleRename } from "./CloudStorageFileTitleRename";

export interface CloudStorageFileTitleProps
extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {
Expand All @@ -25,6 +26,10 @@ export interface CloudStorageFileTitleProps
titleClickable?: boolean;
/** When title is clicked */
onTitleClick?: (fileUUID: string) => void;
/** UUID of file that is under renaming */
renamingFileUUID?: string;
/** Rename file. Empty name for cancelling */
onRename?: (fileUUID: string, name: string) => void;
}

/**
Expand All @@ -37,6 +42,8 @@ export const CloudStorageFileTitle = React.memo<CloudStorageFileTitleProps>(
convertStatus,
titleClickable = false,
onTitleClick,
renamingFileUUID,
onRename,
...restProps
}) {
const isConverting = convertStatus === "converting";
Expand Down Expand Up @@ -78,7 +85,13 @@ export const CloudStorageFileTitle = React.memo<CloudStorageFileTitleProps>(
/>
) : null}
</span>
{titleClickable ? (
{renamingFileUUID === fileUUID ? (
<CloudStorageFileTitleRename
fileUUID={fileUUID}
fileName={fileName}
onRename={onRename}
/>
) : titleClickable ? (
<a
className="cloud-storage-file-title-content"
onClick={e => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@
user-select: none;
}

.cloud-storage-file-title-rename-input {
width: 200px;
max-width: 80%;
box-shadow: none;
border-radius: 4px;
height: 30px;
margin-left: 5px;
}

.cloud-storage-file-title-rename-btn {
margin-left: 5px;
}

.cloud-storage-file-title-converting,
.cloud-storage-file-title-convert-error {
position: absolute;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const fakeStoreImplProps = [
"onUploadRetry",
"onItemMenuClick",
"onItemTitleClick",
"onNewFileName",
] as const;

type FakeStoreImplProps = typeof fakeStoreImplProps[number];
Expand All @@ -38,8 +39,9 @@ class FakeStore extends CloudStorageStore {
onUploadCancel;
onUploadPanelClose;
onUploadRetry;
onItemMenuClick;
onItemMenuClick: FakeStoreConfig["onItemMenuClick"];
onItemTitleClick;
onNewFileName: FakeStoreConfig["onNewFileName"];

constructor(config: FakeStoreConfig) {
super();
Expand All @@ -49,8 +51,20 @@ class FakeStore extends CloudStorageStore {
this.onUploadCancel = config.onUploadCancel;
this.onUploadPanelClose = config.onUploadPanelClose;
this.onUploadRetry = config.onUploadRetry;
this.onItemMenuClick = config.onItemMenuClick;
this.onItemMenuClick = (fileUUID, menuKey) => {
if (menuKey === "rename") {
this.setRenamePanel(fileUUID);
}
config.onItemMenuClick(fileUUID, menuKey);
};
this.onItemTitleClick = config.onItemTitleClick;
this.onNewFileName = (fileUUID, name) => {
const file = this.files.find(file => file.fileUUID === fileUUID);
if (file) {
file.fileName = name;
}
config.onNewFileName(fileUUID, name);
};

makeObservable(
this,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const CloudStorageFileListContainer = observer<CloudStorageFileListContai
fileMenus={store.fileMenus}
onItemMenuClick={store.onItemMenuClick}
onItemTitleClick={store.onItemTitleClick}
renamingFileUUID={store.renamingFileUUID}
onRename={store.onRename}
/>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export abstract class CloudStorageStore {
uploadStatusesMap = observable.map</** fileUUID */ string, CloudStorageUploadStatus>();
/** It changes when user toggles the expand button */
isUploadPanelExpand = false;
/** UUID of file that is under renaming */
renamingFileUUID?: string = "";

/** Number of total upload */
get uploadTotalCount(): number {
Expand Down Expand Up @@ -93,6 +95,7 @@ export abstract class CloudStorageStore {
files: observable,
selectedFileUUIDs: observable,
isUploadPanelExpand: observable,
renamingFileUUID: observable,

uploadFinishedCount: computed,
uploadTotalCount: computed,
Expand All @@ -106,6 +109,10 @@ export abstract class CloudStorageStore {
});
}

setRenamePanel = (fileUUID?: string): void => {
this.renamingFileUUID = fileUUID;
};

setPanelExpand = (isExpand: boolean): void => {
this.isUploadPanelExpand = isExpand;
};
Expand All @@ -119,6 +126,16 @@ export abstract class CloudStorageStore {
this.selectedFileUUIDs = fileUUIDs;
};

/** When a rename event is received. Could be empty. Put business logic in `onNewFileName` instead. */
onRename = (fileUUID: string, name: string): void => {
// hide rename panel
this.renamingFileUUID = "";

if (name) {
this.onNewFileName(fileUUID, name);
}
};

/** Render file menus item base on fileUUID */
abstract fileMenus: (
file: CloudStorageFile,
Expand All @@ -145,4 +162,7 @@ export abstract class CloudStorageStore {

/** Stop uploading a file */
abstract onUploadCancel(fileUUID: string): void;

/** When a filename is changed to a meaningful new name */
abstract onNewFileName(fileUUID: string, name: string): void;
}

0 comments on commit b20ec2a

Please sign in to comment.