@lotics/ui 12.1.2 → 13.7.1
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/AGENTS.md +5 -0
- package/docs/catalog.md +75 -33
- package/docs/composition.md +61 -24
- package/docs/data_entry.md +19 -17
- package/docs/templates.md +71 -76
- package/examples/tpl_item_list.tsx +6 -7
- package/examples/tpl_record.tsx +131 -188
- package/examples/tpl_task_board.tsx +20 -22
- package/package.json +3 -2
- package/src/badge.tsx +26 -10
- package/src/checklist.tsx +1 -1
- package/src/detail_row.tsx +24 -8
- package/src/drawer.tsx +10 -2
- package/src/form_date_picker.tsx +2 -1
- package/src/form_field.tsx +23 -3
- package/src/form_picker.tsx +2 -1
- package/src/form_switch.tsx +16 -2
- package/src/form_text_input.tsx +4 -4
- package/src/inline_date_picker.tsx +19 -9
- package/src/inline_edit.tsx +43 -24
- package/src/inline_member_select.tsx +43 -10
- package/src/inline_number_input.tsx +4 -4
- package/src/inline_select.tsx +193 -87
- package/src/inline_text_input.tsx +4 -4
- package/src/inline_time_picker.tsx +4 -4
- package/src/inset.tsx +38 -0
- package/src/linked_record_box.tsx +102 -0
- package/src/number_input.tsx +1 -1
- package/src/radio_picker.tsx +18 -28
- package/src/text_input_field.tsx +3 -3
- package/examples/tpl_tasks.tsx +0 -456
- package/src/inline_tag_select.tsx +0 -140
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { View, StyleSheet } from "react-native";
|
|
3
|
-
import { Icon } from "./icon";
|
|
4
|
-
import { Text } from "./text";
|
|
5
|
-
import { colors } from "./colors";
|
|
6
|
-
import { Popover, PopoverTrigger, PopoverContent } from "./popover";
|
|
7
|
-
import { OptionList } from "./option_list";
|
|
8
|
-
import type { PickerOption } from "./picker";
|
|
9
|
-
import { ActivityIndicator } from "./activity_indicator";
|
|
10
|
-
import { Badge } from "./badge";
|
|
11
|
-
import { type InlineEditBackground, InlineEditView } from "./inline_edit";
|
|
12
|
-
import { useLoticsLocale } from "./locale";
|
|
13
|
-
|
|
14
|
-
export interface InlineTagSelectProps<T extends string> {
|
|
15
|
-
/** The selected tag values. */
|
|
16
|
-
value: T[];
|
|
17
|
-
/** Commits the NEW SET when the picker closes (only if it changed). Throwing
|
|
18
|
-
* surfaces the inline error, like every inline editor. */
|
|
19
|
-
onSave: (next: T[]) => void | Promise<void>;
|
|
20
|
-
options: PickerOption<T>[];
|
|
21
|
-
/** The resting render of ONE selected tag (an `OptionBadge` dot, a `Chip`).
|
|
22
|
-
* Defaults to a plain zinc `Badge` of the label. */
|
|
23
|
-
renderTag?: (option: PickerOption<T>) => React.ReactNode;
|
|
24
|
-
/** Rich row content in the dropdown. Falls back to `renderTag`, then label. */
|
|
25
|
-
renderOptionContent?: (option: PickerOption<T>) => React.ReactNode;
|
|
26
|
-
placeholder?: string;
|
|
27
|
-
disabled?: boolean;
|
|
28
|
-
accessibilityLabel?: string;
|
|
29
|
-
/** Resting surface — "tint" (default, the zinc-50 chip) or "transparent". */
|
|
30
|
-
background?: InlineEditBackground;
|
|
31
|
-
/** Show an in-menu search box; default false. */
|
|
32
|
-
searchable?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The inline-editable MULTI select — the tag-field member of the `Inline*`
|
|
37
|
-
* family (`InlineSelect` picks one; this holds a set). At rest the selected
|
|
38
|
-
* tags render as badges inside the standard inline chip; clicking floats a
|
|
39
|
-
* multi `OptionList` (checkbox rows) in a popover, toggles edit a local draft,
|
|
40
|
-
* and CLOSING the popover commits the new set in one `onSave` — the blur-commit
|
|
41
|
-
* contract of the inline family, applied to a set.
|
|
42
|
-
*/
|
|
43
|
-
export function InlineTagSelect<T extends string>(props: InlineTagSelectProps<T>) {
|
|
44
|
-
const {
|
|
45
|
-
value, onSave, options, renderTag, renderOptionContent, placeholder, disabled, accessibilityLabel, background, searchable = false,
|
|
46
|
-
} = props;
|
|
47
|
-
const labels = useLoticsLocale().inline;
|
|
48
|
-
const [open, setOpenState] = useState(false);
|
|
49
|
-
const [draft, setDraft] = useState<T[]>(value);
|
|
50
|
-
const [saving, setSaving] = useState(false);
|
|
51
|
-
const [error, setError] = useState<string | null>(null);
|
|
52
|
-
|
|
53
|
-
const commit = async (next: T[]) => {
|
|
54
|
-
const changed = next.length !== value.length || next.some((v) => !value.includes(v));
|
|
55
|
-
if (!changed) return;
|
|
56
|
-
setSaving(true);
|
|
57
|
-
setError(null);
|
|
58
|
-
try {
|
|
59
|
-
await onSave(next);
|
|
60
|
-
} catch (e) {
|
|
61
|
-
setError(e instanceof Error && e.message ? e.message : labels.saveError);
|
|
62
|
-
} finally {
|
|
63
|
-
setSaving(false);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const setOpen = (next: boolean) => {
|
|
68
|
-
if (next) {
|
|
69
|
-
setDraft(value);
|
|
70
|
-
setOpenState(true);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
setOpenState(false);
|
|
74
|
-
void commit(draft);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const selected = options.filter((o) => value.includes(o.value));
|
|
78
|
-
const tag = (o: PickerOption<T>) => (renderTag ? renderTag(o) : <Badge label={o.label ?? String(o.value)} color="zinc" />);
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<View>
|
|
82
|
-
<Popover open={open && !disabled} onOpenChange={setOpen} side="bottom" align="start" inheritTriggerWidth>
|
|
83
|
-
<PopoverTrigger>
|
|
84
|
-
<InlineEditView
|
|
85
|
-
background={background}
|
|
86
|
-
display={
|
|
87
|
-
selected.length > 0 ? (
|
|
88
|
-
<View style={styles.tags}>
|
|
89
|
-
{selected.map((o) => (
|
|
90
|
-
<View key={o.value}>{tag(o)}</View>
|
|
91
|
-
))}
|
|
92
|
-
</View>
|
|
93
|
-
) : (
|
|
94
|
-
""
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
placeholder={placeholder}
|
|
98
|
-
disabled={disabled}
|
|
99
|
-
active={open && !disabled}
|
|
100
|
-
accessibilityLabel={accessibilityLabel}
|
|
101
|
-
trailing={
|
|
102
|
-
saving ? (
|
|
103
|
-
<ActivityIndicator size={16} color={colors.zinc[400]} />
|
|
104
|
-
) : (
|
|
105
|
-
<Icon name="chevron-down" size={18} color={colors.zinc[400]} />
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
/>
|
|
109
|
-
</PopoverTrigger>
|
|
110
|
-
<PopoverContent disableBodyScroll>
|
|
111
|
-
<OptionList<T, true>
|
|
112
|
-
multi
|
|
113
|
-
search={{ mode: searchable ? "internal" : "none" }}
|
|
114
|
-
options={options}
|
|
115
|
-
value={draft}
|
|
116
|
-
onValueChange={setDraft}
|
|
117
|
-
onRequestClose={() => setOpen(false)}
|
|
118
|
-
renderOptionContent={renderOptionContent ?? renderTag}
|
|
119
|
-
/>
|
|
120
|
-
</PopoverContent>
|
|
121
|
-
</Popover>
|
|
122
|
-
{error ? (
|
|
123
|
-
<Text size="xs" color="danger" style={styles.error}>
|
|
124
|
-
{error}
|
|
125
|
-
</Text>
|
|
126
|
-
) : null}
|
|
127
|
-
</View>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const styles = StyleSheet.create({
|
|
132
|
-
tags: {
|
|
133
|
-
flexDirection: "row",
|
|
134
|
-
alignItems: "center",
|
|
135
|
-
flexWrap: "wrap",
|
|
136
|
-
columnGap: 4,
|
|
137
|
-
rowGap: 2,
|
|
138
|
-
},
|
|
139
|
-
error: { marginTop: 4 },
|
|
140
|
-
});
|