@lotics/ui 3.5.0 → 3.6.0

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/callout.tsx +50 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./tokens": "./src/tokens.ts",
package/src/callout.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ import { type ReactNode } from "react";
1
2
  import { View, StyleSheet } from "react-native";
2
3
  import { Text } from "./text";
3
4
  import { Icon, type IconName } from "./icon";
@@ -8,12 +9,12 @@ export type CalloutTone = "info" | "success" | "warning" | "error" | "neutral";
8
9
  interface CalloutProps {
9
10
  /** Sets the accent (icon + tint + border) and the default icon. Default "info". */
10
11
  tone?: CalloutTone;
11
- /** Optional bold heading above the message. */
12
- title?: string;
13
- /** The message body — kept in the default text color for legibility on the tint. */
14
- message: string;
15
12
  /** Override the per-tone default icon (any kit icon), or `null` to drop it. */
16
13
  icon?: IconName | null;
14
+ /** Compose the body with `CalloutTitle`, `CalloutText`, and `CalloutActions`. */
15
+ children: ReactNode;
16
+ /** Test id on the container. */
17
+ testID?: string;
17
18
  }
18
19
 
19
20
  interface ToneStyle {
@@ -23,11 +24,16 @@ interface ToneStyle {
23
24
  glyph: IconName;
24
25
  }
25
26
 
27
+ // Tint surface + SOFT hairline border + deep icon — matches the kit's toned-surface
28
+ // convention (Badge: bg-50 / border-100 / deep accent). A heavier 200 border + 600
29
+ // icon reads as a generic "library alert"; the soft 100 border lets the tint settle
30
+ // into a calm panel with the deep-700 icon carrying the tone. (Neutral keeps a 200
31
+ // border — zinc-100 is invisible on zinc-50.)
26
32
  const TONES: Record<CalloutTone, ToneStyle> = {
27
- info: { bg: colors.blue[50], border: colors.blue[200], icon: colors.blue[600], glyph: "info" },
28
- success: { bg: colors.emerald[50], border: colors.emerald[200], icon: colors.emerald[600], glyph: "circle-check" },
29
- warning: { bg: colors.amber[50], border: colors.amber[200], icon: colors.amber[600], glyph: "triangle-alert" },
30
- error: { bg: colors.red[50], border: colors.red[200], icon: colors.red[600], glyph: "circle-alert" },
33
+ info: { bg: colors.blue[50], border: colors.blue[100], icon: colors.blue[700], glyph: "info" },
34
+ success: { bg: colors.emerald[50], border: colors.emerald[100], icon: colors.emerald[700], glyph: "circle-check" },
35
+ warning: { bg: colors.amber[50], border: colors.amber[100], icon: colors.amber[700], glyph: "triangle-alert" },
36
+ error: { bg: colors.red[50], border: colors.red[100], icon: colors.red[700], glyph: "circle-alert" },
31
37
  neutral: { bg: colors.zinc[50], border: colors.zinc[200], icon: colors.zinc[500], glyph: "info" },
32
38
  };
33
39
 
@@ -38,28 +44,54 @@ const TONES: Record<CalloutTone, ToneStyle> = {
38
44
  * prompt use `Alert`; for a one-word status use `Badge`. The tone is carried by
39
45
  * the icon + tint + border (never color alone — the icon and text stay legible),
40
46
  * so it reads on a glance and meets contrast on the light tint.
47
+ *
48
+ * COMPOUND — compose the body from the sub-components (like `Card`); this lets a
49
+ * callout carry rich content and inline actions, not just a string message:
50
+ *
51
+ * ```tsx
52
+ * <Callout tone="warning">
53
+ * <CalloutTitle>Phí chưa nhập</CalloutTitle>
54
+ * <CalloutText>Nhập trước khi xuất chứng từ, hoặc đặt = 0.</CalloutText>
55
+ * <CalloutActions>
56
+ * <Button title="Đặt = 0" onPress={fill} />
57
+ * </CalloutActions>
58
+ * </Callout>
59
+ * ```
41
60
  */
42
- export function Callout({ tone = "info", title, message, icon }: CalloutProps) {
61
+ export function Callout({ tone = "info", icon, children, testID }: CalloutProps) {
43
62
  const t = TONES[tone];
44
63
  const glyph = icon === null ? null : (icon ?? t.glyph);
45
64
  return (
46
65
  <View
47
66
  accessibilityRole={tone === "error" || tone === "warning" ? "alert" : undefined}
48
67
  style={[styles.container, { backgroundColor: t.bg, borderColor: t.border }]}
68
+ testID={testID}
49
69
  >
50
70
  {glyph ? <Icon name={glyph} size={18} color={t.icon} /> : null}
51
- <View style={styles.body}>
52
- {title ? (
53
- <Text size="sm" weight="semibold">
54
- {title}
55
- </Text>
56
- ) : null}
57
- <Text size="sm">{message}</Text>
58
- </View>
71
+ <View style={styles.body}>{children}</View>
59
72
  </View>
60
73
  );
61
74
  }
62
75
 
76
+ /** Bold heading at the top of a `Callout`. */
77
+ export function CalloutTitle({ children }: { children: ReactNode }) {
78
+ return (
79
+ <Text size="sm" weight="semibold">
80
+ {children}
81
+ </Text>
82
+ );
83
+ }
84
+
85
+ /** Message body of a `Callout` — default text color for legibility on the tint. */
86
+ export function CalloutText({ children }: { children: ReactNode }) {
87
+ return <Text size="sm">{children}</Text>;
88
+ }
89
+
90
+ /** A row of actions (e.g. buttons) at the foot of a `Callout`. */
91
+ export function CalloutActions({ children }: { children: ReactNode }) {
92
+ return <View style={styles.actions}>{children}</View>;
93
+ }
94
+
63
95
  export type { CalloutProps };
64
96
 
65
97
  const styles = StyleSheet.create({
@@ -72,4 +104,5 @@ const styles = StyleSheet.create({
72
104
  borderWidth: 1,
73
105
  },
74
106
  body: { flex: 1, gap: 2, paddingTop: 1 },
107
+ actions: { flexDirection: "row", flexWrap: "wrap", gap: 8, marginTop: 6 },
75
108
  });