@cdx-ui/components 0.0.1-beta.70 → 0.0.1-beta.72

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 (76) hide show
  1. package/lib/commonjs/components/Alert/index.js +263 -0
  2. package/lib/commonjs/components/Alert/index.js.map +1 -0
  3. package/lib/commonjs/components/Alert/styles.js +87 -0
  4. package/lib/commonjs/components/Alert/styles.js.map +1 -0
  5. package/lib/commonjs/components/QuickActionButton/index.js +133 -0
  6. package/lib/commonjs/components/QuickActionButton/index.js.map +1 -0
  7. package/lib/commonjs/components/QuickActionButton/styles.js +17 -0
  8. package/lib/commonjs/components/QuickActionButton/styles.js.map +1 -0
  9. package/lib/commonjs/components/index.js +24 -0
  10. package/lib/commonjs/components/index.js.map +1 -1
  11. package/lib/commonjs/figma/Alert.Button.figma.js +31 -0
  12. package/lib/commonjs/figma/Alert.Button.figma.js.map +1 -0
  13. package/lib/commonjs/figma/Alert.Close.figma.js +24 -0
  14. package/lib/commonjs/figma/Alert.Close.figma.js.map +1 -0
  15. package/lib/commonjs/figma/Alert.Description.figma.js +20 -0
  16. package/lib/commonjs/figma/Alert.Description.figma.js.map +1 -0
  17. package/lib/commonjs/figma/Alert.Title.figma.js +20 -0
  18. package/lib/commonjs/figma/Alert.Title.figma.js.map +1 -0
  19. package/lib/commonjs/figma/Alert.figma.js +42 -0
  20. package/lib/commonjs/figma/Alert.figma.js.map +1 -0
  21. package/lib/commonjs/figma/QuickActionButton.figma.js +38 -0
  22. package/lib/commonjs/figma/QuickActionButton.figma.js.map +1 -0
  23. package/lib/module/components/Alert/index.js +259 -0
  24. package/lib/module/components/Alert/index.js.map +1 -0
  25. package/lib/module/components/Alert/styles.js +84 -0
  26. package/lib/module/components/Alert/styles.js.map +1 -0
  27. package/lib/module/components/QuickActionButton/index.js +129 -0
  28. package/lib/module/components/QuickActionButton/index.js.map +1 -0
  29. package/lib/module/components/QuickActionButton/styles.js +13 -0
  30. package/lib/module/components/QuickActionButton/styles.js.map +1 -0
  31. package/lib/module/components/index.js +2 -0
  32. package/lib/module/components/index.js.map +1 -1
  33. package/lib/module/figma/Alert.Button.figma.js +25 -0
  34. package/lib/module/figma/Alert.Button.figma.js.map +1 -0
  35. package/lib/module/figma/Alert.Close.figma.js +18 -0
  36. package/lib/module/figma/Alert.Close.figma.js.map +1 -0
  37. package/lib/module/figma/Alert.Description.figma.js +14 -0
  38. package/lib/module/figma/Alert.Description.figma.js.map +1 -0
  39. package/lib/module/figma/Alert.Title.figma.js +14 -0
  40. package/lib/module/figma/Alert.Title.figma.js.map +1 -0
  41. package/lib/module/figma/Alert.figma.js +36 -0
  42. package/lib/module/figma/Alert.figma.js.map +1 -0
  43. package/lib/module/figma/QuickActionButton.figma.js +32 -0
  44. package/lib/module/figma/QuickActionButton.figma.js.map +1 -0
  45. package/lib/typescript/components/Alert/index.d.ts +67 -0
  46. package/lib/typescript/components/Alert/index.d.ts.map +1 -0
  47. package/lib/typescript/components/Alert/styles.d.ts +19 -0
  48. package/lib/typescript/components/Alert/styles.d.ts.map +1 -0
  49. package/lib/typescript/components/Button/styles.d.ts +4 -4
  50. package/lib/typescript/components/Chip/styles.d.ts +3 -3
  51. package/lib/typescript/components/IconButton/styles.d.ts +2 -2
  52. package/lib/typescript/components/QuickActionButton/index.d.ts +41 -0
  53. package/lib/typescript/components/QuickActionButton/index.d.ts.map +1 -0
  54. package/lib/typescript/components/QuickActionButton/styles.d.ts +7 -0
  55. package/lib/typescript/components/QuickActionButton/styles.d.ts.map +1 -0
  56. package/lib/typescript/components/Tile/styles.d.ts +1 -1
  57. package/lib/typescript/components/index.d.ts +2 -0
  58. package/lib/typescript/components/index.d.ts.map +1 -1
  59. package/lib/typescript/figma/Alert.Button.figma.d.ts +11 -0
  60. package/lib/typescript/figma/Alert.Button.figma.d.ts.map +1 -0
  61. package/lib/typescript/figma/Alert.Close.figma.d.ts +11 -0
  62. package/lib/typescript/figma/Alert.Close.figma.d.ts.map +1 -0
  63. package/lib/typescript/figma/Alert.Description.figma.d.ts +8 -0
  64. package/lib/typescript/figma/Alert.Description.figma.d.ts.map +1 -0
  65. package/lib/typescript/figma/Alert.Title.figma.d.ts +8 -0
  66. package/lib/typescript/figma/Alert.Title.figma.d.ts.map +1 -0
  67. package/lib/typescript/figma/Alert.figma.d.ts +8 -0
  68. package/lib/typescript/figma/Alert.figma.d.ts.map +1 -0
  69. package/lib/typescript/figma/QuickActionButton.figma.d.ts +8 -0
  70. package/lib/typescript/figma/QuickActionButton.figma.d.ts.map +1 -0
  71. package/package.json +4 -4
  72. package/src/components/Alert/index.tsx +318 -0
  73. package/src/components/Alert/styles.ts +88 -0
  74. package/src/components/QuickActionButton/index.tsx +161 -0
  75. package/src/components/QuickActionButton/styles.ts +42 -0
  76. package/src/components/index.ts +2 -0
