diff --git a/lib/utils/role-utils.js b/lib/utils/role-utils.js --- a/lib/utils/role-utils.js +++ b/lib/utils/role-utils.js @@ -54,7 +54,28 @@ return message; } +type RoleDeletableAndEditableStatus = { + +isDeletable: boolean, + +isEditable: boolean, +}; +function useRoleDeletableAndEditableStatus( + roleName: string, + defaultRoleID: string, + existingRoleID: string, +): RoleDeletableAndEditableStatus { + return React.useMemo(() => { + const canDelete = roleName !== 'Admins' && defaultRoleID !== existingRoleID; + const canEdit = roleName !== 'Admins'; + + return { + isDeletable: canDelete, + isEditable: canEdit, + }; + }, [roleName, defaultRoleID, existingRoleID]); +} + export { useFilterPermissionOptionsByThreadType, constructRoleDeletionMessagePrompt, + useRoleDeletableAndEditableStatus, }; diff --git a/web/components/menu.css b/web/components/menu.css --- a/web/components/menu.css +++ b/web/components/menu.css @@ -42,6 +42,14 @@ top: 24px; } +div.menuActionListRoleActions { + font-size: var(--s-font-14); + background-color: var(--menu-bg-light); + color: var(--menu-color); + stroke: var(--menu-color); + top: 20px; +} + button.menuAction { color: inherit; z-index: 1; diff --git a/web/components/menu.react.js b/web/components/menu.react.js --- a/web/components/menu.react.js +++ b/web/components/menu.react.js @@ -6,7 +6,11 @@ import css from './menu.css'; import { useRenderMenu } from '../menu-provider.react.js'; -type MenuVariant = 'thread-actions' | 'member-actions' | 'community-actions'; +type MenuVariant = + | 'thread-actions' + | 'member-actions' + | 'community-actions' + | 'role-actions'; type MenuProps = { +icon: React.Node, @@ -30,6 +34,7 @@ [css.menuActionListThreadActions]: variant === 'thread-actions', [css.menuActionListMemberActions]: variant === 'member-actions', [css.menuActionListCommunityActions]: variant === 'community-actions', + [css.menuActionListRoleActions]: variant === 'role-actions', }); const menuActionList = React.useMemo( diff --git a/web/roles/community-roles-modal.css b/web/roles/community-roles-modal.css --- a/web/roles/community-roles-modal.css +++ b/web/roles/community-roles-modal.css @@ -17,7 +17,7 @@ } .rolePanelTitle:last-of-type { - margin-right: 32px; + margin-right: 90px; } .separator { diff --git a/web/roles/community-roles-modal.react.js b/web/roles/community-roles-modal.react.js --- a/web/roles/community-roles-modal.react.js +++ b/web/roles/community-roles-modal.react.js @@ -32,11 +32,12 @@ Object.keys(roleNamesToMembers).map(roleName => ( )), - [roleNamesToMembers], + [roleNamesToMembers, threadInfo], ); const rolePermissionsForNewRole = React.useMemo(() => new Set(), []); diff --git a/web/roles/role-actions-menu.css b/web/roles/role-actions-menu.css new file mode 100644 --- /dev/null +++ b/web/roles/role-actions-menu.css @@ -0,0 +1,4 @@ +.menuContainer { + color: var(--menu-color-light); + width: 10px; +} diff --git a/web/roles/role-actions-menu.react.js b/web/roles/role-actions-menu.react.js new file mode 100644 --- /dev/null +++ b/web/roles/role-actions-menu.react.js @@ -0,0 +1,84 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; +import type { ThreadInfo } from 'lib/types/thread-types.js'; +import { useRoleDeletableAndEditableStatus } from 'lib/utils/role-utils.js'; + +import css from './role-actions-menu.css'; +import MenuItem from '../components/menu-item.react.js'; +import Menu from '../components/menu.react.js'; + +const menuIcon = ; + +type RoleActionsMenuProps = { + +threadInfo: ThreadInfo, + +roleName: string, +}; + +function RoleActionsMenu(props: RoleActionsMenuProps): React.Node { + const { threadInfo, roleName } = props; + + const defaultRoleID = Object.keys(threadInfo.roles).find( + roleID => threadInfo.roles[roleID].isDefault, + ); + invariant(defaultRoleID, 'default role should exist'); + + const existingRoleID = Object.keys(threadInfo.roles).find( + roleID => threadInfo.roles[roleID].name === roleName, + ); + invariant(existingRoleID, 'existing role should exist'); + + const roleOptions = useRoleDeletableAndEditableStatus( + roleName, + defaultRoleID, + existingRoleID, + ); + + // TODO: Implement in following diffs + const openEditRoleModal = React.useCallback(() => {}, []); + const openDeleteRoleModal = React.useCallback(() => {}, []); + + const menuItems = React.useMemo(() => { + const availableOptions = []; + const { isDeletable, isEditable } = roleOptions; + + if (isEditable) { + availableOptions.push( + , + ); + } + + if (isDeletable) { + availableOptions.push( + , + ); + } + + return availableOptions; + }, [roleOptions, openDeleteRoleModal, openEditRoleModal]); + + return ( +
+ + {menuItems} + +
+ ); +} + +export default RoleActionsMenu; diff --git a/web/roles/role-panel-entry.css b/web/roles/role-panel-entry.css --- a/web/roles/role-panel-entry.css +++ b/web/roles/role-panel-entry.css @@ -1,7 +1,5 @@ .rolePanelEntry { - display: flex; - flex-direction: row; - justify-content: space-between; + display: grid; align-items: center; padding: 12px; color: var(--community-roles-text-color); @@ -13,9 +11,16 @@ } .rolePanelCountEntryContainer { - margin-right: 40px; display: flex; - align-items: flex-end; + align-items: center; + justify-content: flex-end; + grid-column: 3; +} + +.rolePanelCountAndIcon { + display: flex; + align-items: center; + margin-right: 90px; } .rolePanelCountEntry { diff --git a/web/roles/role-panel-entry.react.js b/web/roles/role-panel-entry.react.js --- a/web/roles/role-panel-entry.react.js +++ b/web/roles/role-panel-entry.react.js @@ -2,22 +2,29 @@ import * as React from 'react'; +import type { ThreadInfo } from 'lib/types/thread-types.js'; + +import RoleActionsMenu from './role-actions-menu.react.js'; import css from './role-panel-entry.css'; import CommIcon from '../CommIcon.react.js'; type RolePanelEntryProps = { + +threadInfo: ThreadInfo, +roleName: string, +memberCount: number, }; function RolePanelEntry(props: RolePanelEntryProps): React.Node { - const { roleName, memberCount } = props; + const { threadInfo, roleName, memberCount } = props; return (
{roleName}
-
{memberCount}
- +
+
{memberCount}
+ +
+
);