diff --git a/documentation-site/examples/badge/hint-dot.tsx b/documentation-site/examples/badge/hint-dot.tsx
deleted file mode 100644
index 31699b3575..0000000000
--- a/documentation-site/examples/badge/hint-dot.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import * as React from "react";
-import { HintDot, COLOR } from "baseui/badge";
-import { Skeleton } from "baseui/skeleton";
-
-export default function Example() {
- return (
-
-
-
- );
-}
diff --git a/documentation-site/examples/badge/notification-circle.tsx b/documentation-site/examples/badge/notification-circle.tsx
deleted file mode 100644
index 8d87b10a7f..0000000000
--- a/documentation-site/examples/badge/notification-circle.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import * as React from "react";
-import { NotificationCircle, COLOR } from "baseui/badge";
-import { Skeleton } from "baseui/skeleton";
-
-export default function Example() {
- return (
-
-
-
- );
-}
diff --git a/documentation-site/pages/components/badge.mdx b/documentation-site/pages/components/badge.mdx
index e9872db420..33b1edf5d5 100644
--- a/documentation-site/pages/components/badge.mdx
+++ b/documentation-site/pages/components/badge.mdx
@@ -11,8 +11,6 @@ import badgeYardConfig from "../../components/yard/config/badge";
import PrimaryInline from "examples/badge/primary-inline.tsx";
import SecondaryInline from "examples/badge/secondary-inline.tsx";
import Offset from "examples/badge/offset.tsx";
-import NotificationCircleExample from "examples/badge/notification-circle.tsx";
-import HintDotExample from "examples/badge/hint-dot.tsx";
export default Layout;
@@ -26,7 +24,7 @@ Badge content should generally be 3 words or less.
## Hierarchy
-Primary badges are bright in color to grab a user's attention to an entry point of either a new product/feature, promotion or alert. Another usage is for transit lines. Avoid using multiple primary badges in the same view.
+Primary badges are bright in color to grab a user’s attention to an entry point of either a new product/feature, promotion or alert. Another usage is for transit lines. Avoid using multiple primary badges in the same view.
Secondary badges should be part of the content inside the component. They are often meta data, highlighted information or simplified information.
@@ -55,24 +53,4 @@ There may be situations where it makes sense to deviate from the standard badge
- `horizontalOffset` sets the `right` CSS attribute when `placement` is `topRight` or `bottomRight`. Otherwise it sets the `left` attribute.
- `verticalOffset` sets the `top` CSS attribute when `placement` is `topLeft`, `top`, or `topRight`. Otherwise it sets the `bottom` attribute.
-## NotificationCircle
-
-Use `NotificationCircle` to display a count or icon indicator anchored to an element — for example, an unread message count on a navigation icon.
-
-
-
-
-
-The `content` prop accepts a number, an icon element, or a render prop `(size: number) => ReactNode`. Numbers greater than 99 are automatically clamped to `99+`. Use the `size` prop (`small` or `medium`, defaults to `medium`) to control the circle dimensions. `NotificationCircle` supports `topLeft`, `topRight`, `bottomLeft`, and `bottomRight` placements.
-
-## HintDot
-
-Use `HintDot` to render a small colored dot anchored to an element, drawing attention without conveying specific information.
-
-
-
-
-
-The dot supports `topRight`, `topLeft`, `bottomRight`, and `bottomLeft` placements (defaults to `topRight`). By default a border separates the dot from its anchor; set `hasBorder={false}` to remove it. When no `children` are provided the dot renders inline without an anchor.
-
diff --git a/src/badge/__tests__/utils.test.tsx b/src/badge/__tests__/utils.test.tsx
deleted file mode 100644
index c8aee2ac53..0000000000
--- a/src/badge/__tests__/utils.test.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import { getAnchorFromChildren } from '../utils';
-
-describe('getAnchorFromChildren', () => {
- it('returns undefined when no children are provided', () => {
- expect(getAnchorFromChildren()).toBeUndefined();
- });
-
- it('returns the child with id and role when one child is provided', () => {
- const child = (
-
- Child
-
- );
- const result = getAnchorFromChildren(child) as React.ReactElement;
- expect(result.type).toBe(child.type);
- expect(result.props.id).toBe('test-id');
- expect(result.props.role).toBe('button');
- });
-
- it('logs an error and returns the first child when multiple children are provided', () => {
- const child1 = (
-
- Child
-
- );
- const child2 = (
-
- Child
-
- );
- console.error = jest.fn();
-
- const result = getAnchorFromChildren([child1, child2]) as React.ReactElement;
- expect(result.type).toBe(child1.type);
- expect(result.props.id).toBe('test-id');
- expect(result.props.role).toBe('button');
- expect(console.error).toHaveBeenCalledWith(
- `[baseui] No more than 1 child may be passed to Badge, found 2 children`
- );
- });
-});
diff --git a/src/badge/constants.ts b/src/badge/constants.ts
index d7f3d0eb2c..e3f3a5fb73 100644
--- a/src/badge/constants.ts
+++ b/src/badge/constants.ts
@@ -9,11 +9,6 @@ export const HIERARCHY = Object.freeze({
secondary: 'secondary',
});
-export const NOTIFICATION_CIRCLE_SIZE = {
- small: 'small',
- medium: 'medium',
-} as const;
-
export const SHAPE = Object.freeze({
pill: 'pill',
rectangle: 'rectangle',
@@ -21,11 +16,10 @@ export const SHAPE = Object.freeze({
export const COLOR = Object.freeze({
accent: 'accent',
- primary: 'primary', // deprecated
+ primary: 'primary',
positive: 'positive',
negative: 'negative',
warning: 'warning',
- onBrand: 'onBrand',
});
export const PLACEMENT = Object.freeze({
diff --git a/src/badge/hint-dot.tsx b/src/badge/hint-dot.tsx
index c6c4592e95..aec1991a40 100644
--- a/src/badge/hint-dot.tsx
+++ b/src/badge/hint-dot.tsx
@@ -5,6 +5,7 @@ This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
import * as React from 'react';
+import { useStyletron } from '../styles/index';
import { getOverrides } from '../helpers/overrides';
import { StyledHintDot, StyledRoot, StyledPositioner } from './styled-components';
import type { HintDotProps } from './types';
@@ -17,15 +18,14 @@ const HintDot = ({
horizontalOffset: horizontalOffsetProp,
verticalOffset: verticalOffsetProp,
hidden,
- // placement was not there for hintBadge, but we need to support in for Avatar and other potential use cases.
- placement = PLACEMENT.topRight,
- hasBorder = true,
overrides = {},
}: HintDotProps) => {
const [HintDot, hintDotProps] = getOverrides(overrides.Badge, StyledHintDot);
const [Root, rootProps] = getOverrides(overrides.Root, StyledRoot);
const [Positioner, positionerProps] = getOverrides(overrides.Positioner, StyledPositioner);
+ const [, theme] = useStyletron();
+
const anchor = getAnchorFromChildren(children);
// if the anchor is a string, we supply default offsets
@@ -39,28 +39,22 @@ const HintDot = ({
verticalOffset = '-4px';
}
}
-
return (
{anchor}
diff --git a/src/badge/index.ts b/src/badge/index.ts
index c11d729711..057934af8a 100644
--- a/src/badge/index.ts
+++ b/src/badge/index.ts
@@ -19,7 +19,7 @@ export { default as Badge } from './badge';
export { default as NotificationCircle } from './notification-circle';
export { default as HintDot } from './hint-dot';
-export { HIERARCHY, SHAPE, COLOR, PLACEMENT, NOTIFICATION_CIRCLE_SIZE } from './constants';
+export { HIERARCHY, SHAPE, COLOR, PLACEMENT } from './constants';
export * from './styled-components';
diff --git a/src/badge/notification-circle.tsx b/src/badge/notification-circle.tsx
index 41ac40f7e3..4e9047b4d2 100644
--- a/src/badge/notification-circle.tsx
+++ b/src/badge/notification-circle.tsx
@@ -8,7 +8,7 @@ import * as React from 'react';
import { getOverrides } from '../helpers/overrides';
import { StyledNotificationCircle, StyledRoot, StyledPositioner } from './styled-components';
import type { NotificationCircleProps } from './types';
-import { PLACEMENT, ROLE, NOTIFICATION_CIRCLE_SIZE } from './constants';
+import { PLACEMENT, ROLE } from './constants';
import { getAnchorFromChildren } from './utils';
const NotificationCircle = ({
@@ -19,7 +19,6 @@ const NotificationCircle = ({
horizontalOffset,
verticalOffset,
hidden,
- size = NOTIFICATION_CIRCLE_SIZE.medium,
overrides = {},
}: NotificationCircleProps) => {
const [NotificationCircle, NotificationCircleProps] = getOverrides(
@@ -35,48 +34,22 @@ const NotificationCircle = ({
if (typeof contentProp === 'string') {
console.error(`[baseui] NotificationCircle child must be number or icon, found string`);
}
- if (
- placement &&
- placement !== PLACEMENT.topLeft &&
- placement !== PLACEMENT.topRight &&
- placement !== PLACEMENT.bottomLeft &&
- placement !== PLACEMENT.bottomRight
- ) {
+ if (placement && placement !== PLACEMENT.topLeft && placement !== PLACEMENT.topRight) {
console.error(
- `[baseui] NotificationCircle must be placed topLeft, topRight, bottomLeft, or bottomRight, found ${placement}`
+ `[baseui] NotificationCircle must be placed topLeft or topRight, found ${placement}`
);
}
}
let content = contentProp;
- const ICON_SIZE = size === NOTIFICATION_CIRCLE_SIZE.small ? 10 : 12;
- const isContentNumber = typeof content === 'number';
- if (typeof content === 'number' && content > 99) {
- content = '99+';
- } else if (typeof content === 'function') {
- // add support for render prop, content = (size) =>
- content = content(ICON_SIZE);
- } else if (React.isValidElement(content)) {
- // backwards compatibility for icon element as child, clone the element and pass size as prop
- // content =
- // React.cloneElement is not recommended but we need this to support the old way of passing icon element as content
- content = React.cloneElement(content as React.ReactElement<{ size?: number }>, {
- size: ICON_SIZE,
- });
+ if (typeof content === 'number' && content > 9) {
+ content = '9+';
}
// If there's no anchor, render the badge inline
if (!anchor) {
return (
-
+
{content}
);
@@ -91,17 +64,9 @@ const NotificationCircle = ({
$verticalOffset={verticalOffset}
$placement={placement}
$role={ROLE.notificationCircle}
- aria-hidden={true}
{...positionerProps}
>
-
+
{content}
diff --git a/src/badge/styled-components.ts b/src/badge/styled-components.ts
index 376a79ae0c..e46341438e 100644
--- a/src/badge/styled-components.ts
+++ b/src/badge/styled-components.ts
@@ -5,9 +5,8 @@ This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
import { styled } from '../styles';
-import type { Theme } from '../styles';
-import type { Placement, Color, Shape, Role, Hierarchy, NotificationCircleSize } from './types';
-import { COLOR, SHAPE, ROLE, PLACEMENT, HIERARCHY, NOTIFICATION_CIRCLE_SIZE } from './constants';
+import type { Placement, Color, Shape, Role, Hierarchy } from './types';
+import { COLOR, SHAPE, ROLE, PLACEMENT, HIERARCHY } from './constants';
// @ts-ignore
function getColorStyles({ $theme, $hierarchy, $color }): {
@@ -36,10 +35,6 @@ function getColorStyles({ $theme, $hierarchy, $color }): {
color: $theme.colors.contentOnColorInverse,
backgroundColor: $theme.colors.backgroundWarning,
},
- [COLOR.onBrand]: {
- color: $theme.colors.brandBackgroundPrimary,
- backgroundColor: $theme.colors.brandContentOnPrimary,
- },
},
[HIERARCHY.secondary]: {
[COLOR.accent]: {
@@ -69,168 +64,119 @@ function getColorStyles({ $theme, $hierarchy, $color }): {
return COLOR_STYLES[$hierarchy][$color];
}
-function getPositionStyles($theme: Theme) {
- const defaultNotificationCirclePlacement = {
- insetBlockStart: `-${$theme.sizing.scale400}`,
- insetInlineEnd: `-${$theme.sizing.scale400}`,
- };
- const defaultHintDotPlacement = {
- insetBlockStart: `-${$theme.sizing.scale100}`,
- insetInlineEnd: `-${$theme.sizing.scale100}`,
- };
- return {
- [ROLE.badge]: {
- [PLACEMENT.topEdge]: {
- insetBlockStart: `-${$theme.sizing.scale300}`,
- left: '50%', // special case not using insetInlineStart since there is a transform to move left by 50% of its own width
- transform: 'translateX(-50%)',
- },
- [PLACEMENT.bottomEdge]: {
- insetBlockEnd: `-${$theme.sizing.scale300}`,
- left: '50%', // special case not using insetInlineStart since there is a transform to move left by 50% of its own width
- transform: 'translateX(-50%)',
- },
- [PLACEMENT.topLeft]: {
- insetBlockStart: $theme.sizing.scale600,
- insetInlineStart: $theme.sizing.scale600,
- },
- [PLACEMENT.topRight]: {
- insetBlockStart: $theme.sizing.scale600,
- insetInlineEnd: $theme.sizing.scale600,
- },
- [PLACEMENT.bottomRight]: {
- insetBlockEnd: $theme.sizing.scale600,
- insetInlineEnd: $theme.sizing.scale600,
- },
- [PLACEMENT.bottomLeft]: {
- insetBlockEnd: $theme.sizing.scale600,
- insetInlineStart: $theme.sizing.scale600,
- },
- [PLACEMENT.topLeftEdge]: {
- insetBlockStart: `-${$theme.sizing.scale300}`,
- insetInlineStart: $theme.sizing.scale600,
- },
- [PLACEMENT.topRightEdge]: {
- insetBlockStart: `-${$theme.sizing.scale300}`,
- insetInlineEnd: $theme.sizing.scale600,
- },
- [PLACEMENT.bottomRightEdge]: {
- insetBlockEnd: `-${$theme.sizing.scale300}`,
- insetInlineEnd: $theme.sizing.scale600,
- },
- [PLACEMENT.bottomLeftEdge]: {
- insetBlockEnd: `-${$theme.sizing.scale300}`,
- insetInlineStart: $theme.sizing.scale600,
- },
- [PLACEMENT.leftTopEdge]: {
- insetBlockStart: $theme.sizing.scale600,
- insetInlineStart: `-${$theme.sizing.scale300}`,
- },
- [PLACEMENT.rightTopEdge]: {
- insetBlockStart: $theme.sizing.scale600,
- insetInlineEnd: `-${$theme.sizing.scale300}`,
- },
- [PLACEMENT.rightBottomEdge]: {
- insetBlockEnd: $theme.sizing.scale600,
- insetInlineEnd: `-${$theme.sizing.scale300}`,
- },
- [PLACEMENT.leftBottomEdge]: {
- insetBlockEnd: $theme.sizing.scale600,
- insetInlineStart: `-${$theme.sizing.scale300}`,
- },
+const DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT = {
+ top: '-10px',
+ right: '-10px',
+};
+
+const DEFAULT_HINT_DOT_PLACEMENT = {
+ top: '-4px',
+ right: '-4px',
+};
+
+const POSITION_STYLES = Object.freeze({
+ [ROLE.badge]: {
+ [PLACEMENT.topEdge]: {
+ top: '-8px',
+ left: '50%',
+ transform: 'translateX(-50%)',
},
- [ROLE.notificationCircle]: {
- [PLACEMENT.topLeft]: {
- insetBlockStart: `-${$theme.sizing.scale400}`,
- insetInlineStart: `-${$theme.sizing.scale400}`,
- },
- [PLACEMENT.topRight]: defaultNotificationCirclePlacement,
- [PLACEMENT.bottomRight]: {
- insetBlockEnd: `-${$theme.sizing.scale400}`,
- insetInlineEnd: `-${$theme.sizing.scale400}`,
- },
- [PLACEMENT.bottomLeft]: {
- insetBlockEnd: `-${$theme.sizing.scale400}`,
- insetInlineStart: `-${$theme.sizing.scale400}`,
- },
- // NotificationCircle can only be placed topLeft, topRight, bottomLeft, or bottomRight, other values fall back to topRight
- [PLACEMENT.topEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.bottomEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.topLeftEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.topRightEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.bottomRightEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.bottomLeftEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.leftTopEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.rightTopEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.rightBottomEdge]: defaultNotificationCirclePlacement,
- [PLACEMENT.leftBottomEdge]: defaultNotificationCirclePlacement,
+ [PLACEMENT.bottomEdge]: {
+ bottom: '-8px',
+ left: '50%',
+ transform: 'translateX(-50%)',
},
- [ROLE.hintDot]: {
- // For now, we only need to support topLeft, topRight, bottomLeft and bottomRight. Others are considered as an invalid placement prop.
- [PLACEMENT.topLeft]: {
- insetBlockStart: `-${$theme.sizing.scale100}`,
- insetInlineStart: `-${$theme.sizing.scale100}`,
- },
- [PLACEMENT.topRight]: defaultHintDotPlacement,
- [PLACEMENT.bottomRight]: {
- insetBlockEnd: `-${$theme.sizing.scale100}`,
- insetInlineEnd: `-${$theme.sizing.scale100}`,
- },
- [PLACEMENT.bottomLeft]: {
- insetBlockEnd: `-${$theme.sizing.scale100}`,
- insetInlineStart: `-${$theme.sizing.scale100}`,
- },
- // unsupported placements fall back to topRight
- [PLACEMENT.topEdge]: defaultHintDotPlacement,
- [PLACEMENT.topLeftEdge]: defaultHintDotPlacement,
- [PLACEMENT.topRightEdge]: defaultHintDotPlacement,
- [PLACEMENT.bottomEdge]: defaultHintDotPlacement,
- [PLACEMENT.bottomRightEdge]: defaultHintDotPlacement,
- [PLACEMENT.bottomLeftEdge]: defaultHintDotPlacement,
- [PLACEMENT.leftTopEdge]: defaultHintDotPlacement,
- [PLACEMENT.rightTopEdge]: defaultHintDotPlacement,
- [PLACEMENT.rightBottomEdge]: defaultHintDotPlacement,
- [PLACEMENT.leftBottomEdge]: defaultHintDotPlacement,
+ [PLACEMENT.topLeft]: {
+ top: '16px',
+ left: '16px',
+ },
+ [PLACEMENT.topRight]: {
+ top: '16px',
+ right: '16px',
+ },
+ [PLACEMENT.bottomRight]: {
+ bottom: '16px',
+ right: '16px',
},
- };
-}
-// This is designed for hint badge. This data structure make future extension on other badges possible.
-function getPositionStrokeOffStyles($theme: Theme) {
- const defaultHintDotPlacement = {
- insetBlockStart: `-${$theme.sizing.scale0}`,
- insetInlineEnd: `-${$theme.sizing.scale0}`,
- };
- return {
- [ROLE.hintDot]: {
- // For now, we only need to support topLeft, topRight, bottomLeft and bottomRight. Others are considered as an invalid placement prop.
- [PLACEMENT.topLeft]: {
- insetBlockStart: `-${$theme.sizing.scale0}`,
- insetInlineStart: `-${$theme.sizing.scale0}`,
- },
- [PLACEMENT.topRight]: defaultHintDotPlacement,
- [PLACEMENT.bottomRight]: {
- insetBlockEnd: `-${$theme.sizing.scale0}`,
- insetInlineEnd: `-${$theme.sizing.scale0}`,
- },
- [PLACEMENT.bottomLeft]: {
- insetBlockEnd: `-${$theme.sizing.scale0}`,
- insetInlineStart: `-${$theme.sizing.scale0}`,
- },
- // unsupported placements fall back to topRight
- [PLACEMENT.topEdge]: defaultHintDotPlacement,
- [PLACEMENT.topLeftEdge]: defaultHintDotPlacement,
- [PLACEMENT.topRightEdge]: defaultHintDotPlacement,
- [PLACEMENT.bottomEdge]: defaultHintDotPlacement,
- [PLACEMENT.bottomRightEdge]: defaultHintDotPlacement,
- [PLACEMENT.bottomLeftEdge]: defaultHintDotPlacement,
- [PLACEMENT.leftTopEdge]: defaultHintDotPlacement,
- [PLACEMENT.rightTopEdge]: defaultHintDotPlacement,
- [PLACEMENT.rightBottomEdge]: defaultHintDotPlacement,
- [PLACEMENT.leftBottomEdge]: defaultHintDotPlacement,
+ [PLACEMENT.bottomLeft]: {
+ bottom: '16px',
+ left: '16px',
},
- };
-}
+ [PLACEMENT.topLeftEdge]: {
+ top: '-8px',
+ left: '16px',
+ },
+ [PLACEMENT.topRightEdge]: {
+ top: '-8px',
+ right: '16px',
+ },
+ [PLACEMENT.bottomRightEdge]: {
+ bottom: '-8px',
+ right: '16px',
+ },
+ [PLACEMENT.bottomLeftEdge]: {
+ bottom: '-8px',
+ left: '16px',
+ },
+ [PLACEMENT.leftTopEdge]: {
+ top: '16px',
+ left: '-8px',
+ },
+ [PLACEMENT.rightTopEdge]: {
+ top: '16px',
+ right: '-8px',
+ },
+ [PLACEMENT.rightBottomEdge]: {
+ bottom: '16px',
+ right: '-8px',
+ },
+ [PLACEMENT.leftBottomEdge]: {
+ bottom: '16px',
+ left: '-8px',
+ },
+ },
+ [ROLE.notificationCircle]: {
+ [PLACEMENT.topLeft]: {
+ top: '-10px',
+ left: '-10px',
+ },
+ [PLACEMENT.topRight]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ // NotificationCircle can only be placed topLeft or topRight, other values fall back to topRight
+ [PLACEMENT.topEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.bottomEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.bottomRight]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.bottomLeft]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.topLeftEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.topRightEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.bottomRightEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.bottomLeftEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.leftTopEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.rightTopEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.rightBottomEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ [PLACEMENT.leftBottomEdge]: DEFAULT_NOTIFICATION_CIRCLE_PLACEMENT,
+ },
+ [ROLE.hintDot]: {
+ [PLACEMENT.topLeft]: {
+ top: '-4px',
+ left: '-4px',
+ },
+ [PLACEMENT.topRight]: DEFAULT_HINT_DOT_PLACEMENT,
+ // HintDot can only be placed topLeft or topRight, other values fall back to topRight
+ [PLACEMENT.topEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.bottomEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.bottomRight]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.bottomLeft]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.topLeftEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.topRightEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.bottomRightEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.bottomLeftEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.leftTopEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.rightTopEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.rightBottomEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ [PLACEMENT.leftBottomEdge]: DEFAULT_HINT_DOT_PLACEMENT,
+ },
+});
export const StyledRoot = styled<'div', {}>('div', () => {
return {
@@ -286,59 +232,35 @@ export const StyledPositioner = styled<
$placement: Placement;
$horizontalOffset?: string | null;
$verticalOffset?: string | null;
- $noAnchor?: boolean;
- $hasBorder?: boolean;
}
->(
- 'div',
- ({
- $theme,
- $role,
- $placement,
- $horizontalOffset,
- $verticalOffset,
- $noAnchor = false,
- $hasBorder = true,
- }) => {
- // If no anchor and no offset specified, the hintBadge should not have any offsets.
- if (
- $role === ROLE.hintDot &&
- $noAnchor &&
- $horizontalOffset === undefined &&
- $verticalOffset === undefined
- )
- return {};
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+>('div', ({ $theme, $role, $placement, $horizontalOffset, $verticalOffset }) => {
+ let positionStyle = POSITION_STYLES[$role][$placement];
- let positionStyle =
- $role === ROLE.hintDot && !$hasBorder
- ? getPositionStrokeOffStyles($theme)[$role][$placement]
- : getPositionStyles($theme)[$role][$placement];
-
- if ($verticalOffset) {
- if (TOP_PLACEMENTS.includes($placement)) {
- positionStyle = { ...positionStyle, insetBlockStart: $verticalOffset };
- }
- if (BOTTOM_PLACEMENTS.includes($placement)) {
- positionStyle = { ...positionStyle, insetBlockEnd: $verticalOffset };
- }
+ if ($verticalOffset) {
+ if (TOP_PLACEMENTS.includes($placement)) {
+ positionStyle = { ...positionStyle, top: $verticalOffset };
}
-
- if ($horizontalOffset) {
- if (LEFT_PLACEMENTS.includes($placement)) {
- positionStyle = { ...positionStyle, insetInlineStart: $horizontalOffset };
- }
- if (RIGHT_PLACEMENTS.includes($placement)) {
- positionStyle = { ...positionStyle, insetInlineEnd: $horizontalOffset };
- }
+ if (BOTTOM_PLACEMENTS.includes($placement)) {
+ positionStyle = { ...positionStyle, bottom: $verticalOffset };
}
+ }
- return {
- ...positionStyle,
- position: 'absolute',
- lineHeight: 'initial',
- };
+ if ($horizontalOffset) {
+ if (LEFT_PLACEMENTS.includes($placement)) {
+ positionStyle = { ...positionStyle, left: $horizontalOffset };
+ }
+ if (RIGHT_PLACEMENTS.includes($placement)) {
+ positionStyle = { ...positionStyle, right: $horizontalOffset };
+ }
}
-);
+
+ return {
+ ...positionStyle,
+ position: 'absolute',
+ lineHeight: 'initial',
+ };
+});
StyledPositioner.displayName = 'StyledPositioner';
@@ -384,96 +306,40 @@ export const StyledNotificationCircle = styled<
{
$color?: Color;
$hidden?: boolean;
- $size?: NotificationCircleSize;
- $extraPadding?: boolean;
}
->('div', ({ $theme, $color = COLOR.accent, $hidden, $size, $extraPadding }) => {
- const sizeDimension =
- $size === NOTIFICATION_CIRCLE_SIZE.small ? $theme.sizing.scale600 : $theme.sizing.scale700;
- const paddingDimension =
- $size === NOTIFICATION_CIRCLE_SIZE.small
- ? $extraPadding
- ? $theme.sizing.scale100
- : $theme.sizing.scale0
- : $extraPadding
- ? '6.5px'
- : $theme.sizing.scale100;
-
+>('div', ({ $theme, $color = COLOR.accent, $hidden }) => {
return {
visibility: $hidden ? 'hidden' : 'inherit',
- height: sizeDimension,
- minWidth: sizeDimension,
- boxSizing: 'border-box',
- paddingLeft: paddingDimension,
- paddingRight: paddingDimension,
- borderRadius: sizeDimension,
+ height: $theme.sizing.scale700,
+ width: $theme.sizing.scale700,
+ borderRadius: '20px',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
...getColorStyles({ $theme, $hierarchy: HIERARCHY.primary, $color }),
- ...($size === NOTIFICATION_CIRCLE_SIZE.small
- ? $theme.typography.LabelXSmall
- : $theme.typography.LabelSmall),
+ ...$theme.typography.LabelXSmall,
};
});
StyledNotificationCircle.displayName = 'StyledNotificationCircle';
-function getHintDotColorStyles(
- $theme: Theme,
- $color: Color
-): {
- backgroundColor: string;
- borderColor: string;
-} {
- const styles = {
- [COLOR.accent]: {
- backgroundColor: $theme.colors.brandBackgroundPrimary,
- borderColor: $theme.colors.backgroundPrimary,
- },
- [COLOR.positive]: {
- backgroundColor: $theme.colors.contentPositive,
- borderColor: $theme.colors.backgroundPrimary,
- },
- [COLOR.warning]: {
- backgroundColor: $theme.colors.contentWarning,
- borderColor: $theme.colors.backgroundPrimary,
- },
- [COLOR.negative]: {
- backgroundColor: $theme.colors.contentNegative,
- borderColor: $theme.colors.backgroundPrimary,
- },
- [COLOR.primary]: {
- backgroundColor: $theme.colors.backgroundInversePrimary,
- borderColor: $theme.colors.backgroundPrimary,
- },
- [COLOR.onBrand]: {
- backgroundColor: $theme.colors.brandContentOnPrimary,
- borderColor: $theme.colors.brandBackgroundPrimary,
- },
- };
- return styles[$color] ?? styles[COLOR.accent];
-}
-
export const StyledHintDot = styled<
'div',
{
- $color?: Color;
+ $color: Color;
$hidden?: boolean;
- $hasBorder?: boolean;
}
->('div', ({ $theme, $color = COLOR.accent, $hidden, $hasBorder = true }) => {
- const { backgroundColor, borderColor } = getHintDotColorStyles($theme, $color);
+>('div', ({ $theme, $color = COLOR.accent, $hidden }) => {
return {
visibility: $hidden ? 'hidden' : 'inherit',
- backgroundColor,
+ // @ts-ignore
+ backgroundColor: $theme.colors[$color],
boxSizing: 'content-box',
- height: $theme.sizing.scale300, // 8px
- width: $theme.sizing.scale300, // 8px
+ height: '8px',
+ width: '8px',
borderRadius: '50%',
- borderStyle: $hasBorder ? 'solid' : 'none',
- borderWidth: $theme.sizing.scale0, // 2px
- borderColor,
+ border: `4px solid ${$theme.colors.backgroundPrimary}`,
+ ...getColorStyles({ $theme, $hierarchy: HIERARCHY.primary, $color }),
};
});
StyledHintDot.displayName = 'StyledHintDot';
diff --git a/src/badge/types.ts b/src/badge/types.ts
index aba61736c8..087eb73c9c 100644
--- a/src/badge/types.ts
+++ b/src/badge/types.ts
@@ -5,14 +5,7 @@ This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
import type * as React from 'react';
-import type {
- HIERARCHY,
- SHAPE,
- COLOR,
- PLACEMENT,
- ROLE,
- NOTIFICATION_CIRCLE_SIZE,
-} from './constants';
+import type { HIERARCHY, SHAPE, COLOR, PLACEMENT, ROLE } from './constants';
import type { Override } from '../helpers/overrides';
export type Hierarchy = (typeof HIERARCHY)[keyof typeof HIERARCHY];
@@ -20,8 +13,6 @@ export type Shape = (typeof SHAPE)[keyof typeof SHAPE];
export type Color = (typeof COLOR)[keyof typeof COLOR];
export type Placement = (typeof PLACEMENT)[keyof typeof PLACEMENT];
export type Role = (typeof ROLE)[keyof typeof ROLE];
-export type NotificationCircleSize =
- (typeof NOTIFICATION_CIRCLE_SIZE)[keyof typeof NOTIFICATION_CIRCLE_SIZE];
export type BadgeOverrides = {
Root?: Override;
@@ -43,7 +34,7 @@ export type BadgeProps = {
};
export type NotificationCircleProps = {
- content: React.ReactNode | ((iconSize: number) => React.ReactNode);
+ content: React.ReactNode;
color?: Color;
placement?: Placement;
hidden?: boolean;
@@ -51,31 +42,13 @@ export type NotificationCircleProps = {
verticalOffset?: string;
overrides?: BadgeOverrides;
children?: React.ReactNode;
- /** The size of the notification circle. Defaults to `medium`. */
- size?: NotificationCircleSize;
};
-export type HintDotPlacement =
- | typeof PLACEMENT.topRight
- | typeof PLACEMENT.topLeft
- | typeof PLACEMENT.bottomRight
- | typeof PLACEMENT.bottomLeft;
-
export type HintDotProps = {
- /** The color of the hint dot. Accepts one of the `COLOR` constant values. */
color?: Color;
- /** When true, the hint dot is not rendered. Useful for conditionally showing/hiding without unmounting. */
hidden?: boolean;
- /** Horizontal offset applied to the dot's position, e.g. `'-14px'`. Overrides the default offset. */
horizontalOffset?: string;
- /** Vertical offset applied to the dot's position, e.g. `'-4px'`. Overrides the default offset. */
verticalOffset?: string;
- /** Corner of the anchor element where the dot is placed. Accepts `topRight`, `topLeft`, `bottomRight`, or `bottomLeft`. Defaults to `topRight`. RTL case has been covered. */
- placement?: HintDotPlacement;
- /** When true (default), renders a border around the dot to visually separate it from the underlying element. */
- hasBorder?: boolean;
- /** Style overrides for the `Root`, `Positioner`, and `Badge` sub-components. */
overrides?: BadgeOverrides;
- /** The element the hint dot is anchored to. Default offsets are applied automatically with/without placement specified. When omitted, the dot renders without an anchor. */
children?: React.ReactNode;
};
diff --git a/src/badge/utils.ts b/src/badge/utils.ts
index 424a0fc1f6..9002ce0643 100644
--- a/src/badge/utils.ts
+++ b/src/badge/utils.ts
@@ -9,6 +9,7 @@ import * as React from 'react';
export const getAnchorFromChildren = (children?: React.ReactNode | null) => {
const childArray = React.Children.toArray(children);
if (childArray.length > 1) {
+ // eslint-disable-next-line no-console
console.error(
`[baseui] No more than 1 child may be passed to Badge, found ${childArray.length} children`
);