import { numberToChroma } from '@roleypoly/design-system/atoms/colors'; import { evaluatePermission, permissions } from '@roleypoly/misc-utils/hasPermission'; import { Role as RPCRole, RoleSafety } from '@roleypoly/types'; import chroma from 'chroma-js'; import * as React from 'react'; import { FaCheck, FaTimes } from 'react-icons/fa'; import * as styled from './Role.styled'; type Props = { role: RPCRole; selected: boolean; disabled?: boolean; onClick?: (newState: boolean) => void; tooltipId?: string; type?: 'delete'; }; const getColorsFromBase = (baseColor: chroma.Color, contrastCheckThrow: number = 5) => { // Which has more contrast? Stepping up or stepping down? const contrastColorUp = baseColor.brighten(contrastCheckThrow); const contrastColorDown = baseColor.darken(contrastCheckThrow); if ( chroma.contrast(baseColor, contrastColorUp) > chroma.contrast(baseColor, contrastColorDown) ) { return { contrastColor: baseColor.brighten(3), accentColor: baseColor.brighten(2), }; } else { return { contrastColor: baseColor.darken(3), accentColor: baseColor.darken(2), }; } }; export const Role = (props: Props) => { const colorVars = { '--role-color': 'white', '--role-contrast': 'hsl(0,0,0%)', '--role-accent': 'hsl(0,0,70%)', }; if (props.role.color !== 0) { const baseColor = numberToChroma(props.role.color); const { accentColor, contrastColor } = getColorsFromBase(baseColor, 5); colorVars['--role-color'] = baseColor.css(); colorVars['--role-accent'] = accentColor.css(); colorVars['--role-contrast'] = contrastColor.css(); } const styledProps: styled.StyledProps = { selected: props.selected, defaultColor: props.role.color === 0, disabled: !!props.disabled, type: props.type, }; const extra = !props.disabled ? {} : { 'data-tip': disabledReason(props.role), 'data-for': props.tooltipId, }; return ( !props.disabled && props.onClick?.(!props.selected)} {...extra} > {!props.disabled && props.type !== 'delete' ? : } {props.role.name} ); }; const disabledReason = (role: RPCRole) => { switch (role.safety) { case RoleSafety.HigherThanBot: return `This role is above Roleypoly's own role.`; case RoleSafety.DangerousPermissions: const rolePermissions = BigInt(role.permissions); let permissionHits: string[] = []; evaluatePermission(rolePermissions, permissions.ADMINISTRATOR) && permissionHits.push('Administrator'); evaluatePermission(rolePermissions, permissions.MANAGE_ROLES) && permissionHits.push('Manage Roles'); return `This role has unsafe permissions: ${permissionHits.join(', ')}`; default: return `This role is disabled.`; } };