@lotics/ui 3.4.0 → 3.5.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.
- package/package.json +2 -1
- package/src/callout.tsx +75 -0
- package/src/filter_pill.tsx +18 -5
- package/src/icon.tsx +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./tokens": "./src/tokens.ts",
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"./action_menu": "./src/action_menu.tsx",
|
|
82
82
|
"./card_select_item": "./src/card_select_item.tsx",
|
|
83
83
|
"./badge": "./src/badge.tsx",
|
|
84
|
+
"./callout": "./src/callout.tsx",
|
|
84
85
|
"./divider": "./src/divider.tsx",
|
|
85
86
|
"./spacer": "./src/spacer.tsx",
|
|
86
87
|
"./index.css": "./src/index.css",
|
package/src/callout.tsx
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { View, StyleSheet } from "react-native";
|
|
2
|
+
import { Text } from "./text";
|
|
3
|
+
import { Icon, type IconName } from "./icon";
|
|
4
|
+
import { colors } from "./colors";
|
|
5
|
+
|
|
6
|
+
export type CalloutTone = "info" | "success" | "warning" | "error" | "neutral";
|
|
7
|
+
|
|
8
|
+
interface CalloutProps {
|
|
9
|
+
/** Sets the accent (icon + tint + border) and the default icon. Default "info". */
|
|
10
|
+
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
|
+
/** Override the per-tone default icon (any kit icon), or `null` to drop it. */
|
|
16
|
+
icon?: IconName | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ToneStyle {
|
|
20
|
+
bg: string;
|
|
21
|
+
border: string;
|
|
22
|
+
icon: string;
|
|
23
|
+
glyph: IconName;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
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" },
|
|
31
|
+
neutral: { bg: colors.zinc[50], border: colors.zinc[200], icon: colors.zinc[500], glyph: "info" },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* An inline callout — a tinted, bordered box carrying a short status message
|
|
36
|
+
* (info / success / warning / error / neutral). Use it inline in a flow: form
|
|
37
|
+
* feedback, a heads-up, an actionable empty state. For a blocking, dismissible
|
|
38
|
+
* prompt use `Alert`; for a one-word status use `Badge`. The tone is carried by
|
|
39
|
+
* the icon + tint + border (never color alone — the icon and text stay legible),
|
|
40
|
+
* so it reads on a glance and meets contrast on the light tint.
|
|
41
|
+
*/
|
|
42
|
+
export function Callout({ tone = "info", title, message, icon }: CalloutProps) {
|
|
43
|
+
const t = TONES[tone];
|
|
44
|
+
const glyph = icon === null ? null : (icon ?? t.glyph);
|
|
45
|
+
return (
|
|
46
|
+
<View
|
|
47
|
+
accessibilityRole={tone === "error" || tone === "warning" ? "alert" : undefined}
|
|
48
|
+
style={[styles.container, { backgroundColor: t.bg, borderColor: t.border }]}
|
|
49
|
+
>
|
|
50
|
+
{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>
|
|
59
|
+
</View>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type { CalloutProps };
|
|
64
|
+
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
container: {
|
|
67
|
+
flexDirection: "row",
|
|
68
|
+
alignItems: "flex-start",
|
|
69
|
+
gap: 10,
|
|
70
|
+
padding: 12,
|
|
71
|
+
borderRadius: 10,
|
|
72
|
+
borderWidth: 1,
|
|
73
|
+
},
|
|
74
|
+
body: { flex: 1, gap: 2, paddingTop: 1 },
|
|
75
|
+
});
|
package/src/filter_pill.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReactNode } from "react";
|
|
1
|
+
import { type ReactNode, useState } from "react";
|
|
2
2
|
import { StyleSheet } from "react-native";
|
|
3
3
|
import { Text } from "./text";
|
|
4
4
|
import { Icon } from "./icon";
|
|
@@ -23,8 +23,11 @@ export interface FilterPillProps {
|
|
|
23
23
|
clearLabel?: string;
|
|
24
24
|
/** The editor revealed on press — a premium primitive (`RangeSlider`,
|
|
25
25
|
* `Counter`, `PickerMenu` multi) or any composed control. `FilterPill` owns
|
|
26
|
-
* the pill + popover chrome; the consumer owns the value and applies it.
|
|
27
|
-
|
|
26
|
+
* the pill + popover chrome; the consumer owns the value and applies it.
|
|
27
|
+
* Pass a render function to receive `close` — the one editor that closes on
|
|
28
|
+
* action is single-select (`<PickerMenu>` → close on pick); range/counter/
|
|
29
|
+
* multi stay open and dismiss on outside-press, so they pass a plain node. */
|
|
30
|
+
children: ReactNode | ((api: { close: () => void }) => ReactNode);
|
|
28
31
|
side?: PopoverSide;
|
|
29
32
|
align?: PopoverAlign;
|
|
30
33
|
/** Optional controlled popover state — for an editor that closes on Save. */
|
|
@@ -71,8 +74,18 @@ export function FilterPill(props: FilterPillProps) {
|
|
|
71
74
|
// custom footer — a valued, non-clearable pill ("Target: 20") keeps its chevron.
|
|
72
75
|
const showClear = active && !!onClear && !footer;
|
|
73
76
|
|
|
77
|
+
// Own the open state so the editor can close itself via the render-prop
|
|
78
|
+
// `close`, while still honoring a controlled `open`/`onOpenChange` from the
|
|
79
|
+
// parent (the Save-footer VALUE-pill case).
|
|
80
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
81
|
+
const isOpen = open ?? internalOpen;
|
|
82
|
+
const setOpen = (next: boolean) => {
|
|
83
|
+
if (open === undefined) setInternalOpen(next);
|
|
84
|
+
onOpenChange?.(next);
|
|
85
|
+
};
|
|
86
|
+
|
|
74
87
|
return (
|
|
75
|
-
<Popover side={side} align={align} open={
|
|
88
|
+
<Popover side={side} align={align} open={isOpen} onOpenChange={setOpen}>
|
|
76
89
|
<PopoverTrigger>
|
|
77
90
|
<PillButton onDismiss={showClear ? onClear : undefined} dismissTooltip={clearLabel}>
|
|
78
91
|
<Text size="sm" weight="medium" color={active ? "zinc-900" : "zinc-700"} numberOfLines={1}>
|
|
@@ -82,7 +95,7 @@ export function FilterPill(props: FilterPillProps) {
|
|
|
82
95
|
</PillButton>
|
|
83
96
|
</PopoverTrigger>
|
|
84
97
|
<PopoverContent style={styles.body} disableBodyScroll>
|
|
85
|
-
{children}
|
|
98
|
+
{typeof children === "function" ? children({ close: () => setOpen(false) }) : children}
|
|
86
99
|
{footer ? (
|
|
87
100
|
<PopoverFooter>{footer}</PopoverFooter>
|
|
88
101
|
) : showClear ? (
|
package/src/icon.tsx
CHANGED
|
@@ -51,6 +51,7 @@ import ChevronsDownUp from "lucide-react-native/dist/esm/icons/chevrons-down-up"
|
|
|
51
51
|
import ChevronsUpDown from "lucide-react-native/dist/esm/icons/chevrons-up-down";
|
|
52
52
|
import CircleAlert from "lucide-react-native/dist/esm/icons/circle-alert";
|
|
53
53
|
import CircleCheck from "lucide-react-native/dist/esm/icons/circle-check";
|
|
54
|
+
import TriangleAlert from "lucide-react-native/dist/esm/icons/triangle-alert";
|
|
54
55
|
import Clock from "lucide-react-native/dist/esm/icons/clock";
|
|
55
56
|
import Code from "lucide-react-native/dist/esm/icons/code";
|
|
56
57
|
import CodeXml from "lucide-react-native/dist/esm/icons/code-xml";
|
|
@@ -242,6 +243,7 @@ const iconComponents = {
|
|
|
242
243
|
"chevrons-up-down": ChevronsUpDown,
|
|
243
244
|
"circle-alert": CircleAlert,
|
|
244
245
|
"circle-check": CircleCheck,
|
|
246
|
+
"triangle-alert": TriangleAlert,
|
|
245
247
|
clock: Clock,
|
|
246
248
|
code: Code,
|
|
247
249
|
"code-xml": CodeXml,
|