From ff09d8cfd533dd7bdfcd703e0d353b6531300225 Mon Sep 17 00:00:00 2001 From: Innders <49156310+Innders@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:17:54 +0100 Subject: [PATCH 1/5] chore: export enum template --- src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index 0ad11d1..225e459 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -30,7 +30,7 @@ export type { TagsSelectProps } from './Dropdowns/TagsSelect' export { IconSelect } from './Dropdowns/IconSelect' export type { IconSelectProps } from './Dropdowns/IconSelect' // enumDropdown -export { EnumDropdown } from './Dropdowns/EnumDropdown' +export { EnumDropdown, EnumTemplate } from './Dropdowns/EnumDropdown' export type { EnumDropdownProps } from './Dropdowns/EnumDropdown' // versionSelect export { VersionSelect } from './Dropdowns/VersionSelect' From 4ceb3ce7e32e011c81f8c994cf44711d54b2180b Mon Sep 17 00:00:00 2001 From: Innders <49156310+Innders@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:27:07 +0100 Subject: [PATCH 2/5] fix(EntityCard): always show reviewable icon --- src/EntityCard/EntityCard.stories.tsx | 8 ++++---- src/EntityCard/EntityCard.styled.ts | 8 -------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/EntityCard/EntityCard.stories.tsx b/src/EntityCard/EntityCard.stories.tsx index 0cea076..4eb6c7d 100644 --- a/src/EntityCard/EntityCard.stories.tsx +++ b/src/EntityCard/EntityCard.stories.tsx @@ -1,9 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react' import { EntityCard, EntityCardProps, PriorityType } from '.' -import { MouseEvent, useEffect, useState } from 'react' -import { Toolbar } from '../Layout/Toolbar' +import { MouseEvent, useState } from 'react' import { Button } from '../Button' -import { Panel } from '../Panels/Panel' import DnDTemplate from './DnD/DnDTemplate' import getRandomImage from '../helpers/getRandomImage' import styled from 'styled-components' @@ -38,7 +36,7 @@ const Template = ({ onActivate, ...props }: TemplateProps) => { const [isActive, setIsActive] = useState(false) return (
-
+
{ @@ -58,6 +56,7 @@ const StatusWrapper = styled.div` ` const StyledCell = styled.div` + width: 250px; padding: 8px; background-color: var(--md-sys-color-surface-container-low); border: 1px solid var(--md-sys-color-outline-variant); @@ -204,6 +203,7 @@ export const ProgressView: Story = { statusOptions: statuses, statusMiddle: true, statusNameOnly: true, + isPlayable: true, }, render: (args) => , } diff --git a/src/EntityCard/EntityCard.styled.ts b/src/EntityCard/EntityCard.styled.ts index fefc657..f8b1e70 100644 --- a/src/EntityCard/EntityCard.styled.ts +++ b/src/EntityCard/EntityCard.styled.ts @@ -284,9 +284,6 @@ export const Card = styled.div` container-type: inline-size; /* use container query for when the card gets smaller */ @container card (inline-size < 150px) { - .playable { - display: none; - } .title { .icon { display: none; @@ -296,11 +293,6 @@ export const Card = styled.div` } } } - @container card (inline-size < 100px) { - .playable { - display: none; - } - } /* hide everything on bottom but the status icon */ @container card (inline-size < 85px) { From e18cd7dde3f3cfa1c80042c79efac12fac81d02d Mon Sep 17 00:00:00 2001 From: Innders <49156310+Innders@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:31:16 +0100 Subject: [PATCH 3/5] fix(EntityCard): Use EnumDropdown for priority --- src/EntityCard/EntityCard.stories.tsx | 9 +++++---- src/EntityCard/EntityCard.tsx | 20 +++++++------------- src/EntityCard/priorities.json | 8 ++++---- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/EntityCard/EntityCard.stories.tsx b/src/EntityCard/EntityCard.stories.tsx index 4eb6c7d..10044eb 100644 --- a/src/EntityCard/EntityCard.stories.tsx +++ b/src/EntityCard/EntityCard.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react' -import { EntityCard, EntityCardProps, PriorityType } from '.' +import { EntityCard, EntityCardProps } from '.' import { MouseEvent, useState } from 'react' import { Button } from '../Button' import DnDTemplate from './DnD/DnDTemplate' @@ -9,6 +9,7 @@ import clsx from 'clsx' import { allUsers } from '../Dropdowns/helpers' import prioritiesData from './priorities.json' import { randomStatus, statuses } from '../Dropdowns/StatusSelect' +import { EnumDropdownOption } from '../Dropdowns/EnumDropdown' const meta: Meta = { component: EntityCard, @@ -21,7 +22,7 @@ type Story = StoryObj interface DataProps extends EntityCardProps {} -const priorities = prioritiesData as PriorityType[] +const priorities = prioritiesData as EnumDropdownOption[] // pick 1 - 3 users randomly from the array const randomUsers = allUsers @@ -76,7 +77,7 @@ const StatusTemplate = (props: TemplateProps) => { const [selectedUsers, setSelectedUsers] = useState(props.users?.map((u) => u.name) || []) const [selectedStatus, setSelectedStatus] = useState(props.status?.name) - const [selectedPriority, setSelectedPriority] = useState(props.priority?.name) + const [selectedPriority, setSelectedPriority] = useState(props.priority?.value) const handleChange = (change: any, key: string) => { console.log('changed:', change, key) @@ -84,7 +85,7 @@ const StatusTemplate = (props: TemplateProps) => { const users = props.assigneeOptions?.filter((u) => selectedUsers.includes(u.name)) || [] const status = statuses.find((s) => s.name === selectedStatus) - const priority = priorities.find((p) => p.name === selectedPriority) + const priority = priorities.find((p) => p.value === selectedPriority) const handleCellClick = (e: MouseEvent) => { // check if the click is editable item diff --git a/src/EntityCard/EntityCard.tsx b/src/EntityCard/EntityCard.tsx index 35e5b86..d518263 100644 --- a/src/EntityCard/EntityCard.tsx +++ b/src/EntityCard/EntityCard.tsx @@ -13,7 +13,8 @@ import { User } from '../User/UserImagesStacked' import clsx from 'clsx' import useImageLoader from '../helpers/useImageLoader' import useUserImagesLoader from './useUserImagesLoader' -import { Dropdown, DropdownProps, DropdownRef } from '../Dropdowns/Dropdown' +import { EnumDropdown, EnumDropdownOption, EnumDropdownProps } from '../Dropdowns/EnumDropdown' +import { DropdownRef } from '../Dropdowns/Dropdown' import { AssigneeSelect, AssigneeSelectProps } from '../Dropdowns/AssigneeSelect' import { Status, StatusSelect, StatusSelectProps } from '../Dropdowns/StatusSelect' import { UserImage } from '../User/UserImage' @@ -40,13 +41,6 @@ const notifications: { }, } -export type PriorityType = { - label?: string - color?: string - icon: IconType - name: string -} - type Section = 'title' | 'header' | 'users' | 'status' | 'priority' export interface EntityCardProps extends React.HTMLAttributes { @@ -61,7 +55,7 @@ export interface EntityCardProps extends React.HTMLAttributes { status?: Status // bottom right statusMiddle?: boolean // puts status in the center and priority in the bottom right statusNameOnly?: boolean // only show the status name unless it's too small to show, then use icon - priority?: PriorityType // bottom left after users + priority?: EnumDropdownOption // bottom left after users hidePriority?: boolean imageUrl?: string imageAlt?: string @@ -80,7 +74,7 @@ export interface EntityCardProps extends React.HTMLAttributes { // editing options assigneeOptions?: User[] statusOptions?: Status[] - priorityOptions?: PriorityType[] + priorityOptions?: EnumDropdownOption[] editOnHover?: boolean editAutoClose?: boolean // editing callbacks @@ -95,7 +89,7 @@ export interface EntityCardProps extends React.HTMLAttributes { image?: HTMLAttributes assigneeSelect?: Partial statusSelect?: Partial - prioritySelect?: Partial + prioritySelect?: Partial title?: HTMLAttributes topRow?: HTMLAttributes playableTag?: HTMLAttributes @@ -409,11 +403,11 @@ export const EntityCard = forwardRef( {/* priority dropdown */} {priorityEditable && ( - onPriorityChange(value as string[])} - value={[priority.name]} + value={[priority.value]} options={priorityOptions} tabIndex={0} {...pt.prioritySelect} diff --git a/src/EntityCard/priorities.json b/src/EntityCard/priorities.json index 4589f1b..7744363 100644 --- a/src/EntityCard/priorities.json +++ b/src/EntityCard/priorities.json @@ -3,25 +3,25 @@ "label": "Critical", "color": "rgb(203, 26, 26)", "icon": "keyboard_double_arrow_up", - "name": "urgent" + "value": "urgent" }, { "label": "High", "color": "rgb(0, 240, 180)", "icon": "keyboard_arrow_up", - "name": "high" + "value": "high" }, { "label": "Medium", "color": "rgb(52, 152, 219)", "icon": "check_indeterminate_small", - "name": "medium" + "value": "medium" }, { "label": "Low", "color": "rgb(186, 186, 186)", "icon": "keyboard_double_arrow_down", - "name": "low" + "value": "low" } ] From 2c4f85af0ba631b111656c3c648e2f9baf5e2bdf Mon Sep 17 00:00:00 2001 From: Innders <49156310+Innders@users.noreply.github.com> Date: Thu, 3 Oct 2024 08:28:43 +0100 Subject: [PATCH 4/5] fix: priority status on entityCard --- src/Dropdowns/EnumDropdown/EnumDropdown.tsx | 11 +++++++---- src/EntityCard/EntityCard.tsx | 18 +++++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Dropdowns/EnumDropdown/EnumDropdown.tsx b/src/Dropdowns/EnumDropdown/EnumDropdown.tsx index 70a40fb..490818b 100644 --- a/src/Dropdowns/EnumDropdown/EnumDropdown.tsx +++ b/src/Dropdowns/EnumDropdown/EnumDropdown.tsx @@ -15,7 +15,7 @@ export const EnumTemplate = ({ option, isSelected, isChanged, ...props }: EnumTe return ( @@ -26,20 +26,21 @@ export const EnumTemplate = ({ option, isSelected, isChanged, ...props }: EnumTe } export type EnumDropdownOption = { - value: string + value: string | number | boolean label: string icon?: IconType color?: string } export interface EnumDropdownProps - extends Omit { + extends Omit { options: EnumDropdownOption[] colorInverse?: boolean + value: (string | number | boolean)[] } export const EnumDropdown = forwardRef( - ({ colorInverse, ...props }, ref) => { + ({ colorInverse, value, ...props }, ref) => { return ( ( String(v))} $color={props.isChanged ? undefined : option?.color} // use color (but not when in changed state - editor) className={clsx({ inverse: colorInverse })} > @@ -59,6 +61,7 @@ export const EnumDropdown = forwardRef( itemTemplate={(option, isSelected) => ( )} + value={value?.map((v) => String(v))} {...props} /> ) diff --git a/src/EntityCard/EntityCard.tsx b/src/EntityCard/EntityCard.tsx index d518263..f226f2a 100644 --- a/src/EntityCard/EntityCard.tsx +++ b/src/EntityCard/EntityCard.tsx @@ -404,7 +404,6 @@ export const EntityCard = forwardRef( {/* priority dropdown */} {priorityEditable && ( onPriorityChange(value as string[])} value={[priority.value]} @@ -448,7 +447,7 @@ export const EntityCard = forwardRef( )} - {/* bottom center - status */} + {/* bottom right - status */} {shouldShowTag(status, 'status') && ( ( )} - {/* bottom right - priority */} + {/* bottom left - priority */} {shouldShowTag(priority && !hidePriority, 'priority') && ( editOnHover && handleEditableHover(e, 'priority')} onClick={(e) => handleEditableHover(e, 'priority')} {...pt.priorityTag} + className={clsx( + 'tag priority', + { editable: priorityEditable, isLoading }, + pt.priorityTag?.className, + )} > - {priority?.icon && } + {priority?.icon && ( + + )} )} From dcc6b6fb2fae77ed6c4f46d720188ff55be3f526 Mon Sep 17 00:00:00 2001 From: Innders <49156310+Innders@users.noreply.github.com> Date: Thu, 3 Oct 2024 09:23:40 +0100 Subject: [PATCH 5/5] fix(EntityCard): notification dot --- src/EntityCard/EntityCard.stories.tsx | 4 +- src/EntityCard/EntityCard.styled.ts | 4 + src/EntityCard/EntityCard.tsx | 529 +++++++++--------- .../Notification/Notification.styled.ts | 23 + src/EntityCard/Notification/Notification.tsx | 72 +++ src/EntityCard/index.ts | 1 + src/index.tsx | 4 +- 7 files changed, 361 insertions(+), 276 deletions(-) create mode 100644 src/EntityCard/Notification/Notification.styled.ts create mode 100644 src/EntityCard/Notification/Notification.tsx diff --git a/src/EntityCard/EntityCard.stories.tsx b/src/EntityCard/EntityCard.stories.tsx index 10044eb..d48369f 100644 --- a/src/EntityCard/EntityCard.stories.tsx +++ b/src/EntityCard/EntityCard.stories.tsx @@ -37,7 +37,7 @@ const Template = ({ onActivate, ...props }: TemplateProps) => { const [isActive, setIsActive] = useState(false) return (
-
+
{ @@ -167,6 +167,7 @@ const initData: DataProps = { export const Default: Story = { args: { ...initData, + notification: { comment: true }, }, render: Template, } @@ -182,7 +183,6 @@ export const Loading: Story = { export const TaskStatus: Story = { args: { variant: 'status', - notification: undefined, disabled: false, ...initData, isPlayable: false, diff --git a/src/EntityCard/EntityCard.styled.ts b/src/EntityCard/EntityCard.styled.ts index f8b1e70..78524db 100644 --- a/src/EntityCard/EntityCard.styled.ts +++ b/src/EntityCard/EntityCard.styled.ts @@ -22,6 +22,10 @@ const cardHoverStyles = css` } ` +export const Wrapper = styled.div` + position: relative; +` + type CardProps = { $statusColor?: string } diff --git a/src/EntityCard/EntityCard.tsx b/src/EntityCard/EntityCard.tsx index f226f2a..314e066 100644 --- a/src/EntityCard/EntityCard.tsx +++ b/src/EntityCard/EntityCard.tsx @@ -18,28 +18,7 @@ import { DropdownRef } from '../Dropdowns/Dropdown' import { AssigneeSelect, AssigneeSelectProps } from '../Dropdowns/AssigneeSelect' import { Status, StatusSelect, StatusSelectProps } from '../Dropdowns/StatusSelect' import { UserImage } from '../User/UserImage' - -type NotificationType = 'comment' | 'due' | 'overdue' - -const notifications: { - [key in NotificationType]: { - color: string - icon: IconType - } -} = { - comment: { - color: 'var(--md-sys-color-primary)', - icon: 'mark_unread_chat_alt', - }, - due: { - color: 'var(--md-custom-color-warning)', - icon: 'schedule', - }, - overdue: { - color: 'var(--md-sys-color-error-container)', - icon: 'alarm', - }, -} +import { NotificationDot, NotificationProps } from './Notification/Notification' type Section = 'title' | 'header' | 'users' | 'status' | 'priority' @@ -60,7 +39,7 @@ export interface EntityCardProps extends React.HTMLAttributes { imageUrl?: string imageAlt?: string imageIcon?: IconType - notification?: NotificationType + notification?: NotificationProps['notification'] isActive?: boolean isLoading?: boolean loadingSections?: Section[] @@ -97,6 +76,7 @@ export interface EntityCardProps extends React.HTMLAttributes { usersTag?: HTMLAttributes statusTag?: HTMLAttributes priorityTag?: HTMLAttributes + notificationDot?: HTMLAttributes } } @@ -245,272 +225,277 @@ export const EntityCard = forwardRef( (!!value && !isLoading) || (isLoading && loadingSections.includes(name)) return ( - { - if (!clickedEditableElement(e)) { - onActivate && onActivate() - } - props.onClick && props.onClick(e) - }} - onKeyDown={(e) => { - props.onKeyDown && props.onKeyDown(e) - if (e.code === 'Enter' || e.code === ' ') { - if (!clickedEditableElement(e)) onActivate && onActivate() - } - }} - > - {shouldShowTag(header, 'header') && ( - - {path && ( -
-
- {project && ( - <> - {project} - - / - - - )} - {path && ( - <> - ... - / - {path} - / - - )} -
-
- )} - {isLoading ? '' : header} -
- )} - { - if (!isDraggable) return - e.stopPropagation() - pt.thumbnail?.onKeyDown && pt.thumbnail?.onKeyDown(e) - if (e.code === 'Enter' || e.code === 'Space') { + + { + if (!clickedEditableElement(e)) { onActivate && onActivate() } + props.onClick && props.onClick(e) + }} + onKeyDown={(e) => { + props.onKeyDown && props.onKeyDown(e) + if (e.code === 'Enter' || e.code === ' ') { + if (!clickedEditableElement(e)) onActivate && onActivate() + } }} > - {/* middle Icon */} - - - {imageUrl && ( - + {path && ( +
+
+ {project && ( + <> + {project} + + / + + + )} + {path && ( + <> + ... + / + {path} + / + + )} +
+
)} - /> + {isLoading ? '' : header} + )} - {/* TOP ROW */} - - {/* top left */} - {(!isLoading || loadingSections.includes('title')) && ( - - {isLoading ? ( - 'loading card...' - ) : ( - <> - {titleIcon && } - {title && {title}} - - )} - - )} - - {/* top right */} - {isPlayable && ( - - - - )} - - {/* BOTTOM ROW */} - { + if (!isDraggable) return + e.stopPropagation() + pt.thumbnail?.onKeyDown && pt.thumbnail?.onKeyDown(e) + if (e.code === 'Enter' || e.code === 'Space') { + onActivate && onActivate() + } + }} > - {atLeastOneEditable && ( - <> - {/* EDITORS */} - - - - {/* assignees dropdown */} - {assigneesEditable && ( - user.name)} - options={assigneeOptions} - ref={assigneesDropdownRef} - onChange={(added, removed) => onAssigneeChange(added, removed)} - tabIndex={0} - {...pt.assigneeSelect} - /> - )} + {/* middle Icon */} + - {statusEditable && ( - onStatusChange([value])} - tabIndex={0} - {...pt.statusSelect} - /> + {imageUrl && ( + + )} + {/* TOP ROW */} + + {/* top left */} + {(!isLoading || loadingSections.includes('title')) && ( + + {isLoading ? ( + 'loading card...' + ) : ( + <> + {titleIcon && } + {title && {title}} + )} + + )} - {/* priority dropdown */} - {priorityEditable && ( - onPriorityChange(value as string[])} - value={[priority.value]} - options={priorityOptions} - tabIndex={0} - {...pt.prioritySelect} - /> - )} - - - )} + {/* top right */} + {isPlayable && ( + + + + )} + + {/* BOTTOM ROW */} + + {atLeastOneEditable && ( + <> + {/* EDITORS */} + - {/* bottom left - users */} - {shouldShowTag(users, 'users') && ( - editOnHover && handleEditableHover(e, 'assignees')} - onClick={(e) => handleEditableHover(e, 'assignees')} - {...pt.usersTag} - > - {users?.length ? ( - 2 })}> - {[...userWithValidatedImages].slice(0, 2).map((user, i) => ( - + {/* assignees dropdown */} + {assigneesEditable && ( + user.name)} + options={assigneeOptions} + ref={assigneesDropdownRef} + onChange={(added, removed) => onAssigneeChange(added, removed)} + tabIndex={0} + {...pt.assigneeSelect} /> - ))} - - ) : ( - - )} - - )} + )} - {/* bottom right - status */} - {shouldShowTag(status, 'status') && ( - -
- editOnHover && handleEditableHover(e, 'status')} - onClick={(e) => handleEditableHover(e, 'status')} - {...pt.statusTag} - > - {status?.icon && ( - onStatusChange([value])} + tabIndex={0} + {...pt.statusSelect} /> )} - {status?.name && ( - - {status.name} - + + {/* priority dropdown */} + {priorityEditable && ( + onPriorityChange(value as string[])} + value={[priority.value]} + options={priorityOptions} + tabIndex={0} + {...pt.prioritySelect} + /> )} - {status?.shortName && {status.shortName}} - -
-
- )} + + + )} - {/* bottom left - priority */} - {shouldShowTag(priority && !hidePriority, 'priority') && ( - editOnHover && handleEditableHover(e, 'priority')} - onClick={(e) => handleEditableHover(e, 'priority')} - {...pt.priorityTag} - className={clsx( - 'tag priority', - { editable: priorityEditable, isLoading }, - pt.priorityTag?.className, - )} - > - {priority?.icon && ( - - )} - - )} -
-
-
+ {/* bottom left - users */} + {shouldShowTag(users, 'users') && ( + editOnHover && handleEditableHover(e, 'assignees')} + onClick={(e) => handleEditableHover(e, 'assignees')} + {...pt.usersTag} + > + {users?.length ? ( + 2 })}> + {[...userWithValidatedImages].slice(0, 2).map((user, i) => ( + + ))} + + ) : ( + + )} + + )} + + {/* bottom right - status */} + {shouldShowTag(status, 'status') && ( + +
+ editOnHover && handleEditableHover(e, 'status')} + onClick={(e) => handleEditableHover(e, 'status')} + {...pt.statusTag} + > + {status?.icon && ( + + )} + {status?.name && ( + + {status.name} + + )} + {status?.shortName && ( + {status.shortName} + )} + +
+
+ )} + + {/* bottom left - priority */} + {shouldShowTag(priority && !hidePriority, 'priority') && ( + editOnHover && handleEditableHover(e, 'priority')} + onClick={(e) => handleEditableHover(e, 'priority')} + {...pt.priorityTag} + className={clsx( + 'tag priority', + { editable: priorityEditable, isLoading }, + pt.priorityTag?.className, + )} + > + {priority?.icon && ( + + )} + + )} + + + + + ) }, ) diff --git a/src/EntityCard/Notification/Notification.styled.ts b/src/EntityCard/Notification/Notification.styled.ts new file mode 100644 index 0000000..13312d6 --- /dev/null +++ b/src/EntityCard/Notification/Notification.styled.ts @@ -0,0 +1,23 @@ +import styled from 'styled-components' + +export const Notification = styled.div` + position: absolute; + top: 0; + right: 0; + translate: 40% -30%; + width: 20px; + height: 20px; + z-index: 100; + + border-radius: 12px; + box-shadow: -1px 1px 4px 1px #00000040; + + display: flex; + align-items: center; + justify-content: center; + + .icon { + font-size: 16px; + font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 200, 'opsz' 5; + } +` diff --git a/src/EntityCard/Notification/Notification.tsx b/src/EntityCard/Notification/Notification.tsx new file mode 100644 index 0000000..cfe7d5e --- /dev/null +++ b/src/EntityCard/Notification/Notification.tsx @@ -0,0 +1,72 @@ +import { Icon, IconType } from '../../Icon' +import * as Styled from './Notification.styled' + +// type NotificationType = 'comment' | 'due' | 'overdue' +export type Notification = { + comment?: boolean + due?: boolean + overdue?: boolean +} + +type NotificationKey = keyof Notification + +type NotificationConfig = { + color: string + icon: IconType +} + +const notifications: Record = { + comment: { + color: 'primary', + icon: 'forum', + }, + due: { + color: 'warning', + icon: 'schedule', + }, + overdue: { + color: 'error', + icon: 'alarm', + }, +} + +// if there are multiple notifications, the priority is based on the order of the keys, first has highest priority +const notificationPriorities: NotificationKey[] = ['overdue', 'due', 'comment'] + +const resolveNotification = (notification: Notification): NotificationConfig | undefined => { + const highest = notificationPriorities.find((key) => notification[key] === true) + return highest ? notifications[highest] : undefined +} + +const resolveColors = (colorKey: string): { backgroundColor: string; color: string } => { + const color = `--md-sys-color-on-${colorKey}` + const backgroundColor = `--md-sys-color-${colorKey}` + + return { + backgroundColor: `var(${backgroundColor})`, + color: `var(${color})`, + } +} + +import { forwardRef } from 'react' + +export interface NotificationProps extends React.HTMLAttributes { + notification?: Notification +} + +export const NotificationDot = forwardRef( + ({ notification, style = {}, ...props }, ref) => { + if (!notification) return null + // get the notification color and icon + const config = resolveNotification(notification) + if (!config) return null + const { icon, color: colorKey } = config + const { backgroundColor, color } = resolveColors(colorKey) + + return ( + + + + ) + }, +) diff --git a/src/EntityCard/index.ts b/src/EntityCard/index.ts index b9a5baa..9907d59 100644 --- a/src/EntityCard/index.ts +++ b/src/EntityCard/index.ts @@ -1,2 +1,3 @@ export * from './EntityCard' export * from './EntityCard.styled' +export * from './Notification/Notification' diff --git a/src/index.tsx b/src/index.tsx index 225e459..db307c6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -130,8 +130,8 @@ export type { UserImagesStackedProps } from './User/UserImagesStacked' // ENTITY // entityCard -export { EntityCard } from './EntityCard' -export type { EntityCardProps } from './EntityCard' +export { EntityCard, NotificationDot } from './EntityCard' +export type { EntityCardProps, Notification } from './EntityCard' // export getShimmerStyles export { getShimmerStyles } from './helpers'