@@ -0,0 +1,11 @@
1
+ import figma from 'figma';
2
+ declare const _default: {
3
+ id: string;
4
+ imports: string[];
5
+ metadata: {
6
+ nestable: boolean;
7
+ };
8
+ example: figma.ResultSection[] | figma.TemplateStringResult | null;
9
+ };
10
+ export default _default;
11
+ //# sourceMappingURL=Alert.Button.figma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.Button.figma.d.ts","sourceRoot":"","sources":["../../../src/figma/Alert.Button.figma.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;;;;AAa1B,wBAYE"}
@@ -0,0 +1,11 @@
1
+ import figma from 'figma';
2
+ declare const _default: {
3
+ id: string;
4
+ imports: string[];
5
+ metadata: {
6
+ nestable: boolean;
7
+ };
8
+ example: figma.ResultSection[] | figma.TemplateStringResult;
9
+ };
10
+ export default _default;
11
+ //# sourceMappingURL=Alert.Close.figma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.Close.figma.d.ts","sourceRoot":"","sources":["../../../src/figma/Alert.Close.figma.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;;;;AAQ1B,wBAUE"}
@@ -0,0 +1,8 @@
1
+ import figma from 'figma';
2
+ declare const _default: {
3
+ id: string;
4
+ imports: string[];
5
+ example: figma.TemplateStringResult;
6
+ };
7
+ export default _default;
8
+ //# sourceMappingURL=Alert.Description.figma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.Description.figma.d.ts","sourceRoot":"","sources":["../../../src/figma/Alert.Description.figma.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;AAM1B,wBAIE"}
@@ -0,0 +1,8 @@
1
+ import figma from 'figma';
2
+ declare const _default: {
3
+ id: string;
4
+ imports: string[];
5
+ example: figma.TemplateStringResult;
6
+ };
7
+ export default _default;
8
+ //# sourceMappingURL=Alert.Title.figma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.Title.figma.d.ts","sourceRoot":"","sources":["../../../src/figma/Alert.Title.figma.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;AAM1B,wBAIE"}
@@ -0,0 +1,8 @@
1
+ import figma from 'figma';
2
+ declare const _default: {
3
+ id: string;
4
+ imports: string[];
5
+ example: figma.TemplateStringResult;
6
+ };
7
+ export default _default;
8
+ //# sourceMappingURL=Alert.figma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.figma.d.ts","sourceRoot":"","sources":["../../../src/figma/Alert.figma.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;AA4B1B,wBAiBE"}
@@ -0,0 +1,8 @@
1
+ import figma from 'figma';
2
+ declare const _default: {
3
+ id: string;
4
+ imports: string[];
5
+ example: figma.TemplateStringResult;
6
+ };
7
+ export default _default;
8
+ //# sourceMappingURL=QuickActionButton.figma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuickActionButton.figma.d.ts","sourceRoot":"","sources":["../../../src/figma/QuickActionButton.figma.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;AAqB1B,wBAQE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdx-ui/components",
3
- "version": "0.0.1-beta.70",
3
+ "version": "0.0.1-beta.72",
4
4
  "main": "lib/commonjs/index.js",
