@lssm/lib.presentation-runtime-react-native 0.0.0-canary-20251120170226
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 +30 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +127 -0
- package/dist/index.js.map +1 -0
- package/dist/nativewind-env.d.ts +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @lssm/lib.presentation-runtime-react-native
|
|
2
|
+
|
|
3
|
+
React Native bindings for ContractSpec presentations.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
To render ContractSpec-defined UIs in React Native (Expo) applications, using native components from `@lssm/lib.ui-kit`.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @lssm/lib.presentation-runtime-react-native
|
|
13
|
+
# or
|
|
14
|
+
bun add @lssm/lib.presentation-runtime-react-native
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Key Concepts
|
|
18
|
+
|
|
19
|
+
- **Native Optimized**: Uses native navigation patterns where applicable.
|
|
20
|
+
- **Universal**: API parity with `@lssm/lib.presentation-runtime-react` where possible.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Similar to the React runtime, but imports from this package.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { useWorkflow } from '@lssm/lib.presentation-runtime-react-native';
|
|
28
|
+
// ... usage
|
|
29
|
+
```
|
|
30
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { DefaultValues, Resolver, UseFormReturn } from "react-hook-form";
|
|
3
|
+
import { ListFetcher, ListState } from "@lssm/lib.presentation-runtime-core";
|
|
4
|
+
|
|
5
|
+
//#region src/index.d.ts
|
|
6
|
+
interface UsePresentationControllerOpts<TFilters extends Record<string, unknown>, TVars, TItem> {
|
|
7
|
+
defaults: ListState<TFilters>;
|
|
8
|
+
form: {
|
|
9
|
+
defaultValues: DefaultValues<TFilters>;
|
|
10
|
+
resolver?: Resolver<TFilters>;
|
|
11
|
+
};
|
|
12
|
+
toVariables: (input: ListState<TFilters>) => TVars;
|
|
13
|
+
fetcher: ListFetcher<TVars, TItem>;
|
|
14
|
+
toChips?: (filters: TFilters, setFilter: (key: keyof TFilters, value: TFilters[keyof TFilters] | null) => void) => {
|
|
15
|
+
key: string;
|
|
16
|
+
label: React.ReactNode;
|
|
17
|
+
onRemove?: () => void;
|
|
18
|
+
}[];
|
|
19
|
+
useUrlState: (args: {
|
|
20
|
+
defaults: ListState<TFilters>;
|
|
21
|
+
replace?: boolean;
|
|
22
|
+
}) => {
|
|
23
|
+
state: ListState<TFilters>;
|
|
24
|
+
setState: (next: Partial<ListState<TFilters>>) => void;
|
|
25
|
+
setFilter: (key: keyof TFilters, value: TFilters[keyof TFilters] | null) => void;
|
|
26
|
+
clearFilters: () => void;
|
|
27
|
+
};
|
|
28
|
+
replace?: boolean;
|
|
29
|
+
}
|
|
30
|
+
declare function usePresentationController<TFilters extends Record<string, unknown>, TVars, TItem>({
|
|
31
|
+
defaults,
|
|
32
|
+
form: formOpts,
|
|
33
|
+
toVariables,
|
|
34
|
+
fetcher,
|
|
35
|
+
toChips,
|
|
36
|
+
useUrlState,
|
|
37
|
+
replace
|
|
38
|
+
}: UsePresentationControllerOpts<TFilters, TVars, TItem>): {
|
|
39
|
+
readonly form: UseFormReturn<TFilters>;
|
|
40
|
+
readonly url: {
|
|
41
|
+
state: ListState<TFilters>;
|
|
42
|
+
setState: (next: Partial<ListState<TFilters>>) => void;
|
|
43
|
+
setFilter: (key: keyof TFilters, value: TFilters[keyof TFilters] | null) => void;
|
|
44
|
+
clearFilters: () => void;
|
|
45
|
+
};
|
|
46
|
+
readonly variables: TVars;
|
|
47
|
+
readonly data: TItem[];
|
|
48
|
+
readonly loading: boolean;
|
|
49
|
+
readonly error: unknown;
|
|
50
|
+
readonly totalItems: number | undefined;
|
|
51
|
+
readonly totalPages: number | undefined;
|
|
52
|
+
readonly refetch: () => Promise<void>;
|
|
53
|
+
readonly chips: {
|
|
54
|
+
key: string;
|
|
55
|
+
label: React.ReactNode;
|
|
56
|
+
onRemove?: () => void;
|
|
57
|
+
}[];
|
|
58
|
+
readonly setSearch: (q: string) => void;
|
|
59
|
+
readonly submitFilters: (e?: React.BaseSyntheticEvent) => Promise<void>;
|
|
60
|
+
readonly clearAll: () => void;
|
|
61
|
+
};
|
|
62
|
+
interface UseListCoordinatorOpts<TFilters extends Record<string, unknown>, TVars> {
|
|
63
|
+
defaults: ListState<TFilters>;
|
|
64
|
+
form: {
|
|
65
|
+
defaultValues: DefaultValues<TFilters>;
|
|
66
|
+
resolver?: Resolver<TFilters>;
|
|
67
|
+
};
|
|
68
|
+
toVariables: (input: ListState<TFilters>) => TVars;
|
|
69
|
+
toChips?: (filters: TFilters, setFilter: (key: keyof TFilters, value: TFilters[keyof TFilters] | null) => void) => {
|
|
70
|
+
key: string;
|
|
71
|
+
label: React.ReactNode;
|
|
72
|
+
onRemove?: () => void;
|
|
73
|
+
}[];
|
|
74
|
+
useUrlState: (args: {
|
|
75
|
+
defaults: ListState<TFilters>;
|
|
76
|
+
replace?: boolean;
|
|
77
|
+
}) => {
|
|
78
|
+
state: ListState<TFilters>;
|
|
79
|
+
setState: (next: Partial<ListState<TFilters>>) => void;
|
|
80
|
+
setFilter: (key: keyof TFilters, value: TFilters[keyof TFilters] | null) => void;
|
|
81
|
+
clearFilters: () => void;
|
|
82
|
+
};
|
|
83
|
+
replace?: boolean;
|
|
84
|
+
}
|
|
85
|
+
declare function useListCoordinator<TFilters extends Record<string, unknown>, TVars>({
|
|
86
|
+
defaults,
|
|
87
|
+
form: formOpts,
|
|
88
|
+
toVariables,
|
|
89
|
+
toChips,
|
|
90
|
+
useUrlState,
|
|
91
|
+
replace
|
|
92
|
+
}: UseListCoordinatorOpts<TFilters, TVars>): {
|
|
93
|
+
readonly form: UseFormReturn<TFilters>;
|
|
94
|
+
readonly url: {
|
|
95
|
+
state: ListState<TFilters>;
|
|
96
|
+
setState: (next: Partial<ListState<TFilters>>) => void;
|
|
97
|
+
setFilter: (key: keyof TFilters, value: TFilters[keyof TFilters] | null) => void;
|
|
98
|
+
clearFilters: () => void;
|
|
99
|
+
};
|
|
100
|
+
readonly variables: TVars;
|
|
101
|
+
readonly chips: {
|
|
102
|
+
key: string;
|
|
103
|
+
label: React.ReactNode;
|
|
104
|
+
onRemove?: () => void;
|
|
105
|
+
}[];
|
|
106
|
+
readonly setSearch: (q: string) => void;
|
|
107
|
+
readonly submitFilters: (e?: React.BaseSyntheticEvent) => Promise<void>;
|
|
108
|
+
readonly clearAll: () => void;
|
|
109
|
+
};
|
|
110
|
+
//#endregion
|
|
111
|
+
export { UseListCoordinatorOpts, UsePresentationControllerOpts, useListCoordinator, usePresentationController };
|
|
112
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useForm } from "@lssm/lib.ui-kit/ui/form";
|
|
3
|
+
|
|
4
|
+
//#region src/index.ts
|
|
5
|
+
function usePresentationController({ defaults, form: formOpts, toVariables, fetcher, toChips, useUrlState, replace }) {
|
|
6
|
+
const url = useUrlState({
|
|
7
|
+
defaults,
|
|
8
|
+
replace
|
|
9
|
+
});
|
|
10
|
+
const form = useForm({
|
|
11
|
+
defaultValues: formOpts.defaultValues,
|
|
12
|
+
resolver: formOpts.resolver
|
|
13
|
+
});
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
form.reset({
|
|
16
|
+
...form.getValues(),
|
|
17
|
+
...url.state.filters
|
|
18
|
+
});
|
|
19
|
+
}, [url.state.filters]);
|
|
20
|
+
const submitFilters = form.handleSubmit((values) => {
|
|
21
|
+
url.setState({
|
|
22
|
+
filters: values,
|
|
23
|
+
page: 1
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
const setSearch = React.useCallback((q) => url.setState({
|
|
27
|
+
q,
|
|
28
|
+
page: 1
|
|
29
|
+
}), [url]);
|
|
30
|
+
const variables = React.useMemo(() => toVariables(url.state), [url.state, toVariables]);
|
|
31
|
+
const [data, setData] = React.useState([]);
|
|
32
|
+
const [loading, setLoading] = React.useState(false);
|
|
33
|
+
const [error, setError] = React.useState(null);
|
|
34
|
+
const [totalItems, setTotalItems] = React.useState(void 0);
|
|
35
|
+
const [totalPages, setTotalPages] = React.useState(void 0);
|
|
36
|
+
const refetch = React.useCallback(async () => {
|
|
37
|
+
setLoading(true);
|
|
38
|
+
setError(null);
|
|
39
|
+
try {
|
|
40
|
+
const out = await fetcher(variables);
|
|
41
|
+
setData(out.items);
|
|
42
|
+
setTotalItems(out.totalItems);
|
|
43
|
+
setTotalPages(out.totalPages);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
setError(e);
|
|
46
|
+
} finally {
|
|
47
|
+
setLoading(false);
|
|
48
|
+
}
|
|
49
|
+
}, [variables, fetcher]);
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
refetch();
|
|
52
|
+
}, [refetch]);
|
|
53
|
+
return {
|
|
54
|
+
form,
|
|
55
|
+
url,
|
|
56
|
+
variables,
|
|
57
|
+
data,
|
|
58
|
+
loading,
|
|
59
|
+
error,
|
|
60
|
+
totalItems,
|
|
61
|
+
totalPages,
|
|
62
|
+
refetch,
|
|
63
|
+
chips: React.useMemo(() => toChips ? toChips(url.state.filters || {}, url.setFilter) : [], [url.state.filters, toChips]),
|
|
64
|
+
setSearch,
|
|
65
|
+
submitFilters,
|
|
66
|
+
clearAll: React.useCallback(() => {
|
|
67
|
+
form.reset(formOpts.defaultValues);
|
|
68
|
+
url.setState({
|
|
69
|
+
filters: {},
|
|
70
|
+
page: 1
|
|
71
|
+
});
|
|
72
|
+
}, [
|
|
73
|
+
form,
|
|
74
|
+
formOpts.defaultValues,
|
|
75
|
+
url
|
|
76
|
+
])
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function useListCoordinator({ defaults, form: formOpts, toVariables, toChips, useUrlState, replace }) {
|
|
80
|
+
const url = useUrlState({
|
|
81
|
+
defaults,
|
|
82
|
+
replace
|
|
83
|
+
});
|
|
84
|
+
const form = useForm({
|
|
85
|
+
defaultValues: formOpts.defaultValues,
|
|
86
|
+
resolver: formOpts.resolver
|
|
87
|
+
});
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
form.reset({
|
|
90
|
+
...form.getValues(),
|
|
91
|
+
...url.state.filters
|
|
92
|
+
});
|
|
93
|
+
}, [url.state.filters]);
|
|
94
|
+
const submitFilters = form.handleSubmit((values) => {
|
|
95
|
+
url.setState({
|
|
96
|
+
filters: values,
|
|
97
|
+
page: 1
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
const setSearch = React.useCallback((q) => url.setState({
|
|
101
|
+
q,
|
|
102
|
+
page: 1
|
|
103
|
+
}), [url]);
|
|
104
|
+
return {
|
|
105
|
+
form,
|
|
106
|
+
url,
|
|
107
|
+
variables: React.useMemo(() => toVariables(url.state), [url.state, toVariables]),
|
|
108
|
+
chips: React.useMemo(() => toChips ? toChips(url.state.filters || {}, url.setFilter) : [], [url.state.filters, toChips]),
|
|
109
|
+
setSearch,
|
|
110
|
+
submitFilters,
|
|
111
|
+
clearAll: React.useCallback(() => {
|
|
112
|
+
form.reset(formOpts.defaultValues);
|
|
113
|
+
url.setState({
|
|
114
|
+
filters: {},
|
|
115
|
+
page: 1
|
|
116
|
+
});
|
|
117
|
+
}, [
|
|
118
|
+
form,
|
|
119
|
+
formOpts.defaultValues,
|
|
120
|
+
url
|
|
121
|
+
])
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
export { useListCoordinator, usePresentationController };
|
|
127
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import * as React from 'react';\nimport type { DefaultValues, Resolver, UseFormReturn } from 'react-hook-form';\nimport { useForm } from '@lssm/lib.ui-kit/ui/form';\nimport type {\n ListFetcher,\n ListState,\n} from '@lssm/lib.presentation-runtime-core';\n\nexport interface UsePresentationControllerOpts<\n TFilters extends Record<string, unknown>,\n TVars,\n TItem,\n> {\n defaults: ListState<TFilters>;\n form: {\n defaultValues: DefaultValues<TFilters>;\n resolver?: Resolver<TFilters>;\n };\n toVariables: (input: ListState<TFilters>) => TVars;\n fetcher: ListFetcher<TVars, TItem>;\n toChips?: (\n filters: TFilters,\n setFilter: (\n key: keyof TFilters,\n value: TFilters[keyof TFilters] | null\n ) => void\n ) => { key: string; label: React.ReactNode; onRemove?: () => void }[];\n useUrlState: (args: { defaults: ListState<TFilters>; replace?: boolean }) => {\n state: ListState<TFilters>;\n setState: (next: Partial<ListState<TFilters>>) => void;\n setFilter: (\n key: keyof TFilters,\n value: TFilters[keyof TFilters] | null\n ) => void;\n clearFilters: () => void;\n };\n replace?: boolean;\n}\n\nexport function usePresentationController<\n TFilters extends Record<string, unknown>,\n TVars,\n TItem,\n>({\n defaults,\n form: formOpts,\n toVariables,\n fetcher,\n toChips,\n useUrlState,\n replace,\n}: UsePresentationControllerOpts<TFilters, TVars, TItem>) {\n const url = useUrlState({ defaults, replace });\n const form = useForm<TFilters>({\n defaultValues: formOpts.defaultValues,\n resolver: formOpts.resolver as any,\n } as any);\n\n React.useEffect(() => {\n form.reset({ ...(form.getValues() as any), ...(url.state.filters as any) });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url.state.filters]);\n\n const submitFilters = form.handleSubmit((values) => {\n url.setState({ filters: values as TFilters, page: 1 });\n });\n\n const setSearch = React.useCallback(\n (q: string) => url.setState({ q, page: 1 }),\n [url]\n );\n const variables = React.useMemo(\n () => toVariables(url.state),\n [url.state, toVariables]\n );\n\n const [data, setData] = React.useState<TItem[]>([]);\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<unknown>(null);\n const [totalItems, setTotalItems] = React.useState<number | undefined>(\n undefined\n );\n const [totalPages, setTotalPages] = React.useState<number | undefined>(\n undefined\n );\n\n const refetch = React.useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const out = await fetcher(variables);\n setData(out.items);\n setTotalItems(out.totalItems);\n setTotalPages(out.totalPages);\n } catch (e) {\n setError(e);\n } finally {\n setLoading(false);\n }\n }, [variables, fetcher]);\n\n React.useEffect(() => {\n void refetch();\n }, [refetch]);\n\n const chips = React.useMemo(\n () =>\n toChips\n ? toChips(\n (url.state.filters as TFilters) || ({} as any),\n url.setFilter as any\n )\n : [],\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [url.state.filters, toChips]\n );\n\n const clearAll = React.useCallback(() => {\n form.reset(formOpts.defaultValues as TFilters);\n url.setState({ filters: {} as TFilters, page: 1 });\n }, [form, formOpts.defaultValues, url]);\n\n return {\n form: form as UseFormReturn<TFilters>,\n url,\n variables,\n data,\n loading,\n error,\n totalItems,\n totalPages,\n refetch,\n chips,\n setSearch,\n submitFilters,\n clearAll,\n } as const;\n}\n\nexport interface UseListCoordinatorOpts<\n TFilters extends Record<string, unknown>,\n TVars,\n> {\n defaults: ListState<TFilters>;\n form: {\n defaultValues: DefaultValues<TFilters>;\n resolver?: Resolver<TFilters>;\n };\n toVariables: (input: ListState<TFilters>) => TVars;\n toChips?: (\n filters: TFilters,\n setFilter: (\n key: keyof TFilters,\n value: TFilters[keyof TFilters] | null\n ) => void\n ) => { key: string; label: React.ReactNode; onRemove?: () => void }[];\n useUrlState: (args: { defaults: ListState<TFilters>; replace?: boolean }) => {\n state: ListState<TFilters>;\n setState: (next: Partial<ListState<TFilters>>) => void;\n setFilter: (\n key: keyof TFilters,\n value: TFilters[keyof TFilters] | null\n ) => void;\n clearFilters: () => void;\n };\n replace?: boolean;\n}\n\nexport function useListCoordinator<\n TFilters extends Record<string, unknown>,\n TVars,\n>({\n defaults,\n form: formOpts,\n toVariables,\n toChips,\n useUrlState,\n replace,\n}: UseListCoordinatorOpts<TFilters, TVars>) {\n const url = useUrlState({ defaults, replace });\n const form = useForm<TFilters>({\n defaultValues: formOpts.defaultValues,\n resolver: formOpts.resolver as any,\n } as any);\n\n React.useEffect(() => {\n form.reset({ ...(form.getValues() as any), ...(url.state.filters as any) });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url.state.filters]);\n\n const submitFilters = form.handleSubmit((values) => {\n url.setState({ filters: values as TFilters, page: 1 });\n });\n\n const setSearch = React.useCallback(\n (q: string) => url.setState({ q, page: 1 }),\n [url]\n );\n const variables = React.useMemo(\n () => toVariables(url.state),\n [url.state, toVariables]\n );\n\n const chips = React.useMemo(\n () =>\n toChips\n ? toChips(\n (url.state.filters as TFilters) || ({} as any),\n url.setFilter as any\n )\n : [],\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [url.state.filters, toChips]\n );\n\n const clearAll = React.useCallback(() => {\n form.reset(formOpts.defaultValues as TFilters);\n url.setState({ filters: {} as TFilters, page: 1 });\n }, [form, formOpts.defaultValues, url]);\n\n return {\n form: form as UseFormReturn<TFilters>,\n url,\n variables,\n chips,\n setSearch,\n submitFilters,\n clearAll,\n } as const;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lssm/lib.presentation-runtime-react-native",
|
|
3
|
+
"version": "0.0.0-canary-20251120170226",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/nativewind-env.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "bun build:bundle && bun build:types",
|
|
11
|
+
"build:bundle": "tsdown",
|
|
12
|
+
"build:types": "tsc --noEmit -p tsconfig.json",
|
|
13
|
+
"dev": "bun run build:bundle --watch",
|
|
14
|
+
"lint": "bun run lint:fix",
|
|
15
|
+
"lint:fix": "eslint src --fix",
|
|
16
|
+
"lint:check": "eslint src"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"react": "^19.1.0",
|
|
20
|
+
"react-native": "*",
|
|
21
|
+
"react-hook-form": "7.66.0",
|
|
22
|
+
"zod": "^4.1.5",
|
|
23
|
+
"@lssm/lib.presentation-runtime-core": "0.0.0-canary-20251120170226"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@lssm/lib.presentation-runtime-core": "0.0.0-canary-20251120170226",
|
|
27
|
+
"@lssm/lib.ui-kit": "0.0.0-canary-20251120170226"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"exports": {
|
|
34
|
+
".": "./dist/index.js",
|
|
35
|
+
"./*": "./*"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"exports": {
|
|
39
|
+
".": "./dist/index.js",
|
|
40
|
+
"./*": "./*"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|