@lotics/ui 1.27.0 → 2.0.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/README.md +34 -0
- package/package.json +10 -11
- package/src/card.tsx +7 -23
- package/src/card_select_item.tsx +52 -0
- package/src/column_filter.tsx +0 -1
- package/src/combobox.tsx +365 -109
- package/src/legend_item.tsx +2 -2
- package/src/picker.tsx +2 -17
- package/src/picker_menu.tsx +84 -121
- package/src/switcher.tsx +4 -3
- package/src/chart_area.tsx +0 -105
- package/src/chart_bar.tsx +0 -154
- package/src/chart_internals.tsx +0 -43
- package/src/custom_option.test.ts +0 -50
- package/src/custom_option.ts +0 -30
- package/src/search_select.tsx +0 -348
- package/src/select_item.tsx +0 -55
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @lotics/ui
|
|
2
|
+
|
|
3
|
+
Cross-platform (react-native-web) UI primitives with no Lotics domain coupling —
|
|
4
|
+
no i18n, analytics, or domain types. Consumed by `frontend`, `browser_extension`,
|
|
5
|
+
and custom-code apps. Lotics-specific compositions live in `frontend` or
|
|
6
|
+
`@lotics/ui-internal`, not here.
|
|
7
|
+
|
|
8
|
+
The package ships `.tsx` source directly (no build step); consumers import
|
|
9
|
+
subpaths, e.g. `import { Button } from "@lotics/ui/button"`.
|
|
10
|
+
|
|
11
|
+
## Dev harness
|
|
12
|
+
|
|
13
|
+
A standalone Vite app for developing the primitives in isolation — no Expo app,
|
|
14
|
+
no backend.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm run dev # from packages/ui — serves http://localhost:5173
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It lives in `dev/`: `main.tsx` mounts a hash-routed sidebar, `registry.ts` lists
|
|
21
|
+
the showcase pages, and `dev/pages/*` are the per-primitive demos. `vite.config.ts`
|
|
22
|
+
wires the react-native-web aliases (the same ones a consuming app's bundler sets up).
|
|
23
|
+
|
|
24
|
+
To add a page: create `dev/pages/<name>.tsx` exporting a component (use the
|
|
25
|
+
`Page`/`Section`/`Row` helpers from `dev/showcase.tsx`) and add one entry to the
|
|
26
|
+
`pages` array in `dev/registry.ts`.
|
|
27
|
+
|
|
28
|
+
## Checks
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run typecheck # tsgo (covers src + dev)
|
|
32
|
+
npm run lint # oxlint
|
|
33
|
+
npm run test # vitest
|
|
34
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./tokens": "./src/tokens.ts",
|
|
@@ -35,8 +35,6 @@
|
|
|
35
35
|
"./stacked_progress_bar": "./src/stacked_progress_bar.tsx",
|
|
36
36
|
"./legend_item": "./src/legend_item.tsx",
|
|
37
37
|
"./trend_footer": "./src/trend_footer.tsx",
|
|
38
|
-
"./chart_area": "./src/chart_area.tsx",
|
|
39
|
-
"./chart_bar": "./src/chart_bar.tsx",
|
|
40
38
|
"./spacing": "./src/spacing.ts",
|
|
41
39
|
"./theme": "./src/theme.tsx",
|
|
42
40
|
"./progress_bar": "./src/progress_bar.tsx",
|
|
@@ -57,7 +55,6 @@
|
|
|
57
55
|
"./button": "./src/button.tsx",
|
|
58
56
|
"./checkbox": "./src/checkbox.tsx",
|
|
59
57
|
"./combobox": "./src/combobox.tsx",
|
|
60
|
-
"./search_select": "./src/search_select.tsx",
|
|
61
58
|
"./portal": "./src/portal.tsx",
|
|
62
59
|
"./popover_nav": "./src/popover_nav.tsx",
|
|
63
60
|
"./popover": "./src/popover.tsx",
|
|
@@ -68,7 +65,7 @@
|
|
|
68
65
|
"./pressable_highlight": "./src/pressable_highlight.tsx",
|
|
69
66
|
"./icon_button": "./src/icon_button.tsx",
|
|
70
67
|
"./info_popover": "./src/info_popover.tsx",
|
|
71
|
-
"./
|
|
68
|
+
"./card_select_item": "./src/card_select_item.tsx",
|
|
72
69
|
"./badge": "./src/badge.tsx",
|
|
73
70
|
"./divider": "./src/divider.tsx",
|
|
74
71
|
"./spacer": "./src/spacer.tsx",
|
|
@@ -184,8 +181,7 @@
|
|
|
184
181
|
"react-dom": "^19.2.0",
|
|
185
182
|
"react-native": ">=0.85.0",
|
|
186
183
|
"react-native-svg": ">=15.0.0",
|
|
187
|
-
"react-native-web": ">=0.20.0"
|
|
188
|
-
"recharts": ">=3.0.0"
|
|
184
|
+
"react-native-web": ">=0.20.0"
|
|
189
185
|
},
|
|
190
186
|
"peerDependenciesMeta": {
|
|
191
187
|
"@lotics/docx": {
|
|
@@ -205,12 +201,10 @@
|
|
|
205
201
|
},
|
|
206
202
|
"react-native-svg": {
|
|
207
203
|
"optional": true
|
|
208
|
-
},
|
|
209
|
-
"recharts": {
|
|
210
|
-
"optional": true
|
|
211
204
|
}
|
|
212
205
|
},
|
|
213
206
|
"scripts": {
|
|
207
|
+
"dev": "vite",
|
|
214
208
|
"typecheck": "tsgo --noEmit",
|
|
215
209
|
"lint": "oxlint",
|
|
216
210
|
"test": "vitest run"
|
|
@@ -218,6 +212,11 @@
|
|
|
218
212
|
"devDependencies": {
|
|
219
213
|
"@lotics/docx": "^0.1.0",
|
|
220
214
|
"@lotics/xlsx": "^0.1.0",
|
|
221
|
-
"
|
|
215
|
+
"@types/react-dom": "~19.2.2",
|
|
216
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
217
|
+
"lucide-react": "^0.562.0",
|
|
218
|
+
"react-dom": "^19.2.0",
|
|
219
|
+
"react-native-web": "^0.21.0",
|
|
220
|
+
"vite": "^7.2.4"
|
|
222
221
|
}
|
|
223
222
|
}
|
package/src/card.tsx
CHANGED
|
@@ -1,32 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StyleProp, StyleSheet, View, ViewStyle } from "react-native";
|
|
2
2
|
import { colors } from "./colors";
|
|
3
3
|
|
|
4
4
|
interface CardProps {
|
|
5
5
|
children: React.ReactNode;
|
|
6
6
|
testID?: string;
|
|
7
|
-
onPress?: () => void;
|
|
8
7
|
style?: StyleProp<ViewStyle>;
|
|
9
8
|
}
|
|
10
9
|
|
|
10
|
+
/**
|
|
11
|
+
* A bordered presentational surface — view only. It never handles interaction:
|
|
12
|
+
* for a pressable/selectable card use CardSelectItem (a real focusable button),
|
|
13
|
+
* or compose PressableHighlight for a bespoke action.
|
|
14
|
+
*/
|
|
11
15
|
export function Card(props: CardProps) {
|
|
12
|
-
const { children, testID,
|
|
13
|
-
|
|
14
|
-
if (onPress) {
|
|
15
|
-
return (
|
|
16
|
-
<Pressable
|
|
17
|
-
testID={testID}
|
|
18
|
-
onPress={() => {
|
|
19
|
-
onPress();
|
|
20
|
-
}}
|
|
21
|
-
style={(state) => {
|
|
22
|
-
const hovered = (state as { hovered?: boolean }).hovered;
|
|
23
|
-
return [styles.container, hovered && styles.hovered, style];
|
|
24
|
-
}}
|
|
25
|
-
>
|
|
26
|
-
{children}
|
|
27
|
-
</Pressable>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
16
|
+
const { children, testID, style } = props;
|
|
30
17
|
|
|
31
18
|
return (
|
|
32
19
|
<View testID={testID} style={[styles.container, style]}>
|
|
@@ -55,7 +42,4 @@ const styles = StyleSheet.create({
|
|
|
55
42
|
"0 1px 2px 0 rgba(38,38,38,0.06), 0 4px 12px -2px rgba(38,38,38,0.06)",
|
|
56
43
|
} as ViewStyle),
|
|
57
44
|
},
|
|
58
|
-
hovered: {
|
|
59
|
-
borderColor: colors.zinc["900"],
|
|
60
|
-
},
|
|
61
45
|
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { PressableStateCallbackType, StyleProp, StyleSheet, ViewStyle } from "react-native";
|
|
2
|
+
import { colors } from "./colors";
|
|
3
|
+
import { PressableHighlight } from "./pressable_highlight";
|
|
4
|
+
|
|
5
|
+
interface CardSelectItemProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
onPress: () => void;
|
|
8
|
+
testID?: string;
|
|
9
|
+
style?: StyleProp<ViewStyle>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A bordered, card-shaped button — the selectable row used on auth screens
|
|
14
|
+
* (organization picker, login choices). Always interactive: a real focusable
|
|
15
|
+
* `button` that shows a 2px ring on hover/press, matching the global keyboard
|
|
16
|
+
* focus ring. For a static surface use Card instead.
|
|
17
|
+
*/
|
|
18
|
+
export function CardSelectItem(props: CardSelectItemProps) {
|
|
19
|
+
const { children, onPress, testID, style } = props;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<PressableHighlight
|
|
23
|
+
testID={testID}
|
|
24
|
+
accessibilityRole="button"
|
|
25
|
+
onPress={onPress}
|
|
26
|
+
style={(state: PressableStateCallbackType) => {
|
|
27
|
+
const hovered = (state as { hovered?: boolean }).hovered;
|
|
28
|
+
const active = hovered || state.pressed;
|
|
29
|
+
return [styles.container, active && styles.ring, style];
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</PressableHighlight>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const styles = StyleSheet.create({
|
|
38
|
+
container: {
|
|
39
|
+
padding: 16,
|
|
40
|
+
borderRadius: 8,
|
|
41
|
+
backgroundColor: colors.background,
|
|
42
|
+
borderColor: colors.border,
|
|
43
|
+
borderWidth: 1,
|
|
44
|
+
},
|
|
45
|
+
// A 2px ring flush against the border (spread 2, no offset/blur) — the same
|
|
46
|
+
// weight and color as the global `:focus-visible` outline, so hover, press,
|
|
47
|
+
// and keyboard focus all read as one consistent ring rather than a thin
|
|
48
|
+
// border-darken. boxShadow keeps it layout-neutral (no 1px→2px reflow).
|
|
49
|
+
ring: {
|
|
50
|
+
...({ boxShadow: `0 0 0 2px ${colors.zinc["900"]}` } as ViewStyle),
|
|
51
|
+
},
|
|
52
|
+
});
|
package/src/column_filter.tsx
CHANGED
|
@@ -155,7 +155,6 @@ export function ColumnFilter(props: ColumnFilterProps) {
|
|
|
155
155
|
) : (
|
|
156
156
|
<PickerMenu
|
|
157
157
|
multi
|
|
158
|
-
enableSearch
|
|
159
158
|
options={column.options ?? []}
|
|
160
159
|
value={value?.kind === "select" ? value.selected : []}
|
|
161
160
|
onValueChange={(selected) => onChange({ kind: "select", selected })}
|