mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39:11 +00:00
add role search prototype
This commit is contained in:
parent
38e2aa1c50
commit
ef63260bd5
6 changed files with 113 additions and 23 deletions
|
@ -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;
|
||||
`};
|
||||
`;
|
||||
|
|
|
@ -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 </>}
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue