@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.
Files changed (46) hide show
  1. package/lib/commonjs/components/Avatar/index.js +9 -0
  2. package/lib/commonjs/components/Avatar/index.js.map +1 -1
  3. package/lib/commonjs/components/Badge/index.js +86 -0
  4. package/lib/commonjs/components/Badge/index.js.map +1 -0
  5. package/lib/commonjs/components/Badge/styles.js +127 -0
  6. package/lib/commonjs/components/Badge/styles.js.map +1 -0
  7. package/lib/commonjs/components/Textarea/index.js.map +1 -1
  8. package/lib/commonjs/components/index.js +12 -0
  9. package/lib/commonjs/components/index.js.map +1 -1
  10. package/lib/commonjs/figma/Badge.Indicator.figma.js +47 -0
  11. package/lib/commonjs/figma/Badge.Indicator.figma.js.map +1 -0
  12. package/lib/commonjs/figma/Badge.figma.js +65 -0
  13. package/lib/commonjs/figma/Badge.figma.js.map +1 -0
  14. package/lib/module/components/Avatar/index.js +9 -0
  15. package/lib/module/components/Avatar/index.js.map +1 -1
  16. package/lib/module/components/Badge/index.js +83 -0
  17. package/lib/module/components/Badge/index.js.map +1 -0
  18. package/lib/module/components/Badge/styles.js +123 -0
  19. package/lib/module/components/Badge/styles.js.map +1 -0
  20. package/lib/module/components/Textarea/index.js.map +1 -1
  21. package/lib/module/components/index.js +1 -0
  22. package/lib/module/components/index.js.map +1 -1
  23. package/lib/module/figma/Badge.Indicator.figma.js +41 -0
  24. package/lib/module/figma/Badge.Indicator.figma.js.map +1 -0
  25. package/lib/module/figma/Badge.figma.js +59 -0
  26. package/lib/module/figma/Badge.figma.js.map +1 -0
  27. package/lib/typescript/components/Avatar/index.d.ts +8 -0
  28. package/lib/typescript/components/Avatar/index.d.ts.map +1 -1
  29. package/lib/typescript/components/Badge/index.d.ts +14 -0
  30. package/lib/typescript/components/Badge/index.d.ts.map +1 -0
  31. package/lib/typescript/components/Badge/styles.d.ts +18 -0
  32. package/lib/typescript/components/Badge/styles.d.ts.map +1 -0
  33. package/lib/typescript/components/Textarea/index.d.ts +2 -1
  34. package/lib/typescript/components/Textarea/index.d.ts.map +1 -1
  35. package/lib/typescript/components/index.d.ts +1 -0
  36. package/lib/typescript/components/index.d.ts.map +1 -1
  37. package/lib/typescript/figma/Badge.Indicator.figma.d.ts +11 -0
  38. package/lib/typescript/figma/Badge.Indicator.figma.d.ts.map +1 -0
  39. package/lib/typescript/figma/Badge.figma.d.ts +8 -0
  40. package/lib/typescript/figma/Badge.figma.d.ts.map +1 -0
  41. package/package.json +4 -4
  42. package/src/components/Avatar/index.tsx +8 -0
  43. package/src/components/Badge/index.tsx +91 -0
  44. package/src/components/Badge/styles.ts +136 -0
  45. package/src/components/Textarea/index.tsx +2 -1
  46. 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 measures via `onContentSizeChange`.
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. */
@@ -1,6 +1,7 @@
1
1
  export * from './Alert';
2
2
  export * from './AlertDialog';
3
3
  export * from './Avatar';
4
+ export * from './Badge';
4
5
  export * from './BottomSheet';
5
6
  export * from './Box';
6
7
  export * from './Button';