diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 5a4bab67..60ffb959 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,7 +1,7 @@ name: Feature request 💄 description: Suggest a new idea for one of the packages. labels: ['enhancement', 'needs triage'] -title: '[Feature?]: ' +title: '[Feat?]: ' body: - type: markdown attributes: diff --git a/docs/app/components/MatchSideNavItem.tsx b/docs/app/components/MatchSideNavItem.tsx index 9861c460..153e9bf2 100644 --- a/docs/app/components/MatchSideNavItem.tsx +++ b/docs/app/components/MatchSideNavItem.tsx @@ -17,7 +17,7 @@ export default function MatchSideNavItem(props: HeadingItem | LinkItem) { return (

) { w: 'full', })} > - {props.navList.map((item) => ( + {props.navList.map((item, idx) => (

  • diff --git a/docs/app/data/categories.json b/docs/app/data/categories.json index 9bc1359d..1ccf130b 100644 --- a/docs/app/data/categories.json +++ b/docs/app/data/categories.json @@ -7,7 +7,7 @@ "communication": { "name": "Communication", "description": "Communication components provide useful information.", - "items": ["Tag", "Tooltip", "Notification"] + "items": ["Notification", "Tag", "Tooltip"] }, "containment": { "name": "Containment", diff --git a/docs/app/preset/side-nav.json b/docs/app/preset/side-nav.json index 340c14ae..47f6aee0 100644 --- a/docs/app/preset/side-nav.json +++ b/docs/app/preset/side-nav.json @@ -20,14 +20,14 @@ "id": "2:a", "label": "Theme", "route": "/preset/theme", - "tag": "new", + "tag": "", "type": "route" }, { "id": "2:b", "label": "Colors", "route": "/preset/colors", - "tag": "new", + "tag": "next", "type": "route" }, { @@ -41,7 +41,7 @@ "id": "2:d", "label": "Animation", "route": "/preset/animation", - "tag": "new", + "tag": "", "type": "route" }, { diff --git a/docs/app/react/notification/components/messages.data.json b/docs/app/react/notification/components/messages.data.json index c00a80cf..b0e1e195 100644 --- a/docs/app/react/notification/components/messages.data.json +++ b/docs/app/react/notification/components/messages.data.json @@ -15,7 +15,7 @@ "palette": "warning", "id": "3", "heading": "Slow Network", - "description": "Your network is slow. Please check your connection." + "description": "Please check your connection." }, { "palette": "danger", diff --git a/docs/app/react/side-nav.json b/docs/app/react/side-nav.json index 08e9170a..82b2e378 100644 --- a/docs/app/react/side-nav.json +++ b/docs/app/react/side-nav.json @@ -1,203 +1,197 @@ [ { - "id": "1", "label": "Overview", "type": "heading" }, { - "id": "1:a", "label": "Getting Started", "route": "/react", "tag": "", "type": "route" }, { - "id": "2", - "label": "Components", + "label": "Actions", "type": "heading" }, { - "id": "2:a", "label": "Button", "route": "/react/button", "tag": "", "type": "route" }, { - "id": "2:b", "label": "Icon Button", "route": "/react/icon-button", "tag": "", "type": "route" }, { - "id": "2:c", - "label": "Label", - "route": "/react/label", + "label": "Communication", + "type": "heading" + }, + { + "label": "Notification", + "route": "/react/notification", + "tag": "next", + "type": "route" + }, + { + "label": "Tag", + "route": "/react/tags", "tag": "", "type": "route" }, { - "id": "2:d", - "label": "Input", - "route": "/react/input", + "label": "Tooltip", + "route": "/react/tooltip", "tag": "", "type": "route" }, { - "id": "2:e", - "label": "Field Message", - "route": "/react/field-message", + "label": "Containment", + "type": "heading" + }, + { + "label": "Confirm Modal", + "route": "/react/confirm-modal", "tag": "", "type": "route" }, { - "id": "2:f", - "label": "Nav Menu", - "route": "/react/nav-menu", + "label": "Prompt Modal", + "route": "/react/prompt-modal", "tag": "", "type": "route" }, { - "id": "2:g", - "label": "Radio", - "route": "/react/radio", + "label": "Modal", + "route": "/react/modal", + "tag": "", + "type": "route" + }, + { + "label": "Navigation", + "type": "heading" + }, + { + "label": "Nav Menu", + "route": "/react/nav-menu", "tag": "", "type": "route" }, { - "id": "2:h", "label": "Tabs", "route": "/react/tabs", - "tag": "new", + "tag": "", "type": "route" }, { - "id": "2:i", - "label": "Tag", - "route": "/react/tags", - "tag": "", + "label": "Selection", + "type": "heading" + }, + { + "label": "Drag & Drop", + "route": "/react/drag-n-drop", + "tag": "next", "type": "route" }, { - "id": "2:j", - "label": "Textarea", - "route": "/react/textarea", + "label": "Radio", + "route": "/react/radio", "tag": "", "type": "route" }, { - "id": "2:k", "label": "Toggle", "route": "/react/toggle", "tag": "", "type": "route" }, { - "id": "2:l", - "label": "Tooltip", - "route": "/react/tooltip", + "label": "Forms", + "type": "heading" + }, + { + "label": "Field Message", + "route": "/react/field-message", "tag": "", "type": "route" }, { - "id": "2:m", - "label": "Confirm Modal", - "route": "/react/confirm-modal", - "tag": "new", + "label": "File Uploader", + "route": "/react/file-uploader", + "tag": "next", "type": "route" }, { - "id": "2:n", - "label": "Prompt Modal", - "route": "/react/prompt-modal", - "tag": "new", + "label": "Input", + "route": "/react/input", + "tag": "", "type": "route" }, { - "id": "2:o", - "label": "Modal", - "route": "/react/modal", - "tag": "new", + "label": "Label", + "route": "/react/label", + "tag": "", "type": "route" }, { - "id": "2:p", - "label": "Notification", - "route": "/react/notification", - "tag": "next", + "label": "Textarea", + "route": "/react/textarea", + "tag": "", "type": "route" }, { - "id": "2:q", + "label": "Rendering", + "type": "heading" + }, + { "label": "Show", "route": "/react/show", "tag": "", "type": "route" }, { - "id": "2:qr", "label": "Portal", "route": "/react/portal", - "tag": "new", + "tag": "", "type": "route" }, { - "id": "2:r", "label": "Feature Flags", "route": "/react/feature-flags", - "tag": "new", - "type": "route" - }, - { - "id": "2:s", - "label": "File Uploader", - "route": "/react/file-uploader", - "tag": "next", - "type": "route" - }, - { - "id": "2:t", - "label": "Drag & Drop", - "route": "/react/drag-n-drop", - "tag": "next", + "tag": "", "type": "route" }, { - "id": "3", "label": "Hooks", "type": "heading" }, { - "id": "3:a", "label": "useModal", "route": "/react/use-modal", - "tag": "new", + "tag": "", "type": "route" }, { - "id": "3:b", "label": "useTheme", "route": "/react/use-theme", "tag": "", "type": "route" }, { - "id": "3:c", "label": "useThemeContext", "route": "/react/use-theme-context", "tag": "", "type": "route" }, { - "id": "3:d", "label": "useToggle", "route": "/react/use-toggle", "tag": "", "type": "route" }, { - "id": "4", "label": "Helpers", "type": "heading" }, @@ -205,7 +199,7 @@ "id": "4:a", "label": "trapFocus", "route": "/react/trap-focus", - "tag": "new", + "tag": "", "type": "route" } ] diff --git a/packages/panda-preset/src/recipes/slots/notification.ts b/packages/panda-preset/src/recipes/slots/notification.ts index 6fb8bfda..e4cac03a 100644 --- a/packages/panda-preset/src/recipes/slots/notification.ts +++ b/packages/panda-preset/src/recipes/slots/notification.ts @@ -7,6 +7,8 @@ import { WARNING, } from '../shared/palettes' import { focusStates } from '../shared/states' +import { iconButton } from '../iconButton' +import { button } from '../button' /** * This module contains the notification recipe. @@ -22,6 +24,7 @@ function getNotificationPalette( icon: statePalette, heading: statePalette, description: statePalette, + close: statePalette, } } @@ -33,7 +36,15 @@ function getNotificationPalette( export const notification: Partial = defineSlotRecipe({ className: 'notification', description: 'The styles for Notification components', - slots: ['center', 'dialog', 'icon', 'heading', 'description'], + slots: [ + 'center', + 'dialog', + 'icon', + 'heading', + 'description', + 'close', + 'closeAll', + ], jsx: [ 'NotificationCenter', 'Notification', @@ -45,14 +56,16 @@ export const notification: Partial = defineSlotRecipe({ base: { center: { - // combine with vstack + color: 'colorPalette.text.initial', position: 'fixed', right: '4', - top: '4', + textAlign: 'right', + top: '24', zIndex: 'toast', }, dialog: { bgColor: 'colorPalette.surface.initial', + color: 'colorPalette.text.initial', maxW: '29rem', minH: '3.125rem', opacity: '0', @@ -73,13 +86,13 @@ export const notification: Partial = defineSlotRecipe({ paddingInlineStart: '4', }, heading: { - color: 'colorPalette.text.initial', + color: 'inherit', textStyle: 'label-md', fontWeight: '600', userSelect: 'none', }, description: { - color: 'colorPalette.text.initial', + color: 'inherit', textStyle: 'body-sm', userSelect: 'none', ['& :is(a)']: { @@ -88,6 +101,25 @@ export const notification: Partial = defineSlotRecipe({ ...focusStates, }, }, + close: { + ...iconButton.base, + bgColor: 'transparent', + color: 'inherit', + _hover: { + bgColor: 'colorPalette.bg.hover', + }, + }, + closeAll: { + ...button.base, + bgColor: 'transparent', + color: 'action.text.inverse', + height: '2.75rem', + pxi: '0', + _hover: { + bgColor: 'transparent', + color: 'action.bg.hover', + }, + }, }, variants: { @@ -95,7 +127,15 @@ export const notification: Partial = defineSlotRecipe({ [INFO]: getNotificationPalette(INFO), [SUCCESS]: getNotificationPalette(SUCCESS), [WARNING]: getNotificationPalette(WARNING), - [DANGER]: getNotificationPalette(DANGER), + [DANGER]: { + ...getNotificationPalette(DANGER), + close: { + ...getNotificationPalette(DANGER).close, + _hover: { + color: 'danger.text.inverse', + }, + }, + }, }, }, diff --git a/packages/panda-preset/src/theme/semantic-tokens/info.ts b/packages/panda-preset/src/theme/semantic-tokens/info.ts index 59db1683..253c36fa 100644 --- a/packages/panda-preset/src/theme/semantic-tokens/info.ts +++ b/packages/panda-preset/src/theme/semantic-tokens/info.ts @@ -1,5 +1,6 @@ import type { Prominence, SemanticToken } from './types' -import { colors, deepGetByPaths, rawTokens } from '../../tokens' +import { colors, deepGetByPaths, rawTokens, semanticColors } from '../../tokens' +import { INFO } from '../../recipes/shared/palettes' /** * This module is a collection of info tokens that are used to generate the theme. @@ -15,6 +16,8 @@ export interface InfoTokens { } readonly bg: { readonly initial: SemanticToken + readonly hover: SemanticToken + readonly active: SemanticToken } readonly surface: { readonly initial: SemanticToken @@ -31,7 +34,9 @@ export const infoTokens: InfoTokens = { info: { border: { initial: { - description: 'The default border color of informational elements.', + description: + semanticColors.border[INFO].initial.$description || + 'The default border color of informational elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -53,7 +58,9 @@ export const infoTokens: InfoTokens = { bg: { initial: { - description: 'The default background color of informational elements.', + description: + semanticColors.background[INFO].initial.$description || + 'The default background color of informational elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -71,11 +78,54 @@ export const infoTokens: InfoTokens = { }, }, }, + hover: { + description: + semanticColors.background[INFO].hover.$description || + 'The hover background color of informational elements.', + value: { + _cerberusTheme: { + base: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.info.hover.$value, + ).$value, + _lightMode: deepGetByPaths( + colors, + rawTokens.semanticColors.light.background.info.hover.$value, + ).$value, + _darkMode: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.info.hover.$value, + ).$value, + }, + }, + }, + active: { + description: + semanticColors.background[INFO].active.$description || + 'The active background color of informational elements.', + value: { + _cerberusTheme: { + base: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.info.active.$value, + ).$value, + _lightMode: deepGetByPaths( + colors, + rawTokens.semanticColors.light.background.info.active.$value, + ).$value, + _darkMode: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.info.active.$value, + ).$value, + }, + }, + }, }, surface: { initial: { description: + semanticColors.surface[INFO].initial.$description || 'The default color for a layout-based surface element (like a page or card) in a informational state.', value: { _cerberusTheme: { @@ -96,6 +146,7 @@ export const infoTokens: InfoTokens = { }, 100: { description: + semanticColors.surface[INFO]['100'].$description || 'The second layer of color for static (surface) elements that display a info state - used on top of initial.', value: { _cerberusTheme: { @@ -116,6 +167,7 @@ export const infoTokens: InfoTokens = { }, 200: { description: + semanticColors.surface[INFO]['200'].$description || 'The third layer of color for static (surface) elements that display a info state - used on top of 100.', value: { _cerberusTheme: { @@ -138,7 +190,9 @@ export const infoTokens: InfoTokens = { text: { initial: { - description: 'The default text color of informational elements.', + description: + semanticColors.text[INFO].initial.$description || + 'The default text color of informational elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -157,7 +211,9 @@ export const infoTokens: InfoTokens = { }, }, 100: { - description: 'The secondary text color of informational elements.', + description: + semanticColors.text[INFO]['100'].$description || + 'The secondary text color of informational elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -176,7 +232,9 @@ export const infoTokens: InfoTokens = { }, }, 200: { - description: 'The tertiary text color of informational elements.', + description: + semanticColors.text[INFO]['200'].$description || + 'The tertiary text color of informational elements.', value: { _cerberusTheme: { base: deepGetByPaths( diff --git a/packages/panda-preset/src/theme/semantic-tokens/warning.ts b/packages/panda-preset/src/theme/semantic-tokens/warning.ts index 3f5ff889..2351d03c 100644 --- a/packages/panda-preset/src/theme/semantic-tokens/warning.ts +++ b/packages/panda-preset/src/theme/semantic-tokens/warning.ts @@ -1,5 +1,6 @@ import type { Prominence, SemanticToken } from './types' -import { colors, deepGetByPaths, rawTokens } from '../../tokens' +import { colors, deepGetByPaths, rawTokens, semanticColors } from '../../tokens' +import { WARNING } from '../../recipes/shared/palettes' /** * This module is a collection of warning tokens that are used to generate the theme. @@ -13,6 +14,11 @@ export interface WarningTokens { readonly border: { readonly initial: SemanticToken } + readonly bg: { + readonly initial: SemanticToken + readonly hover: SemanticToken + readonly active: SemanticToken + } readonly surface: { readonly initial: SemanticToken readonly 100: SemanticToken @@ -28,7 +34,9 @@ export const warningTokens: WarningTokens = { warning: { border: { initial: { - description: 'The default border color of warning elements.', + description: + semanticColors.border[WARNING].initial.$description || + 'The default border color of warning elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -48,9 +56,76 @@ export const warningTokens: WarningTokens = { }, }, + bg: { + initial: { + description: + semanticColors.background[WARNING].initial.$description || + 'The default background color of warning elements.', + value: { + _cerberusTheme: { + base: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.warning.initial.$value, + ).$value, + _lightMode: deepGetByPaths( + colors, + rawTokens.semanticColors.light.background.warning.initial.$value, + ).$value, + _darkMode: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.warning.initial.$value, + ).$value, + }, + }, + }, + hover: { + description: + semanticColors.background[WARNING].hover.$description || + 'The hover background color of warning elements.', + value: { + _cerberusTheme: { + base: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.warning.hover.$value, + ).$value, + _lightMode: deepGetByPaths( + colors, + rawTokens.semanticColors.light.background.warning.hover.$value, + ).$value, + _darkMode: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.warning.hover.$value, + ).$value, + }, + }, + }, + active: { + description: + semanticColors.background[WARNING].active.$description || + 'The active background color of warning elements.', + value: { + _cerberusTheme: { + base: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.warning.active.$value, + ).$value, + _lightMode: deepGetByPaths( + colors, + rawTokens.semanticColors.light.background.warning.active.$value, + ).$value, + _darkMode: deepGetByPaths( + colors, + rawTokens.semanticColors.dark.background.warning.active.$value, + ).$value, + }, + }, + }, + }, + surface: { initial: { description: + semanticColors.surface[WARNING].initial.$description || 'The default color for static (surface) elements that display a warning state.', value: { _cerberusTheme: { @@ -71,6 +146,7 @@ export const warningTokens: WarningTokens = { }, 100: { description: + semanticColors.surface[WARNING]['100'].$description || 'The second layer of color for static (surface) elements that display a warning state - used on top of initial.', value: { _cerberusTheme: { @@ -91,6 +167,7 @@ export const warningTokens: WarningTokens = { }, 200: { description: + semanticColors.surface[WARNING]['200'].$description || 'The third layer of color for static (surface) elements that display a warning state - used on top of 100.', value: { _cerberusTheme: { @@ -113,7 +190,9 @@ export const warningTokens: WarningTokens = { text: { initial: { - description: 'The default text color of warning elements.', + description: + semanticColors.text[WARNING].initial.$description || + 'The default text color of warning elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -132,7 +211,9 @@ export const warningTokens: WarningTokens = { }, }, 100: { - description: 'The secondary text color of warning elements.', + description: + semanticColors.text[WARNING]['100'].$description || + 'The secondary text color of warning elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -151,7 +232,9 @@ export const warningTokens: WarningTokens = { }, }, 200: { - description: 'The tertiary text color of warning elements.', + description: + semanticColors.text[WARNING]['200'].$description || + 'The tertiary text color of warning elements.', value: { _cerberusTheme: { base: deepGetByPaths( @@ -171,6 +254,7 @@ export const warningTokens: WarningTokens = { }, inverse: { description: + semanticColors.text[WARNING].inverse.$description || 'The inverse version of the default text color of warning elements.', value: { _cerberusTheme: { diff --git a/packages/panda-preset/src/tokens/semantic-colors.dark-mode.json b/packages/panda-preset/src/tokens/semantic-colors.dark-mode.json index 3eb8c7e5..68068542 100644 --- a/packages/panda-preset/src/tokens/semantic-colors.dark-mode.json +++ b/packages/panda-preset/src/tokens/semantic-colors.dark-mode.json @@ -1103,6 +1103,44 @@ } } } + }, + "warning": { + "initial": { + "$type": "color", + "$value": "colors.warning.70", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["FRAME_FILL", "SHAPE_FILL"], + "codeSyntax": {} + } + } + }, + "hover": { + "$type": "color", + "$value": "colors.warning.60", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["FRAME_FILL", "SHAPE_FILL"], + "codeSyntax": {} + } + } + }, + "active": { + "$type": "color", + "$value": "colors.warning.40", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["FRAME_FILL", "SHAPE_FILL"], + "codeSyntax": {} + } + } + } } }, "ghost": { @@ -1259,6 +1297,22 @@ } } }, + "backdrop": { + "page": { + "initial": { + "$type": "color", + "$value": "#130024bf", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["SHAPE_FILL"], + "codeSyntax": {} + } + } + } + } + }, "WIP-data-viz": { "data-1": { "$type": "color", @@ -1309,22 +1363,6 @@ } } }, - "backdrop": { - "page": { - "initial": { - "$type": "color", - "$value": "#130024bf", - "$description": "", - "$extensions": { - "com.figma": { - "hiddenFromPublishing": false, - "scopes": ["SHAPE_FILL"], - "codeSyntax": {} - } - } - } - } - }, "drop-shadow": { "sm": { "$type": "color", diff --git a/packages/panda-preset/src/tokens/semantic-colors.light-mode.json b/packages/panda-preset/src/tokens/semantic-colors.light-mode.json index 3525e23d..953a9644 100644 --- a/packages/panda-preset/src/tokens/semantic-colors.light-mode.json +++ b/packages/panda-preset/src/tokens/semantic-colors.light-mode.json @@ -1103,6 +1103,44 @@ } } } + }, + "warning": { + "initial": { + "$type": "color", + "$value": "colors.warning.50", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["FRAME_FILL", "SHAPE_FILL"], + "codeSyntax": {} + } + } + }, + "hover": { + "$type": "color", + "$value": "colors.warning.60", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["FRAME_FILL", "SHAPE_FILL"], + "codeSyntax": {} + } + } + }, + "active": { + "$type": "color", + "$value": "colors.warning.70", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["FRAME_FILL", "SHAPE_FILL"], + "codeSyntax": {} + } + } + } } }, "ghost": { @@ -1259,6 +1297,22 @@ } } }, + "backdrop": { + "page": { + "initial": { + "$type": "color", + "$value": "#bcbaca80", + "$description": "", + "$extensions": { + "com.figma": { + "hiddenFromPublishing": false, + "scopes": ["SHAPE_FILL"], + "codeSyntax": {} + } + } + } + } + }, "WIP-data-viz": { "data-1": { "$type": "color", @@ -1309,22 +1363,6 @@ } } }, - "backdrop": { - "page": { - "initial": { - "$type": "color", - "$value": "#bcbaca80", - "$description": "", - "$extensions": { - "com.figma": { - "hiddenFromPublishing": false, - "scopes": ["SHAPE_FILL"], - "codeSyntax": {} - } - } - } - } - }, "drop-shadow": { "sm": { "$type": "color", diff --git a/packages/react/src/components/Notification.tsx b/packages/react/src/components/Notification.tsx index 407ed40c..f871e0f2 100644 --- a/packages/react/src/components/Notification.tsx +++ b/packages/react/src/components/Notification.tsx @@ -12,7 +12,6 @@ import { type PropsWithChildren, type MouseEvent, } from 'react' -import { IconButton } from './IconButton' import { Close } from '@cerberus/icons' import { $cerberusIcons } from '../config/defineIcons' import type { IconType } from '../config/cerbIcons' @@ -86,9 +85,14 @@ export function Notification(props: PropsWithChildren) { {children} - + ) } diff --git a/packages/react/src/context/notification-center.tsx b/packages/react/src/context/notification-center.tsx index bb0ecfd4..333a81ad 100644 --- a/packages/react/src/context/notification-center.tsx +++ b/packages/react/src/context/notification-center.tsx @@ -14,10 +14,11 @@ import { Show } from '../components/Show' import { NotificationHeading } from '../components/NotificationHeading' import { NotificationDescription } from '../components/NotificationDescription' import { Notification } from '../components/Notification' -import { vstack } from '@cerberus-design/styled-system/patterns' +import { animateIn, vstack } from '@cerberus-design/styled-system/patterns' import { Portal, type PortalProps } from '../components/Portal' -import { cx } from '@cerberus-design/styled-system/css' import { notification } from '@cerberus-design/styled-system/recipes' +import { Button } from '../components/Button' +import { cx } from '@cerberus-design/styled-system/css' /** * This module provides a context and hook for notifications. @@ -67,6 +68,7 @@ export function NotificationCenter( const [activeNotifications, setActiveNotifications] = useState< NotifyOptions[] >([]) + const styles = notification() const handleNotify = useCallback((options: NotifyOptions) => { setActiveNotifications((prev) => { @@ -84,6 +86,15 @@ export function NotificationCenter( }) }, []) + const handleCloseAll = useCallback(() => { + setActiveNotifications((prev) => { + prev.forEach((item) => { + if (item.onClose) item.onClose() + }) + return [] + }) + }, []) + const value = useMemo( () => ({ notify: handleNotify, @@ -100,34 +111,45 @@ export function NotificationCenter( 0}> -
    + = 4}> + + +
    - {activeNotifications.map((option) => ( - - - {option.heading} - - - {option.description} - - - ))} + })} + style={{ + alignItems: 'flex-end', + }} + > + {activeNotifications.map((option) => ( + + + {option.heading} + + + {option.description} + + + ))} +
    diff --git a/tests/panda-preset/recipes/slots/notification.test.ts b/tests/panda-preset/recipes/slots/notification.test.ts index 71265a7d..cbd08c54 100644 --- a/tests/panda-preset/recipes/slots/notification.test.ts +++ b/tests/panda-preset/recipes/slots/notification.test.ts @@ -10,9 +10,11 @@ describe('notification recipe', () => { test('should have a center style', () => { expect(notification.base?.center).toMatchObject({ + color: 'colorPalette.text.initial', position: 'fixed', right: '4', - top: '4', + textAlign: 'right', + top: '24', zIndex: 'toast', }) }) @@ -20,6 +22,7 @@ describe('notification recipe', () => { test('should have a base style', () => { expect(notification.base?.dialog).toMatchObject({ bgColor: 'colorPalette.surface.initial', + color: 'colorPalette.text.initial', maxW: '29rem', minH: '3.125rem', opacity: '0', @@ -46,7 +49,7 @@ describe('notification recipe', () => { test('should have a heading style', () => { expect(notification.base?.heading).toMatchObject({ - color: 'colorPalette.text.initial', + color: 'inherit', textStyle: 'label-md', fontWeight: '600', userSelect: 'none', @@ -55,7 +58,7 @@ describe('notification recipe', () => { test('should have a description style', () => { expect(notification.base?.description).toMatchObject({ - color: 'colorPalette.text.initial', + color: 'inherit', textStyle: 'body-sm', userSelect: 'none', ['& :is(a)']: { @@ -65,6 +68,28 @@ describe('notification recipe', () => { }) }) + test('should have a close style', () => { + expect(notification.base?.close).toMatchObject({ + bgColor: 'transparent', + color: 'inherit', + _hover: { + bgColor: 'colorPalette.bg.hover', + }, + }) + }) + + test('should have a close all style', () => { + expect(notification.base?.closeAll).toMatchObject({ + bgColor: 'transparent', + color: 'action.text.inverse', + pxi: '0', + _hover: { + bgColor: 'transparent', + color: 'action.bg.hover', + }, + }) + }) + test('should had a default variant', () => { expect(notification.defaultVariants).toMatchObject({ palette: 'info', diff --git a/tests/panda-preset/theme/semantic-tokens/info.test.ts b/tests/panda-preset/theme/semantic-tokens/info.test.ts index 6ad5b6b8..e40464c8 100644 --- a/tests/panda-preset/theme/semantic-tokens/info.test.ts +++ b/tests/panda-preset/theme/semantic-tokens/info.test.ts @@ -46,6 +46,30 @@ describe('infoTokens', () => { ).toEqual('#9ACFEE') }) + test('should have a bg.hover property', () => { + expect( + formatToken(infoTokens.info.bg.hover.value._cerberusTheme.base), + ).toEqual('#013655') + expect( + formatToken(infoTokens.info.bg.hover.value._cerberusTheme._darkMode), + ).toEqual('#013655') + expect( + formatToken(infoTokens.info.bg.hover.value._cerberusTheme._lightMode), + ).toEqual('#CCE7F7') + }) + + test('should have a bg.active property', () => { + expect( + formatToken(infoTokens.info.bg.active.value._cerberusTheme.base), + ).toEqual('#35A0DD') + expect( + formatToken(infoTokens.info.bg.active.value._cerberusTheme._darkMode), + ).toEqual('#35A0DD') + expect( + formatToken(infoTokens.info.bg.active.value._cerberusTheme._lightMode), + ).toEqual('#35A0DD') + }) + test('should have a surface.initial property', () => { expect(infoTokens.info.surface).toBeDefined() }) diff --git a/tests/panda-preset/theme/semantic-tokens/warning.test.ts b/tests/panda-preset/theme/semantic-tokens/warning.test.ts index 3065b099..7bb92941 100644 --- a/tests/panda-preset/theme/semantic-tokens/warning.test.ts +++ b/tests/panda-preset/theme/semantic-tokens/warning.test.ts @@ -28,6 +28,54 @@ describe('warning', () => { ).toEqual('#F4DA49') }) + test('should have a bg.initial property', () => { + expect( + formatToken(warningTokens.warning.bg.initial.value._cerberusTheme.base), + ).toEqual('#C1A716') + expect( + formatToken( + warningTokens.warning.bg.initial.value._cerberusTheme._darkMode, + ), + ).toEqual('#C1A716') + expect( + formatToken( + warningTokens.warning.bg.initial.value._cerberusTheme._lightMode, + ), + ).toEqual('#F4DA49') + }) + + test('should have a bg.hover property', () => { + expect( + formatToken(warningTokens.warning.bg.hover.value._cerberusTheme.base), + ).toEqual('#F1D11B') + expect( + formatToken( + warningTokens.warning.bg.hover.value._cerberusTheme._darkMode, + ), + ).toEqual('#F1D11B') + expect( + formatToken( + warningTokens.warning.bg.hover.value._cerberusTheme._lightMode, + ), + ).toEqual('#F1D11B') + }) + + test('should have a bg.active property', () => { + expect( + formatToken(warningTokens.warning.bg.active.value._cerberusTheme.base), + ).toEqual('#F7E376') + expect( + formatToken( + warningTokens.warning.bg.active.value._cerberusTheme._darkMode, + ), + ).toEqual('#F7E376') + expect( + formatToken( + warningTokens.warning.bg.active.value._cerberusTheme._lightMode, + ), + ).toEqual('#C1A716') + }) + test('should have a surface.initial property', () => { expect( formatToken(