@lotics/app-sdk 0.23.0 → 0.25.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/dist/src/hooks.d.ts +40 -0
- package/dist/src/hooks.js +8 -2
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.js +1 -1
- package/dist/src/row.d.ts +21 -0
- package/dist/src/row.js +29 -0
- package/package.json +1 -1
package/dist/src/hooks.d.ts
CHANGED
|
@@ -28,6 +28,36 @@ interface QueryState<R> {
|
|
|
28
28
|
/** True while a `loadMore` request is in flight. */
|
|
29
29
|
loadingMore: boolean;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* One sort key — the wire shape of the query RPC's `sort`. The server applies
|
|
33
|
+
* these AFTER the named query, bounded to the query's output columns (an
|
|
34
|
+
* un-projected `field_key` is rejected), so an app can sort by any column it
|
|
35
|
+
* actually selects without the query template declaring it.
|
|
36
|
+
*/
|
|
37
|
+
export interface QuerySortKey {
|
|
38
|
+
field_key: string;
|
|
39
|
+
order: "asc" | "desc";
|
|
40
|
+
}
|
|
41
|
+
/** A filter condition over one output column (wire shape of a filter node). */
|
|
42
|
+
export interface QueryFilterCondition {
|
|
43
|
+
node_type: "condition";
|
|
44
|
+
field_key: string;
|
|
45
|
+
type?: string;
|
|
46
|
+
operator: string;
|
|
47
|
+
value?: unknown;
|
|
48
|
+
}
|
|
49
|
+
/** A boolean group of filter nodes (wire shape — recursive). */
|
|
50
|
+
export interface QueryFilterGroup {
|
|
51
|
+
node_type: "group";
|
|
52
|
+
logic: "and" | "or";
|
|
53
|
+
children: Array<QueryFilterCondition | QueryFilterGroup>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Runtime filter applied AFTER the named query, bounded to its output columns
|
|
57
|
+
* (same exposure invariant as `sort`). Build a group from per-column filters
|
|
58
|
+
* with `columnFilterToConditions` (`@lotics/ui/grid/column_filter`).
|
|
59
|
+
*/
|
|
60
|
+
export type QueryFilter = QueryFilterCondition | QueryFilterGroup;
|
|
31
61
|
/** Options for `useQuery`. */
|
|
32
62
|
export interface QueryOptions {
|
|
33
63
|
/**
|
|
@@ -53,6 +83,16 @@ export interface QueryOptions {
|
|
|
53
83
|
* re-run is wasted work and a visible reload.
|
|
54
84
|
*/
|
|
55
85
|
revalidateOnFocus?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Sort the result by output columns at runtime. Changing it re-queries (it is
|
|
88
|
+
* part of the cache key). Empty/omitted leaves the query's own order intact.
|
|
89
|
+
*/
|
|
90
|
+
sort?: QuerySortKey[];
|
|
91
|
+
/**
|
|
92
|
+
* Filter the result by output columns at runtime. Changing it re-queries.
|
|
93
|
+
* Compose from per-column UI filters via `columnFilterToConditions`.
|
|
94
|
+
*/
|
|
95
|
+
filter?: QueryFilter;
|
|
56
96
|
}
|
|
57
97
|
/**
|
|
58
98
|
* Trigger a workflow by alias from the app's manifest.
|
package/dist/src/hooks.js
CHANGED
|
@@ -36,6 +36,8 @@ export function useQuery(alias, params, opts) {
|
|
|
36
36
|
const pageSize = opts?.pageSize;
|
|
37
37
|
const enabled = opts?.enabled ?? true;
|
|
38
38
|
const revalidateOnFocus = opts?.revalidateOnFocus ?? true;
|
|
39
|
+
const sort = opts?.sort && opts.sort.length > 0 ? opts.sort : undefined;
|
|
40
|
+
const filter = opts?.filter;
|
|
39
41
|
// A registered fixture short-circuits the network in mock / dev mode.
|
|
40
42
|
const mockRows = getMockRows(alias);
|
|
41
43
|
// The cache key is (alias, params, pageSize, pageIndex), built HERE so an app
|
|
@@ -52,15 +54,19 @@ export function useQuery(alias, params, opts) {
|
|
|
52
54
|
if (index > 0 && (pageSize == null || prev == null || prev.rows.length < pageSize)) {
|
|
53
55
|
return null;
|
|
54
56
|
}
|
|
55
|
-
|
|
57
|
+
// sort/filter join the key so a re-sort or re-filter is a distinct cache
|
|
58
|
+
// entry — SWR refetches instead of serving the previous order/subset.
|
|
59
|
+
return ["app-query", alias, params ?? {}, pageSize ?? null, sort ?? null, filter ?? null, index];
|
|
56
60
|
};
|
|
57
61
|
const swr = useSWRInfinite(getKey, (key) => {
|
|
58
|
-
const index = Number(key[
|
|
62
|
+
const index = Number(key[6]);
|
|
59
63
|
return rpc("query", {
|
|
60
64
|
alias,
|
|
61
65
|
params: params ?? {},
|
|
62
66
|
limit: pageSize,
|
|
63
67
|
offset: pageSize != null ? index * pageSize : 0,
|
|
68
|
+
sort,
|
|
69
|
+
filter,
|
|
64
70
|
});
|
|
65
71
|
}, {
|
|
66
72
|
// loadMore appends pages — don't re-fetch earlier pages when `size` grows.
|
package/dist/src/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
export { mount } from "./mount.js";
|
|
18
18
|
export type { MountOptions } from "./mount.js";
|
|
19
19
|
export { useWorkflow, useQuery, useFileUpload, useMembers } from "./hooks.js";
|
|
20
|
-
export type { UploadedFile, QueryOptions, WorkflowResult, MembersOptions } from "./hooks.js";
|
|
20
|
+
export type { UploadedFile, QueryOptions, QuerySortKey, QueryFilter, QueryFilterCondition, QueryFilterGroup, WorkflowResult, MembersOptions, } from "./hooks.js";
|
|
21
21
|
export { rpc } from "./rpc.js";
|
|
22
22
|
export type { RpcOp } from "./rpc.js";
|
|
23
23
|
export { openExternal } from "./open_external.js";
|
|
@@ -27,8 +27,8 @@ export { readSelect } from "./select.js";
|
|
|
27
27
|
export type { ResolvedOption } from "./select.js";
|
|
28
28
|
export type { AppFixture } from "./mock.js";
|
|
29
29
|
export type { AppWorkflows, AppQueries } from "./types.js";
|
|
30
|
-
export { row, readLinks } from "./row.js";
|
|
31
|
-
export type { ResolvedLink } from "./row.js";
|
|
30
|
+
export { row, readLinks, readFiles } from "./row.js";
|
|
31
|
+
export type { ResolvedLink, AppFile } from "./row.js";
|
|
32
32
|
export { useOptimistic } from "./use_optimistic.js";
|
|
33
33
|
export type { OptimisticApi } from "./use_optimistic.js";
|
|
34
34
|
export { useRecents } from "./use_recents.js";
|
package/dist/src/index.js
CHANGED
|
@@ -20,6 +20,6 @@ export { rpc } from "./rpc.js";
|
|
|
20
20
|
export { openExternal } from "./open_external.js";
|
|
21
21
|
export { readMembers } from "./members.js";
|
|
22
22
|
export { readSelect } from "./select.js";
|
|
23
|
-
export { row, readLinks } from "./row.js";
|
|
23
|
+
export { row, readLinks, readFiles } from "./row.js";
|
|
24
24
|
export { useOptimistic } from "./use_optimistic.js";
|
|
25
25
|
export { useRecents } from "./use_recents.js";
|
package/dist/src/row.d.ts
CHANGED
|
@@ -44,6 +44,27 @@ export interface ResolvedLink {
|
|
|
44
44
|
declare function link(v: unknown): ResolvedLink | null;
|
|
45
45
|
/** select_record_link → ALL linked records as `{ id, display }[]` (empty if none). */
|
|
46
46
|
export declare function readLinks(v: unknown): ResolvedLink[];
|
|
47
|
+
/**
|
|
48
|
+
* A `files`-field cell entry, as the app query serializes it. The server
|
|
49
|
+
* presigns each file (24h) so `url`/`thumbnail_url` load directly — including
|
|
50
|
+
* from the sandboxed app iframe and for public apps — so the app can preview or
|
|
51
|
+
* download without deriving its own URL.
|
|
52
|
+
*/
|
|
53
|
+
export interface AppFile {
|
|
54
|
+
id: string;
|
|
55
|
+
filename: string;
|
|
56
|
+
mime_type: string;
|
|
57
|
+
/** Presigned serving URL — render in an <Image>/preview or pass to openExternal. */
|
|
58
|
+
url: string;
|
|
59
|
+
/** Presigned thumbnail URL for images, when the server produced one. */
|
|
60
|
+
thumbnail_url?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* files field → the attached files with their presigned `url` (empty if none).
|
|
64
|
+
* Skips entries the server didn't presign (no `url`) so a consumer never renders
|
|
65
|
+
* an unservable file. Map to `@lotics/ui` `DisplayFile` for FileThumbnail/Gallery.
|
|
66
|
+
*/
|
|
67
|
+
export declare function readFiles(v: unknown): AppFile[];
|
|
47
68
|
export declare const row: {
|
|
48
69
|
opt: typeof opt;
|
|
49
70
|
text: typeof text;
|
package/dist/src/row.js
CHANGED
|
@@ -89,4 +89,33 @@ export function readLinks(v) {
|
|
|
89
89
|
}
|
|
90
90
|
return v.map(asLink).filter((x) => x !== null);
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* files field → the attached files with their presigned `url` (empty if none).
|
|
94
|
+
* Skips entries the server didn't presign (no `url`) so a consumer never renders
|
|
95
|
+
* an unservable file. Map to `@lotics/ui` `DisplayFile` for FileThumbnail/Gallery.
|
|
96
|
+
*/
|
|
97
|
+
export function readFiles(v) {
|
|
98
|
+
if (!Array.isArray(v))
|
|
99
|
+
return [];
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const f of v) {
|
|
102
|
+
if (!f || typeof f !== "object")
|
|
103
|
+
continue;
|
|
104
|
+
const id = f.id;
|
|
105
|
+
const url = f.url;
|
|
106
|
+
if (typeof id !== "string" || !id || typeof url !== "string" || !url)
|
|
107
|
+
continue;
|
|
108
|
+
const filename = f.filename;
|
|
109
|
+
const mime = f.mime_type;
|
|
110
|
+
const thumb = f.thumbnail_url;
|
|
111
|
+
out.push({
|
|
112
|
+
id,
|
|
113
|
+
filename: typeof filename === "string" ? filename : "",
|
|
114
|
+
mime_type: typeof mime === "string" ? mime : "",
|
|
115
|
+
url,
|
|
116
|
+
thumbnail_url: typeof thumb === "string" ? thumb : undefined,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
92
121
|
export const row = { opt, text, num, bool, date, link };
|