Skip to content

Commit

Permalink
Ability to customise import statement for icon. (#13)
Browse files Browse the repository at this point in the history
* feat: add base customize modal

* feat: collection action modal base

* feat: collection actions base

* update

* add drag sort collection action

* feat: save to db and icon update

* feat: customize copy action name and template string

* copy right section button update

* typescript fix
  • Loading branch information
MrRobz authored Oct 10, 2021
1 parent 48f1368 commit b935fb8
Show file tree
Hide file tree
Showing 44 changed files with 900 additions and 140 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@
"yarn-deduplicate": "^3.1.0"
},
"dependencies": {
"@dnd-kit/core": "^4.0.0",
"@dnd-kit/sortable": "^5.0.0",
"@fontsource/dm-sans": "^4.5.0",
"@headlessui/react": "^1.4.0",
"dexie": "^3.0.3",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/icons/cursor-click.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/drag-handle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/eye-off.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/icons/eye.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/mail.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/pencil-alt.svg
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 @@ -6,7 +6,7 @@ import './style.css';
interface Props {
icon: Icon;
isSelected: boolean;
setSelectedIcon: Dispatch<SetStateAction<Icon | null>>;
setSelectedIcon?: Dispatch<SetStateAction<Icon | null>>;
}

export const IconCard: FC<Props> = ({ icon, isSelected, setSelectedIcon }) => {
Expand All @@ -21,7 +21,7 @@ export const IconCard: FC<Props> = ({ icon, isSelected, setSelectedIcon }) => {
}}
type="button"
data-icon-card-id={icon.id}
onClick={() => setSelectedIcon(icon)}
onClick={() => setSelectedIcon?.(icon)}
>
<div
className="rounded-2xl bg-black2 flex flex-col items-center justify-center border border-transparent hover:border-gray-600"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,38 @@
import { FC } from 'react';
import { ReactComponent as TrashIcon } from 'assets/icons/trash-16.svg';
import { ReactComponent as CopyIcon } from 'assets/icons/clipboard-copy-16.svg';
import { FC, useEffect, useRef, useState } from 'react';
import { ContextMenu } from 'components/ui/atomic-components';
import { useQueryClient } from 'react-query';
import { ReactComponent as ExternalLinkIcon } from 'assets/icons/external-link-16.svg';
import { CollectionsApi } from 'data/collections';
import { IconsApi } from 'data/icons';
import { capitalize, camelCase } from 'lodash';
import { ipcRenderer } from 'electron';
import { CollectionAction, CollectionsApi } from 'data/collections';
import { Icon, IconsApi } from 'data/icons';
import { getIconActionOfCollection } from 'data/collections/iconActions/utils';
import { useOnActionClick } from 'data/collections/iconActions/useOnActionClick';
import { inlineIconsMap } from 'data/collections/iconActions/inlineIconsMap';
import { useContextMenu } from './hooks/useContextMenu';
import { deleteIcon } from './utils';

export const IconContextMenu: FC<{
parentDom: HTMLDivElement;
}> = ({ parentDom }) => {
const queryClient = useQueryClient();
const { anchorPoint, clickedIconId } = useContextMenu();

const onCopy = async () => {
if (clickedIconId) {
const selectedIcon = await IconsApi.find(clickedIconId);

if (selectedIcon) {
const collection = await CollectionsApi.find(selectedIcon.collectionId);
const [iconActions, setIconActions] = useState<CollectionAction[]>([]);
const selectedIconRef = useRef<Icon | null>(null);

const collectionLoc = collection?.folderSrc || '';
const relativeIconPath = selectedIcon.imageSrc.replace(
collectionLoc,
''
);
const { anchorPoint, clickedIconId } = useContextMenu();

const copyText = `import { ReactComponent as ${capitalize(
camelCase(selectedIcon.name.replace(/^ic_/, ''))
)}Icon } from 'assets${relativeIconPath}';`;
const onActionClick = useOnActionClick();

navigator.clipboard.writeText(copyText);
}
}
};
useEffect(() => {
(async () => {
if (clickedIconId) {
const selectedIcon = await IconsApi.find(clickedIconId);

const openInFinder = async () => {
if (clickedIconId) {
const selectedIcon = await IconsApi.find(clickedIconId);
if (selectedIcon) {
selectedIconRef.current = selectedIcon;
const collection = await CollectionsApi.find(
selectedIcon.collectionId
);

if (selectedIcon) {
const collection = await CollectionsApi.find(selectedIcon.collectionId);

if (collection) {
ipcRenderer.send('open-collection-folder-icon', {
folderSrc: collection?.folderSrc,
fileName: `${selectedIcon.name}.${selectedIcon.mime}`,
});
setIconActions(getIconActionOfCollection(collection));
}
}
}
};

const onDelete = () => {
deleteIcon(clickedIconId, queryClient);
};
})();
});

if (!clickedIconId) {
return <></>;
Expand All @@ -71,20 +45,19 @@ export const IconContextMenu: FC<{
left: anchorPoint.x + parentDom.scrollLeft - parentDom.clientLeft - 260,
}}
>
<ContextMenu.Item onClick={onCopy}>
<CopyIcon className="mr-2" />
<div>Copy as React</div>
</ContextMenu.Item>

<ContextMenu.Item onClick={openInFinder}>
<ExternalLinkIcon className="mr-2" />
<div>Open in finder</div>
</ContextMenu.Item>

<ContextMenu.Item onClick={onDelete}>
<TrashIcon className="mr-2" />
<div>Delete</div>
</ContextMenu.Item>
{iconActions
.filter((action) => !action.hidden)
.map((actionObj) => (
<ContextMenu.Item
onClick={() =>
onActionClick({ actionObj, icon: selectedIconRef.current })
}
key={actionObj.id}
>
<div className="mr-2">{inlineIconsMap[actionObj.icon]}</div>
<div>{actionObj.name}</div>
</ContextMenu.Item>
))}
</ContextMenu>
);
};

This file was deleted.

This file was deleted.

6 changes: 4 additions & 2 deletions src/components/modules/IconsHome/IconCardsSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const IconCardsSection: FC<Props> = ({

return (
<div
className="w-full h-full overflow-y-auto pb-6 relative"
className="w-full h-full overflow-y-auto overflow-x-hidden pb-6 relative"
ref={wrapperDivRef}
>
<div
Expand All @@ -45,7 +45,9 @@ export const IconCardsSection: FC<Props> = ({
))}
</div>

<IconContextMenu parentDom={wrapperDivRef.current} />
{wrapperDivRef.current && (
<IconContextMenu parentDom={wrapperDivRef.current} />
)}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const CreateEditCollectionModal: FC<Props> = ({
folderSrc: folderLoc,
createdAt: Date.now(),
updatedAt: Date.now(),
actions: [],
};

return CollectionsApi.create(updatedCollection).then(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { CollectionAction } from 'data/collections';
import { inlineIconsMap } from 'data/collections/iconActions/inlineIconsMap';
import { FC } from 'react';
import { ReactComponent as EditIcon } from 'assets/icons/pencil-alt.svg';
import { ReactComponent as EyeIcon } from 'assets/icons/eye.svg';
import { ReactComponent as EyeOffIcon } from 'assets/icons/eye-off.svg';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { ReactComponent as DragHandleIcon } from 'assets/icons/drag-handle.svg';
import { ActionsListProps } from '.';

interface Props
extends Omit<ActionsListProps, 'actionItems' | 'setActionItems'> {
item: CollectionAction;
onActionChange: (selectedAction: CollectionAction) => void;
}

export const ActionItem: FC<Props> = ({
item,
onEditClick,
onActionChange,
}) => {
const icon = inlineIconsMap[item.icon];

const onVisibleChange = () => {
onActionChange({ ...item, hidden: !item.hidden });
};

const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isSorting,
} = useSortable({ id: item.id });

const style = {
transform: CSS.Transform.toString(transform),
transition,
zIndex: !transition ? 1 : 0,
};

return (
<div
className="group py-1 border-b border-gray-400 relative flex bg-gray-600"
ref={setNodeRef}
style={style}
>
<div className="text-gray-200 group flex items-center w-full outline-none px-2 py-2 text-sm">
<div className="mr-2">{icon}</div>
{item.name}
</div>

<div
{...attributes}
{...listeners}
className="flex items-center mr-1 outline-none"
>
<DragHandleIcon />
</div>

<div
className={`${
!isSorting && 'group-hover:opacity-100'
} opacity-10 w-0 h-full relative top-0 -right-2`}
>
<div className="absolute px-2 py-2 flex gap-3">
{item.isEditable && (
<button
className="outline-none"
type="button"
onClick={() => onEditClick(item)}
>
<EditIcon />
</button>
)}
<button
className="outline-none"
type="button"
onClick={onVisibleChange}
>
{item.hidden ? <EyeOffIcon /> : <EyeIcon />}
</button>
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { FC } from 'react';
import { ReactComponent as MailIcon } from 'assets/icons/mail.svg';

export const TempIconCard: FC = () => {
return (
<div className="w-20 h-20 rounded-2xl bg-black2 flex flex-col items-center justify-center border border-gray-600">
<MailIcon />
</div>
);
};
Loading

0 comments on commit b935fb8

Please sign in to comment.