@cdx-ui/components 0.0.1-beta.92 → 0.0.1-beta.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/Avatar/index.js +9 -0
- package/lib/commonjs/components/Avatar/index.js.map +1 -1
- package/lib/commonjs/components/Badge/index.js +86 -0
- package/lib/commonjs/components/Badge/index.js.map +1 -0
- package/lib/commonjs/components/Badge/styles.js +127 -0
- package/lib/commonjs/components/Badge/styles.js.map +1 -0
- package/lib/commonjs/components/Textarea/index.js.map +1 -1
- package/lib/commonjs/components/index.js +12 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/figma/Badge.Indicator.figma.js +47 -0
- package/lib/commonjs/figma/Badge.Indicator.figma.js.map +1 -0
- package/lib/commonjs/figma/Badge.figma.js +65 -0
- package/lib/commonjs/figma/Badge.figma.js.map +1 -0
- package/lib/module/components/Avatar/index.js +9 -0
- package/lib/module/components/Avatar/index.js.map +1 -1
- package/lib/module/components/Badge/index.js +83 -0
- package/lib/module/components/Badge/index.js.map +1 -0
- package/lib/module/components/Badge/styles.js +123 -0
- package/lib/module/components/Badge/styles.js.map +1 -0
- package/lib/module/components/Textarea/index.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/figma/Badge.Indicator.figma.js +41 -0
- package/lib/module/figma/Badge.Indicator.figma.js.map +1 -0
- package/lib/module/figma/Badge.figma.js +59 -0
- package/lib/module/figma/Badge.figma.js.map +1 -0
- package/lib/typescript/components/Avatar/index.d.ts +8 -0
- package/lib/typescript/components/Avatar/index.d.ts.map +1 -1
- package/lib/typescript/components/Badge/index.d.ts +14 -0
- package/lib/typescript/components/Badge/index.d.ts.map +1 -0
- package/lib/typescript/components/Badge/styles.d.ts +18 -0
- package/lib/typescript/components/Badge/styles.d.ts.map +1 -0
- package/lib/typescript/components/Textarea/index.d.ts +2 -1
- package/lib/typescript/components/Textarea/index.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/figma/Badge.Indicator.figma.d.ts +11 -0
- package/lib/typescript/figma/Badge.Indicator.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Badge.figma.d.ts +8 -0
- package/lib/typescript/figma/Badge.figma.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/components/Avatar/index.tsx +8 -0
- package/src/components/Badge/index.tsx +91 -0
- package/src/components/Badge/styles.ts +136 -0
- package/src/components/Textarea/index.tsx +2 -1
- package/src/components/index.ts +1 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { forwardRef, type ReactNode } from 'react';
|
|
2
|
+
import { Text, View, type ViewProps } from 'react-native';
|
|
3
|
+
import { dataAttributes, mergeDataAttributes } from '@cdx-ui/primitives';
|
|
4
|
+
import { cn } from '@cdx-ui/utils';
|
|
5
|
+
import type { ForgeIcon } from '@cdx-ui/icons';
|
|
6
|
+
import { Icon } from '../Icon';
|
|
7
|
+
import {
|
|
8
|
+
badgeIconVariants,
|
|
9
|
+
badgeIndicatorVariants,
|
|
10
|
+
badgeLabelVariants,
|
|
11
|
+
badgeRootVariants,
|
|
12
|
+
type BadgeVariantProps,
|
|
13
|
+
} from './styles';
|
|
14
|
+
|
|
15
|
+
const INDICATOR_STYLE = { pointerEvents: 'none' as const };
|
|
16
|
+
|
|
17
|
+
export interface BadgeProps extends ViewProps, BadgeVariantProps {
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
label?: ReactNode;
|
|
20
|
+
icon?: ForgeIcon;
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const Badge = forwardRef<View, BadgeProps>(
|
|
25
|
+
(
|
|
26
|
+
{
|
|
27
|
+
children,
|
|
28
|
+
variant = 'default',
|
|
29
|
+
color = 'action',
|
|
30
|
+
placement = 'top-end',
|
|
31
|
+
overlap = 'rectangular',
|
|
32
|
+
label,
|
|
33
|
+
icon: IconComponent,
|
|
34
|
+
accessibilityLabel,
|
|
35
|
+
className,
|
|
36
|
+
style,
|
|
37
|
+
...props
|
|
38
|
+
},
|
|
39
|
+
ref,
|
|
40
|
+
) => {
|
|
41
|
+
const isDecorative = variant === 'dot' || !accessibilityLabel;
|
|
42
|
+
|
|
43
|
+
const renderContent = () => {
|
|
44
|
+
if (variant === 'dot') return null;
|
|
45
|
+
if (IconComponent) {
|
|
46
|
+
return <Icon as={IconComponent} className={badgeIconVariants({ color })} />;
|
|
47
|
+
}
|
|
48
|
+
if (typeof label === 'string' || typeof label === 'number') {
|
|
49
|
+
return (
|
|
50
|
+
<Text
|
|
51
|
+
className={badgeLabelVariants({ color })}
|
|
52
|
+
{...dataAttributes({ slot: 'badge-label' })}
|
|
53
|
+
>
|
|
54
|
+
{label}
|
|
55
|
+
</Text>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return label ?? null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const hasChildren = children != null;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<View
|
|
65
|
+
ref={ref}
|
|
66
|
+
className={cn(badgeRootVariants({ standalone: !hasChildren }), className)}
|
|
67
|
+
style={style}
|
|
68
|
+
{...props}
|
|
69
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'badge' }))}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
<View
|
|
73
|
+
className={cn(badgeIndicatorVariants({ variant, color, placement, overlap }))}
|
|
74
|
+
style={INDICATOR_STYLE}
|
|
75
|
+
accessible={!isDecorative}
|
|
76
|
+
accessibilityLabel={accessibilityLabel}
|
|
77
|
+
aria-hidden={isDecorative}
|
|
78
|
+
accessibilityElementsHidden={isDecorative}
|
|
79
|
+
importantForAccessibility={isDecorative ? 'no-hide-descendants' : 'auto'}
|
|
80
|
+
{...dataAttributes({ slot: 'badge-indicator' })}
|
|
81
|
+
>
|
|
82
|
+
{renderContent()}
|
|
83
|
+
</View>
|
|
84
|
+
</View>
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
Badge.displayName = 'Badge';
|
|
89
|
+
|
|
90
|
+
export { Badge };
|
|
91
|
+
export type { BadgeVariantProps } from './styles';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
import { TRANSITION_COLORS } from '../../styles/primitives';
|
|
3
|
+
|
|
4
|
+
export const badgeRootVariants = cva(['relative self-start'], {
|
|
5
|
+
variants: {
|
|
6
|
+
standalone: {
|
|
7
|
+
true: 'inline-flex',
|
|
8
|
+
false: '',
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
defaultVariants: { standalone: false },
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const badgeIndicatorVariants = cva(
|
|
15
|
+
[
|
|
16
|
+
'absolute items-center justify-center overflow-hidden',
|
|
17
|
+
'rounded-[var(--border-radius-round)]',
|
|
18
|
+
'border border-surface-primary',
|
|
19
|
+
TRANSITION_COLORS,
|
|
20
|
+
],
|
|
21
|
+
{
|
|
22
|
+
variants: {
|
|
23
|
+
variant: {
|
|
24
|
+
default: 'h-5 min-w-5 px-1',
|
|
25
|
+
dot: 'h-2 w-2 min-w-0 px-0',
|
|
26
|
+
},
|
|
27
|
+
color: {
|
|
28
|
+
action: 'bg-surface-action-strong',
|
|
29
|
+
danger: 'bg-surface-danger-strong',
|
|
30
|
+
warning: 'bg-surface-warning-strong',
|
|
31
|
+
success: 'bg-surface-success-strong',
|
|
32
|
+
info: 'bg-surface-info-strong',
|
|
33
|
+
},
|
|
34
|
+
placement: {
|
|
35
|
+
'top-start': 'top-0 ltr:left-0 rtl:right-0',
|
|
36
|
+
'top-end': 'top-0 ltr:right-0 rtl:left-0',
|
|
37
|
+
'bottom-start': 'bottom-0 ltr:left-0 rtl:right-0',
|
|
38
|
+
'bottom-end': 'bottom-0 ltr:right-0 rtl:left-0',
|
|
39
|
+
},
|
|
40
|
+
overlap: { circular: '', rectangular: '' },
|
|
41
|
+
},
|
|
42
|
+
compoundVariants: [
|
|
43
|
+
// Positioning model (mirrors MUI Badge): the indicator is always centered on an
|
|
44
|
+
// anchor point via a 50% self-translate, so it straddles that point regardless of
|
|
45
|
+
// its own size (dot vs default). `overlap` only moves the anchor point.
|
|
46
|
+
//
|
|
47
|
+
// Translate uses arbitrary `[50%]` (not the `1/2` fraction utility) because the
|
|
48
|
+
// fraction sugar is web-only in Uniwind — Avatar's badge uses `[N%]` translate on
|
|
49
|
+
// native, Toast gates `-translate-x-1/2` behind `web:`.
|
|
50
|
+
//
|
|
51
|
+
// rectangular: anchor at the host's true bounding-box corner.
|
|
52
|
+
{
|
|
53
|
+
placement: 'top-end',
|
|
54
|
+
overlap: 'rectangular',
|
|
55
|
+
className: 'ltr:translate-x-[50%] rtl:-translate-x-[50%] -translate-y-[50%]',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
placement: 'top-start',
|
|
59
|
+
overlap: 'rectangular',
|
|
60
|
+
className: 'ltr:-translate-x-[50%] rtl:translate-x-[50%] -translate-y-[50%]',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
placement: 'bottom-end',
|
|
64
|
+
overlap: 'rectangular',
|
|
65
|
+
className: 'ltr:translate-x-[50%] rtl:-translate-x-[50%] translate-y-[50%]',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
placement: 'bottom-start',
|
|
69
|
+
overlap: 'rectangular',
|
|
70
|
+
className: 'ltr:-translate-x-[50%] rtl:translate-x-[50%] translate-y-[50%]',
|
|
71
|
+
},
|
|
72
|
+
// circular: anchor inset 14% from each edge — the ~45° point of the inscribed
|
|
73
|
+
// circle ((1 - √2/2)/2 ≈ 14.6%, rounded to 14% per MUI) — so the indicator sits
|
|
74
|
+
// on the host's visible arc. The inset is host-relative (percent of the wrapper,
|
|
75
|
+
// which hugs the host), keeping it independent of the indicator's own size.
|
|
76
|
+
{
|
|
77
|
+
placement: 'top-end',
|
|
78
|
+
overlap: 'circular',
|
|
79
|
+
className:
|
|
80
|
+
'top-[14%] ltr:right-[14%] rtl:left-[14%] ltr:translate-x-[50%] rtl:-translate-x-[50%] -translate-y-[50%]',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
placement: 'top-start',
|
|
84
|
+
overlap: 'circular',
|
|
85
|
+
className:
|
|
86
|
+
'top-[14%] ltr:left-[14%] rtl:right-[14%] ltr:-translate-x-[50%] rtl:translate-x-[50%] -translate-y-[50%]',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
placement: 'bottom-end',
|
|
90
|
+
overlap: 'circular',
|
|
91
|
+
className:
|
|
92
|
+
'bottom-[14%] ltr:right-[14%] rtl:left-[14%] ltr:translate-x-[50%] rtl:-translate-x-[50%] translate-y-[50%]',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
placement: 'bottom-start',
|
|
96
|
+
overlap: 'circular',
|
|
97
|
+
className:
|
|
98
|
+
'bottom-[14%] ltr:left-[14%] rtl:right-[14%] ltr:-translate-x-[50%] rtl:translate-x-[50%] translate-y-[50%]',
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
defaultVariants: {
|
|
102
|
+
variant: 'default',
|
|
103
|
+
color: 'action',
|
|
104
|
+
placement: 'top-end',
|
|
105
|
+
overlap: 'rectangular',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
export const badgeLabelVariants = cva(['body-sm font-medium text-center'], {
|
|
111
|
+
variants: {
|
|
112
|
+
color: {
|
|
113
|
+
action: 'text-content-action-on-strong',
|
|
114
|
+
danger: 'text-content-danger-on-strong',
|
|
115
|
+
warning: 'text-content-warning-on-strong',
|
|
116
|
+
success: 'text-content-success-on-strong',
|
|
117
|
+
info: 'text-content-info-on-strong',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
defaultVariants: { color: 'action' },
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
export const badgeIconVariants = cva(['size-3'], {
|
|
124
|
+
variants: {
|
|
125
|
+
color: {
|
|
126
|
+
action: 'text-content-action-on-strong',
|
|
127
|
+
danger: 'text-content-danger-on-strong',
|
|
128
|
+
warning: 'text-content-warning-on-strong',
|
|
129
|
+
success: 'text-content-success-on-strong',
|
|
130
|
+
info: 'text-content-info-on-strong',
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
defaultVariants: { color: 'action' },
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
export type BadgeVariantProps = VariantProps<typeof badgeIndicatorVariants>;
|
|
@@ -46,7 +46,8 @@ export interface TextareaProps
|
|
|
46
46
|
numberOfLines?: number;
|
|
47
47
|
/**
|
|
48
48
|
* Grow the field height to fit its content. Defaults to `false`.
|
|
49
|
-
* Web uses `field-sizing: content`; native
|
|
49
|
+
* Web uses `field-sizing: content`; native grows intrinsically (`scrollEnabled`
|
|
50
|
+
* off), using `onContentSizeChange` only to enable scrolling past `maxRows`.
|
|
50
51
|
*/
|
|
51
52
|
autoGrow?: boolean;
|
|
52
53
|
/** Caps `autoGrow` at this many rows, after which the field scrolls. */
|