5
5
  "module": "lib/module/index.js",
6
6
  "react-native": "src/index.ts",
@@ -67,9 +67,9 @@
67
67
  "@gorhom/bottom-sheet": "^5.2.6",
68
68
  "class-variance-authority": "^0.7.1",
69
69
  "uniwind": "1.6.1",
70
- "@cdx-ui/utils": "0.0.1-beta.70",
71
- "@cdx-ui/primitives": "0.0.1-beta.70",
72
- "@cdx-ui/icons": "0.0.1-beta.70"
70
+ "@cdx-ui/utils": "0.0.1-beta.72",
71
+ "@cdx-ui/icons": "0.0.1-beta.72",
72
+ "@cdx-ui/primitives": "0.0.1-beta.72"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@react-navigation/bottom-tabs": "*",
@@ -0,0 +1,318 @@
1
+ import React, { forwardRef, useMemo, type ReactNode } from 'react';
2
+ import { Text, View, type TextProps, type ViewProps } from 'react-native';
3
+ import { Slot, dataAttributes, mergeDataAttributes } from '@cdx-ui/primitives';
4
+ import type { ForgeIcon } from '@cdx-ui/icons';
5
+ import { CheckCircle, EmergencyHome, Info, Warning } from '@cdx-ui/icons';
6
+ import { cn, ParentContext, useParentContext, useStyleContext } from '@cdx-ui/utils';
7
+ import { Icon, type IconProps } from '../Icon';
8
+ import {
9
+ alertActionsVariants,
10
+ alertBodyVariants,
11
+ alertDescriptionVariants,
12
+ alertIconVariants,
13
+ alertRootVariants,
14
+ alertTextVariants,
15
+ alertTitleVariants,
16
+ type AlertSeverity,
17
+ type AlertVariantProps,
18
+ } from './styles';
19
+
20
+ // =============================================================================
21
+ // CONTEXT
22
+ // =============================================================================
23
+
24
+ const SCOPE = 'ALERT';
25
+
26
+ interface AlertContextValue {
27
+ severity: AlertSeverity;
28
+ }
29
+
30
+ const useAlertStyleContext = (): AlertContextValue => {
31
+ const ctx = useStyleContext(SCOPE) as { severity?: AlertSeverity };
32
+ return { severity: ctx?.severity ?? 'info' };
33
+ };
34
+
35
+ // Severity → default icon mapping
36
+ const SEVERITY_ICONS: Record<AlertSeverity, ForgeIcon> = {
37
+ danger: EmergencyHome,
38
+ warning: Warning,
39
+ success: CheckCircle,
40
+ info: Info,
41
+ };
42
+
43
+ // =============================================================================
44
+ // ROOT
45
+ // =============================================================================
46
+
47
+ export interface AlertProps extends ViewProps, AlertVariantProps {
48
+ /**
49
+ * Replace the root element with the consumer's child element via `Slot`.
50
+ * When `true`, children are rendered as-is (no automatic icon/text/actions
51
+ * layout splitting). Severity context is still provided so sub-components
52
+ * receive the correct styles.
53
+ */
54
+ asChild?: boolean;
55
+ /** Severity level that controls background colour, icon, and live-region urgency. */
56
+ severity?: AlertSeverity;
57
+ className?: string;
58
+ /**
59
+ * Sub-components (`Alert.Icon`, `Alert.Title`, `Alert.Description`,
60
+ * `Alert.Actions`) must be rendered as **direct** children — wrapping them
61
+ * in HOCs or `memo()` will break the automatic layout splitting.
62
+ */
63
+ children?: ReactNode;
64
+ }
65
+
66
+ const AlertRoot = forwardRef<View, AlertProps>(
67
+ ({ asChild, severity = 'info', className, style, children, ...props }, ref) => {
68
+ const isAssertive = severity === 'danger' || severity === 'warning';
69
+ const parent = useParentContext();
70
+ const ctx = useMemo(() => ({ ...parent, [SCOPE]: { severity } }), [parent, severity]);
71
+
72
+ const rootClassName = cn(alertRootVariants({ severity }), className);
73
+ const role = isAssertive ? 'alert' : 'status';
74
+ const liveRegion = isAssertive ? 'assertive' : 'polite';
75
+ const dataProps = mergeDataAttributes(props, dataAttributes({ slot: 'alert' }));
76
+
77
+ if (asChild) {
78
+ return (
79
+ <ParentContext.Provider value={ctx}>
80
+ <Slot
81
+ ref={ref}
82
+ className={rootClassName}
83
+ style={style}
84
+ role={role}
85
+ accessibilityLiveRegion={liveRegion}
86
+ {...props}
87
+ {...dataProps}
88
+ >
89
+ {children}
90
+ </Slot>
91
+ </ParentContext.Provider>
92
+ );
93
+ }
94
+
95
+ // Split children into three regions:
96
+ // 1. Alert.Icon — rendered at inline-start (left)
97
+ // 2. Alert.Actions — rendered at inline-end (right), beside the text
98
+ // 3. Everything else (Title, Description) — text column, flex-1
99
+ //
100
+ // Layout: [Icon] [flex-1 flex-row: [text flex-1] [Actions]]
101
+ const childArray = React.Children.toArray(children);
102
+ const iconChild = childArray.find(
103
+ (child) =>
104
+ React.isValidElement(child) && (child as React.ReactElement).type === AlertIconComponent,
105
+ );
106
+ const actionsChild = childArray.find(
107
+ (child) => React.isValidElement(child) && (child as React.ReactElement).type === AlertActions,
108
+ );
109
+ const textChildren = childArray.filter(
110
+ (child) =>
111
+ !React.isValidElement(child) ||
112
+ ((child as React.ReactElement).type !== AlertIconComponent &&
113
+ (child as React.ReactElement).type !== AlertActions),
114
+ );
115
+ const hasBody = textChildren.length > 0 || !!actionsChild;
116
+
117
+ return (
118
+ <ParentContext.Provider value={ctx}>
119
+ <View
120
+ ref={ref}
121
+ className={rootClassName}
122
+ style={style}
123
+ role={role}
124
+ accessibilityLiveRegion={liveRegion}
125
+ {...props}
126
+ {...dataProps}
127
+ >
128
+ {iconChild}
129
+ {hasBody ? (
130
+ <View className={alertBodyVariants()}>
131
+ {textChildren.length > 0 ? (
132
+ <View className={alertTextVariants()}>{textChildren}</View>
133
+ ) : null}
134
+ {actionsChild}
135
+ </View>
136
+ ) : null}
137
+ </View>
138
+ </ParentContext.Provider>
139
+ );
140
+ },
141
+ );
142
+
143
+ AlertRoot.displayName = 'Alert';
144
+
145
+ // =============================================================================
146
+ // ICON
147
+ // =============================================================================
148
+
149
+ export interface AlertIconProps extends Omit<IconProps, 'as'> {
150
+ asChild?: boolean;
151
+ /**
152
+ * Override the auto-rendered severity icon. Omit to use the default icon for
153
+ * the current severity (EmergencyHome, Warning, CheckCircle, or Info).
154
+ * Not required when `asChild` is set.
155
+ */
156
+ as?: ForgeIcon;
157
+ }
158
+
159
+ const AlertIconComponent = forwardRef<View, AlertIconProps>(
160
+ ({ asChild, as: asOverride, className, style, ...props }, ref) => {
161
+ const { severity } = useAlertStyleContext();
162
+ const computedClassName = cn(alertIconVariants({ severity }), className);
163
+
164
+ if (asChild) {
165
+ return (
166
+ <Slot
167
+ ref={ref}
168
+ className={computedClassName}
169
+ style={style}
170
+ {...props}
171
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-icon' }))}
172
+ />
173
+ );
174
+ }
175
+
176
+ return (
177
+ <Icon
178
+ as={asOverride ?? SEVERITY_ICONS[severity]}
179
+ className={computedClassName}
180
+ style={style}
181
+ {...props}
182
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-icon' }))}
183
+ />
184
+ );
185
+ },
186
+ );
187
+
188
+ AlertIconComponent.displayName = 'Alert.Icon';
189
+
190
+ // =============================================================================
191
+ // TITLE
192
+ // =============================================================================
193
+
194
+ export interface AlertTitleProps extends TextProps {
195
+ asChild?: boolean;
196
+ className?: string;
197
+ children?: ReactNode;
198
+ }
199
+
200
+ const AlertTitle = forwardRef<Text, AlertTitleProps>(
201
+ ({ asChild, className, style, children, ...props }, ref) => {
202
+ const { severity } = useAlertStyleContext();
203
+ const computedClassName = cn(alertTitleVariants({ severity }), className);
204
+ const Comp = asChild ? Slot : Text;
205
+
206
+ return (
207
+ <Comp
208
+ ref={ref}
209
+ className={computedClassName}
210
+ style={style}
211
+ {...props}
212
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-title' }))}
213
+ >
214
+ {children}
215
+ </Comp>
216
+ );
217
+ },
218
+ );
219
+
220
+ AlertTitle.displayName = 'Alert.Title';
221
+
222
+ // =============================================================================
223
+ // DESCRIPTION
224
+ // =============================================================================
225
+
226
+ export interface AlertDescriptionProps extends TextProps {
227
+ asChild?: boolean;
228
+ className?: string;
229
+ children?: ReactNode;
230
+ }
231
+
232
+ const AlertDescription = forwardRef<Text, AlertDescriptionProps>(
233
+ ({ asChild, className, style, children, ...props }, ref) => {
234
+ const { severity } = useAlertStyleContext();
235
+ const computedClassName = cn(alertDescriptionVariants({ severity }), className);
236
+ const Comp = asChild ? Slot : Text;
237
+
238
+ return (
239
+ <Comp
240
+ ref={ref}
241
+ className={computedClassName}
242
+ style={style}
243
+ {...props}
244
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-description' }))}
245
+ >
246
+ {children}
247
+ </Comp>
248
+ );
249
+ },
250
+ );
251
+
252
+ AlertDescription.displayName = 'Alert.Description';
253
+
254
+ // =============================================================================
255
+ // ACTIONS
256
+ // =============================================================================
257
+
258
+ export interface AlertActionsProps extends ViewProps {
259
+ /**
260
+ * Replace the wrapper element with the consumer's child via `Slot`.
261
+ * **Note:** when `true`, automatic `color={severity}` injection on
262
+ * direct `Button`/`IconButton` children is bypassed — the consumer
263
+ * must set `color` explicitly.
264
+ */
265
+ asChild?: boolean;
266
+ className?: string;
267
+ children?: ReactNode;
268
+ }
269
+
270
+ const AlertActions = forwardRef<View, AlertActionsProps>(
271
+ ({ asChild, className, style, children, ...props }, ref) => {
272
+ const { severity } = useAlertStyleContext();
273
+ const computedClassName = cn(alertActionsVariants(), className);
274
+ const Comp = asChild ? Slot : View;
275
+
276
+ // Auto-inject color={severity} on direct Button / IconButton children unless
277
+ // the consumer already provides a color prop.
278
+ const injectedChildren = React.Children.map(children, (child) => {
279
+ if (!React.isValidElement<{ color?: unknown }>(child)) return child;
280
+ if ('color' in child.props) return child;
281
+ return React.cloneElement(child, { color: severity });
282
+ });
283
+
284
+ return (
285
+ <Comp
286
+ ref={ref}
287
+ className={computedClassName}
288
+ style={style}
289
+ {...props}
290
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-actions' }))}
291
+ >
292
+ {asChild ? children : injectedChildren}
293
+ </Comp>
294
+ );
295
+ },
296
+ );
297
+
298
+ AlertActions.displayName = 'Alert.Actions';
299
+
300
+ // =============================================================================
301
+ // COMPOUND EXPORT
302
+ // =============================================================================
303
+
304
+ type AlertCompoundComponent = typeof AlertRoot & {
305
+ Icon: typeof AlertIconComponent;
306
+ Title: typeof AlertTitle;
307
+ Description: typeof AlertDescription;
308
+ Actions: typeof AlertActions;
309
+ };
310
+
311
+ export const Alert = Object.assign(AlertRoot, {
312
+ Icon: AlertIconComponent,
313
+ Title: AlertTitle,
314
+ Description: AlertDescription,
315
+ Actions: AlertActions,
316
+ }) as AlertCompoundComponent;
317
+
318
+ export type { AlertVariantProps, AlertSeverity };
@@ -0,0 +1,88 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+
3
+ // ── Severity type ────────────────────────────────────────────────────────────
4
+
5
+ export type AlertSeverity = 'danger' | 'warning' | 'success' | 'info';
6
+
7
+ // ── Root ─────────────────────────────────────────────────────────────────────
8
+
9
+ export const alertRootVariants = cva(
10
+ ['flex-row items-start', 'gap-2 px-3', 'rounded-[var(--border-radius-default)]'],
11
+ {
12
+ variants: {
13
+ severity: {
14
+ danger: 'bg-surface-danger-subtle',
15
+ warning: 'bg-surface-warning-subtle',
16
+ success: 'bg-surface-success-subtle',
17
+ info: 'bg-surface-info-subtle',
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ severity: 'info',
22
+ },
23
+ },
24
+ );
25
+
26
+ // ── Icon ─────────────────────────────────────────────────────────────────────
27
+
28
+ export const alertIconVariants = cva(['size-5 shrink-0 mt-3'], {
29
+ variants: {
30
+ severity: {
31
+ danger: 'text-content-danger-on-subtle',
32
+ warning: 'text-content-warning-on-subtle',
33
+ success: 'text-content-success-on-subtle',
34
+ info: 'text-content-info-on-subtle',
35
+ },
36
+ },
37
+ defaultVariants: {
38
+ severity: 'info',
39
+ },
40
+ });
41
+
42
+ // ── Body (internal row: text area + trailing actions) ────────────────────────
43
+
44
+ export const alertBodyVariants = cva(['flex-1 flex-row items-start gap-2 min-w-0']);
45
+
46
+ // ── Text (internal flex-col for title + description) ─────────────────────────
47
+
48
+ export const alertTextVariants = cva(['flex-1 flex-col gap-1 min-w-0 mt-3 mb-3']);
49
+
50
+ // ── Title ────────────────────────────────────────────────────────────────────
51
+
52
+ export const alertTitleVariants = cva(['body-md font-semibold'], {
53
+ variants: {
54
+ severity: {
55
+ danger: 'text-content-danger-on-subtle',
56
+ warning: 'text-content-warning-on-subtle',
57
+ success: 'text-content-success-on-subtle',
58
+ info: 'text-content-info-on-subtle',
59
+ },
60
+ },
61
+ defaultVariants: {
62
+ severity: 'info',
63
+ },
64
+ });
65
+
66
+ // ── Description ──────────────────────────────────────────────────────────────
67
+
68
+ export const alertDescriptionVariants = cva(['body-md'], {
69
+ variants: {
70
+ severity: {
71
+ danger: 'text-content-danger-on-subtle',
72
+ warning: 'text-content-warning-on-subtle',
73
+ success: 'text-content-success-on-subtle',
74
+ info: 'text-content-info-on-subtle',
75
+ },
76
+ },
77
+ defaultVariants: {
78
+ severity: 'info',
79
+ },
80
+ });
81
+
82
+ // ── Actions ──────────────────────────────────────────────────────────────────
83
+
84
+ export const alertActionsVariants = cva(['shrink-0 flex-row items-center gap-1.5 mt-1.5 mb-1.5']);
85
+
86
+ // ── Types ─────────────────────────────────────────────────────────────────────
87
+
88
+ export type AlertVariantProps = VariantProps<typeof alertRootVariants>;
@@ -0,0 +1,161 @@
1
+ import { forwardRef, type ReactNode } from 'react';
2
+ import { Text, View, type PressableProps, type TextProps, type ViewProps } from 'react-native';
3
+ import {
4
+ ButtonRoot,
5
+ dataAttributes,
6
+ mergeDataAttributes,
7
+ type IButtonProps,
8
+ useButtonContext,
9
+ } from '@cdx-ui/primitives';
10
+ import type { ForgeIcon } from '@cdx-ui/icons';
11
+ import { cn } from '@cdx-ui/utils';
12
+ import { Icon, type IconProps } from '../Icon';
13
+ import {
14
+ quickActionButtonGlyphVariants,
15
+ quickActionButtonIndicatorVariants,
16
+ quickActionButtonLabelVariants,
17
+ quickActionButtonRootVariants,
18
+ type QuickActionButtonVariantProps,
19
+ } from './styles';
20
+
21
+ // =============================================================================
22
+ // ROOT
23
+ // =============================================================================
24
+
25
+ export interface QuickActionButtonProps extends PressableProps, IButtonProps {
26
+ className?: string;
27
+ children?: ReactNode;
28
+ }
29
+
30
+ const QuickActionButtonRoot = forwardRef<View, QuickActionButtonProps>(
31
+ ({ className, style, children, accessibilityRole = 'button', ...props }, ref) => (
32
+ <ButtonRoot
33
+ ref={ref}
34
+ accessibilityRole={accessibilityRole}
35
+ className={cn(quickActionButtonRootVariants(), className)}
36
+ style={style}
37
+ {...props}
38
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'quick-action-button' }))}
39
+ >
40
+ {children}
41
+ </ButtonRoot>
42
+ ),
43
+ );
44
+ QuickActionButtonRoot.displayName = 'QuickActionButton';
45
+
46
+ // =============================================================================
47
+ // INDICATOR
48
+ // Circular container. Auto-rendered by `QuickActionButton.Icon` in standard usage.
49
+ // Exposed as a compound member for Code Connect parity and custom compositions.
50
+ // =============================================================================
51
+
52
+ export interface QuickActionButtonIndicatorProps extends ViewProps {
53
+ className?: string;
54
+ children?: ReactNode;
55
+ }
56
+
57
+ const QuickActionButtonIndicator = forwardRef<View, QuickActionButtonIndicatorProps>(
58
+ ({ className, style, children, ...props }, ref) => {
59
+ const { hover, focus, active, disabled, focusVisible } = useButtonContext();
60
+ return (
61
+ <View
62
+ ref={ref}
63
+ className={cn(quickActionButtonIndicatorVariants(), className)}
64
+ style={style}
65
+ {...props}
66
+ {...mergeDataAttributes(
67
+ props,
68
+ dataAttributes({
69
+ slot: 'quick-action-button-indicator',
70
+ hover,
71
+ focus,
72
+ active,
73
+ disabled,
74
+ focusVisible,
75
+ }),
76
+ )}
77
+ >
78
+ {children}
79
+ </View>
80
+ );
81
+ },
82
+ );
83
+ QuickActionButtonIndicator.displayName = 'QuickActionButton.Indicator';
84
+
85
+ // =============================================================================
86
+ // ICON
87
+ // Renders the Indicator circle + icon glyph as one convenient sub-component.
88
+ // This is the primary way consumers add the icon to a QuickActionButton.
89
+ // =============================================================================
90
+
91
+ export interface QuickActionButtonIconProps {
92
+ /** Forge icon component to render inside the circular indicator. */
93
+ as: ForgeIcon;
94
+ /** Applied to the icon glyph, not the indicator circle. */
95
+ className?: IconProps['className'];
96
+ /** Applied to the indicator circle wrapper. */
97
+ indicatorClassName?: string;
98
+ }
99
+
100
+ function QuickActionButtonIcon({
101
+ as: IconComponent,
102
+ className,
103
+ indicatorClassName,
104
+ }: QuickActionButtonIconProps) {
105
+ return (
106
+ <QuickActionButtonIndicator className={indicatorClassName}>
107
+ <Icon as={IconComponent} className={cn(quickActionButtonGlyphVariants(), className)} />
108
+ </QuickActionButtonIndicator>
109
+ );
110
+ }
111
+ QuickActionButtonIcon.displayName = 'QuickActionButton.Icon';
112
+
113
+ // =============================================================================
114
+ // LABEL
115
+ // =============================================================================
116
+
117
+ export interface QuickActionButtonLabelProps extends TextProps {
118
+ className?: string;
119
+ children?: ReactNode;
120
+ }
121
+
122
+ const QuickActionButtonLabel = forwardRef<Text, QuickActionButtonLabelProps>(
123
+ ({ className, style, children, ...props }, ref) => {
124
+ const { disabled } = useButtonContext();
125
+ return (
126
+ <Text
127
+ ref={ref}
128
+ numberOfLines={2}
129
+ ellipsizeMode="tail"
130
+ className={cn(quickActionButtonLabelVariants(), className)}
131
+ style={style}
132
+ {...props}
133
+ {...mergeDataAttributes(
134
+ props,
135
+ dataAttributes({ slot: 'quick-action-button-label', disabled }),
136
+ )}
137
+ >
138
+ {children}
139
+ </Text>
140
+ );
141
+ },
142
+ );
143
+ QuickActionButtonLabel.displayName = 'QuickActionButton.Label';
144
+
145
+ // =============================================================================
146
+ // COMPOUND EXPORT
147
+ // =============================================================================
148
+
149
+ type QuickActionButtonCompoundComponent = typeof QuickActionButtonRoot & {
150
+ Indicator: typeof QuickActionButtonIndicator;
151
+ Icon: typeof QuickActionButtonIcon;
152
+ Label: typeof QuickActionButtonLabel;
153
+ };
154
+
155
+ export const QuickActionButton = Object.assign(QuickActionButtonRoot, {
156
+ Indicator: QuickActionButtonIndicator,
157
+ Icon: QuickActionButtonIcon,
158
+ Label: QuickActionButtonLabel,
159
+ }) as QuickActionButtonCompoundComponent;
160
+
161
+ export type { QuickActionButtonVariantProps };