add role search prototype

This commit is contained in:
41666 2021-07-08 14:53:40 -05:00
parent 38e2aa1c50
commit ef63260bd5
6 changed files with 113 additions and 23 deletions

View file

@ -45,17 +45,17 @@ export const RoleContainer = styled.div`
}
`;
export const AddRoleButton = styled.div`
export const AddRoleButton = styled.div<{ long?: boolean }>`
border: 2px solid ${palette.taupe500};
color: ${palette.taupe500};
border-radius: 24px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all ${transitions.actionable}s ease-in-out;
&:hover {
background-color: ${palette.taupe100};
transform: translateY(-2px);
@ -66,4 +66,13 @@ export const AddRoleButton = styled.div`
transform: translateY(0);
box-shadow: none;
}
${(props) =>
props.long
? css`
padding: 0 14px;
`
: css`
width: 32px;
`};
`;

View file

@ -1,9 +1,11 @@
import { Popover } from '@roleypoly/design-system/atoms/popover';
import { Role } from '@roleypoly/design-system/atoms/role';
import { TextInput } from '@roleypoly/design-system/atoms/text-input';
import { Toggle } from '@roleypoly/design-system/atoms/toggle';
import { Text } from '@roleypoly/design-system/atoms/typography';
import { RoleSearch } from '@roleypoly/design-system/molecules/role-search';
import { Category as CategoryT, CategoryType, Role as RoleT } from '@roleypoly/types';
import { sortBy } from 'lodash';
import { sortBy, uniq } from 'lodash';
import * as React from 'react';
import { GoPlus } from 'react-icons/go';
import ReactTooltip from 'react-tooltip';
@ -13,10 +15,13 @@ export type CategoryProps = {
title: string;
roles: RoleT[];
category: CategoryT;
unselectedRoles: RoleT[];
onChange: (updatedCategory: CategoryT) => void;
};
export const EditorCategory = (props: CategoryProps) => {
const [searchOpen, setSearchOpen] = React.useState(false);
const updateValue = <T extends keyof CategoryT>(key: T, value: CategoryT[T]) => {
props.onChange({ ...props.category, [key]: value });
};
@ -26,6 +31,16 @@ export const EditorCategory = (props: CategoryProps) => {
updateValue('roles', updatedRoles);
};
const handleRoleAdd = (role: RoleT) => {
const updatedRoles = uniq([...props.category.roles, role.id]);
updateValue('roles', updatedRoles);
setSearchOpen(false);
};
const handleSearchOpen = () => {
setSearchOpen(true);
};
return (
<Box>
<Section>
@ -65,21 +80,32 @@ export const EditorCategory = (props: CategoryProps) => {
<Text>Roles</Text>
</div>
<RoleContainer>
{sortBy(props.roles, 'position').map((role) => (
<Role
key={role.id}
role={role}
selected={false}
type="delete"
onClick={handleRoleDelete(role)}
{props.roles.length > 0 ? (
<>
{sortBy(props.roles, 'position').map((role) => (
<Role
key={role.id}
role={role}
selected={false}
type="delete"
onClick={handleRoleDelete(role)}
/>
))}
<RoleAddButton onClick={handleSearchOpen} tooltipId={props.category.id} />
</>
) : (
<RoleAddButton
long
onClick={handleSearchOpen}
tooltipId={props.category.id}
/>
))}
<AddRoleButton
data-tip="Add a role to the category"
data-for={props.category.id}
>
<GoPlus />
</AddRoleButton>
)}
<RoleSearchPopover
isOpen={searchOpen}
onExit={() => setSearchOpen(false)}
unselectedRoles={props.unselectedRoles}
onSelect={handleRoleAdd}
/>
</RoleContainer>
</Section>
@ -87,3 +113,47 @@ export const EditorCategory = (props: CategoryProps) => {
</Box>
);
};
const RoleAddButton = (props: {
onClick: () => void;
tooltipId: string;
long?: boolean;
}) => (
<AddRoleButton
data-tip="Add a role to the category"
data-for={props.tooltipId}
onClick={props.onClick}
long={props.long}
>
{props.long && <>Add a role&nbsp;&nbsp;</>}
<GoPlus />
</AddRoleButton>
);
const RoleSearchPopover = (props: {
onSelect: (role: RoleT) => void;
onExit: (type: string) => void;
isOpen: boolean;
unselectedRoles: RoleT[];
}) => {
const [searchTerm, setSearchTerm] = React.useState('');
return (
<Popover
position="top left"
active={props.isOpen}
canDefocus
onExit={props.onExit}
headContent={null}
>
{() => (
<RoleSearch
onSelect={props.onSelect}
roles={props.unselectedRoles}
searchTerm={searchTerm}
onSearchUpdate={setSearchTerm}
/>
)}
</Popover>
);
};

View file

@ -17,7 +17,6 @@ export type EditorShellProps = {
export const EditorShell = (props: EditorShellProps) => {
const [guild, setGuild] = React.useState<PresentableGuild>(props.guild);
const [reorderMode, setReorderMode] = React.useState<boolean>(false);
React.useEffect(() => {
setGuild(props.guild);
@ -43,10 +42,10 @@ export const EditorShell = (props: EditorShellProps) => {
props.onGuildChange?.(guild);
};
const hasChanges = React.useMemo(
() => !deepEqual(guild.data, props.guild.data),
[guild.data, props.guild.data]
);
const hasChanges = React.useMemo(() => !deepEqual(guild.data, props.guild.data), [
guild.data,
props.guild.data,
]);
return (
<>

View file

@ -6,7 +6,7 @@ import { EditorCategory } from '@roleypoly/design-system/molecules/editor-catego
import { CategoryContainer } from '@roleypoly/design-system/organisms/role-picker/RolePicker.styled';
import { Category, CategoryType, PresentableGuild, Role } from '@roleypoly/types';
import KSUID from 'ksuid';
import { sortBy } from 'lodash';
import { flatten, sortBy } from 'lodash';
import React from 'react';
import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd';
import { CgReorder } from 'react-icons/cg';
@ -31,6 +31,11 @@ const forceOrder = (categories: Category[]) =>
export const ServerCategoryEditor = (props: Props) => {
const [reorderMode, setReorderMode] = React.useState(false);
const unselectedRoles = React.useMemo(() => {
const selectedRoles = flatten(props.guild.data.categories.map((c) => c.roles));
return props.guild.roles.filter((r) => !selectedRoles.includes(r.id));
}, [props.guild.data.categories, props.guild.roles]);
const updateSingleCategory = (category: Category) => {
const newCategories = props.guild.data.categories.map((c) => {
if (c.id === category.id) {
@ -85,6 +90,7 @@ export const ServerCategoryEditor = (props: Props) => {
<EditorCategory
category={category}
title={category.name}
unselectedRoles={unselectedRoles}
roles={
category.roles
.map((role) => props.guild.roles.find((r) => r.id === role))

View file

@ -19,6 +19,7 @@
"react-helmet": "^6.1.0",
"react-icons": "^4.2.0",
"react-is": "^17.0.2",
"react-tiny-popover": "^6.0.5",
"react-tooltip": "^4.2.21",
"styled-components": "^5.3.0",
"styled-normalize": "^8.0.7"

View file

@ -14183,6 +14183,11 @@ react-textarea-autosize@^8.3.0:
use-composed-ref "^1.0.0"
use-latest "^1.0.0"
react-tiny-popover@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/react-tiny-popover/-/react-tiny-popover-6.0.5.tgz#2d5eb21d8a2758396ffa5bf5b761baac87dd6297"
integrity sha512-na6ZghMy5kqTPFSATb1pdSHO+/MikZvUxNk+zjXlz+gMXgiaOVuik5AiC5Oyj4yHpPf0nxoOmVQOmOmuDob6+A==
react-tooltip@^4.2.21:
version "4.2.21"
resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.21.tgz#840123ed86cf33d50ddde8ec8813b2960bfded7f"