@trellisjs/plugin-datasource 0.1.0-alpha.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/dist/index.d.mts +84 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +161 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +132 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { TrellisAPI } from '@trellisjs/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trellis 標準查詢協定 — 前端向後端發送的查詢格式。
|
|
5
|
+
* 簡潔、可移植、不依賴任何後端框架。
|
|
6
|
+
*/
|
|
7
|
+
interface TrellisQuery {
|
|
8
|
+
/** 目前頁碼(從 1 開始) */
|
|
9
|
+
page: number;
|
|
10
|
+
/** 每頁筆數 */
|
|
11
|
+
pageSize: number;
|
|
12
|
+
/** 排序條件 */
|
|
13
|
+
sort?: TrellisSortQuery[];
|
|
14
|
+
/** 篩選條件 */
|
|
15
|
+
filter?: TrellisFilterQuery;
|
|
16
|
+
}
|
|
17
|
+
/** 單一排序條件 */
|
|
18
|
+
interface TrellisSortQuery {
|
|
19
|
+
columnId: string;
|
|
20
|
+
direction: 'asc' | 'desc';
|
|
21
|
+
}
|
|
22
|
+
/** 篩選條件 */
|
|
23
|
+
interface TrellisFilterQuery {
|
|
24
|
+
/** 全域搜尋關鍵字 */
|
|
25
|
+
global?: string;
|
|
26
|
+
/** 單欄篩選值 */
|
|
27
|
+
columns?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 後端標準回應格式。
|
|
31
|
+
* 所有 server-side 模式的後端都必須回傳這個結構。
|
|
32
|
+
*/
|
|
33
|
+
interface TrellisResponse<T = Record<string, unknown>> {
|
|
34
|
+
/** 當前頁的資料列 */
|
|
35
|
+
data: T[];
|
|
36
|
+
/** 總筆數(未篩選) */
|
|
37
|
+
total: number;
|
|
38
|
+
/** 篩選後筆數 */
|
|
39
|
+
filtered: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 資料源介面 — 所有資料源都要實作這個介面。
|
|
43
|
+
*/
|
|
44
|
+
interface TrellisDatasource<T = Record<string, unknown>> {
|
|
45
|
+
/** 根據查詢條件取得資料 */
|
|
46
|
+
fetch(query: TrellisQuery): Promise<TrellisResponse<T>>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 遠端資料源設定
|
|
50
|
+
*/
|
|
51
|
+
interface RemoteDatasourceOptions {
|
|
52
|
+
/** API 端點 URL */
|
|
53
|
+
url: string;
|
|
54
|
+
/** HTTP 方法(預設 POST) */
|
|
55
|
+
method?: 'GET' | 'POST';
|
|
56
|
+
/** 自訂請求標頭 */
|
|
57
|
+
headers?: Record<string, string>;
|
|
58
|
+
/** 自訂查詢轉換(發送前處理) */
|
|
59
|
+
transformQuery?: (query: TrellisQuery) => unknown;
|
|
60
|
+
/** 自訂回應轉換(接收後處理) */
|
|
61
|
+
transformResponse?: (response: unknown) => TrellisResponse;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 從 Trellis 狀態建構查詢物件
|
|
65
|
+
*/
|
|
66
|
+
declare function buildQueryFromState(api: TrellisAPI): TrellisQuery;
|
|
67
|
+
|
|
68
|
+
interface StaticDatasourceOptions<T> {
|
|
69
|
+
/** 自訂欄位值提取函式 */
|
|
70
|
+
columnAccessors?: Record<string, (row: T) => unknown>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 建立靜態(本地)資料源。
|
|
74
|
+
* 在本地執行排序、篩選、分頁 — 不需要後端。
|
|
75
|
+
*/
|
|
76
|
+
declare function createStaticDatasource<T extends Record<string, unknown>>(data: T[], options?: StaticDatasourceOptions<T>): TrellisDatasource<T>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 建立遠端 API 資料源。
|
|
80
|
+
* 透過 fetch 向後端發送標準化查詢,並解析回應。
|
|
81
|
+
*/
|
|
82
|
+
declare function createRemoteDatasource<T = Record<string, unknown>>(options: RemoteDatasourceOptions): TrellisDatasource<T>;
|
|
83
|
+
|
|
84
|
+
export { type RemoteDatasourceOptions, type TrellisDatasource, type TrellisFilterQuery, type TrellisQuery, type TrellisResponse, type TrellisSortQuery, buildQueryFromState, createRemoteDatasource, createStaticDatasource };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { TrellisAPI } from '@trellisjs/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trellis 標準查詢協定 — 前端向後端發送的查詢格式。
|
|
5
|
+
* 簡潔、可移植、不依賴任何後端框架。
|
|
6
|
+
*/
|
|
7
|
+
interface TrellisQuery {
|
|
8
|
+
/** 目前頁碼(從 1 開始) */
|
|
9
|
+
page: number;
|
|
10
|
+
/** 每頁筆數 */
|
|
11
|
+
pageSize: number;
|
|
12
|
+
/** 排序條件 */
|
|
13
|
+
sort?: TrellisSortQuery[];
|
|
14
|
+
/** 篩選條件 */
|
|
15
|
+
filter?: TrellisFilterQuery;
|
|
16
|
+
}
|
|
17
|
+
/** 單一排序條件 */
|
|
18
|
+
interface TrellisSortQuery {
|
|
19
|
+
columnId: string;
|
|
20
|
+
direction: 'asc' | 'desc';
|
|
21
|
+
}
|
|
22
|
+
/** 篩選條件 */
|
|
23
|
+
interface TrellisFilterQuery {
|
|
24
|
+
/** 全域搜尋關鍵字 */
|
|
25
|
+
global?: string;
|
|
26
|
+
/** 單欄篩選值 */
|
|
27
|
+
columns?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 後端標準回應格式。
|
|
31
|
+
* 所有 server-side 模式的後端都必須回傳這個結構。
|
|
32
|
+
*/
|
|
33
|
+
interface TrellisResponse<T = Record<string, unknown>> {
|
|
34
|
+
/** 當前頁的資料列 */
|
|
35
|
+
data: T[];
|
|
36
|
+
/** 總筆數(未篩選) */
|
|
37
|
+
total: number;
|
|
38
|
+
/** 篩選後筆數 */
|
|
39
|
+
filtered: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 資料源介面 — 所有資料源都要實作這個介面。
|
|
43
|
+
*/
|
|
44
|
+
interface TrellisDatasource<T = Record<string, unknown>> {
|
|
45
|
+
/** 根據查詢條件取得資料 */
|
|
46
|
+
fetch(query: TrellisQuery): Promise<TrellisResponse<T>>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 遠端資料源設定
|
|
50
|
+
*/
|
|
51
|
+
interface RemoteDatasourceOptions {
|
|
52
|
+
/** API 端點 URL */
|
|
53
|
+
url: string;
|
|
54
|
+
/** HTTP 方法(預設 POST) */
|
|
55
|
+
method?: 'GET' | 'POST';
|
|
56
|
+
/** 自訂請求標頭 */
|
|
57
|
+
headers?: Record<string, string>;
|
|
58
|
+
/** 自訂查詢轉換(發送前處理) */
|
|
59
|
+
transformQuery?: (query: TrellisQuery) => unknown;
|
|
60
|
+
/** 自訂回應轉換(接收後處理) */
|
|
61
|
+
transformResponse?: (response: unknown) => TrellisResponse;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 從 Trellis 狀態建構查詢物件
|
|
65
|
+
*/
|
|
66
|
+
declare function buildQueryFromState(api: TrellisAPI): TrellisQuery;
|
|
67
|
+
|
|
68
|
+
interface StaticDatasourceOptions<T> {
|
|
69
|
+
/** 自訂欄位值提取函式 */
|
|
70
|
+
columnAccessors?: Record<string, (row: T) => unknown>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 建立靜態(本地)資料源。
|
|
74
|
+
* 在本地執行排序、篩選、分頁 — 不需要後端。
|
|
75
|
+
*/
|
|
76
|
+
declare function createStaticDatasource<T extends Record<string, unknown>>(data: T[], options?: StaticDatasourceOptions<T>): TrellisDatasource<T>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 建立遠端 API 資料源。
|
|
80
|
+
* 透過 fetch 向後端發送標準化查詢,並解析回應。
|
|
81
|
+
*/
|
|
82
|
+
declare function createRemoteDatasource<T = Record<string, unknown>>(options: RemoteDatasourceOptions): TrellisDatasource<T>;
|
|
83
|
+
|
|
84
|
+
export { type RemoteDatasourceOptions, type TrellisDatasource, type TrellisFilterQuery, type TrellisQuery, type TrellisResponse, type TrellisSortQuery, buildQueryFromState, createRemoteDatasource, createStaticDatasource };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
buildQueryFromState: () => buildQueryFromState,
|
|
24
|
+
createRemoteDatasource: () => createRemoteDatasource,
|
|
25
|
+
createStaticDatasource: () => createStaticDatasource
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/types.ts
|
|
30
|
+
function buildQueryFromState(api) {
|
|
31
|
+
const state = api.getState();
|
|
32
|
+
const query = {
|
|
33
|
+
page: state.pagination.page,
|
|
34
|
+
pageSize: state.pagination.pageSize
|
|
35
|
+
};
|
|
36
|
+
if (state.sorting.direction) {
|
|
37
|
+
query.sort = [{
|
|
38
|
+
columnId: state.sorting.columnId,
|
|
39
|
+
direction: state.sorting.direction
|
|
40
|
+
}];
|
|
41
|
+
}
|
|
42
|
+
if (state.filtering.query || Object.keys(state.filtering.columnFilters).length > 0) {
|
|
43
|
+
query.filter = {};
|
|
44
|
+
if (state.filtering.query) {
|
|
45
|
+
query.filter.global = state.filtering.query;
|
|
46
|
+
}
|
|
47
|
+
if (Object.keys(state.filtering.columnFilters).length > 0) {
|
|
48
|
+
query.filter.columns = { ...state.filtering.columnFilters };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return query;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/static-datasource.ts
|
|
55
|
+
function createStaticDatasource(data, options) {
|
|
56
|
+
const columnAccessors = options?.columnAccessors ?? {};
|
|
57
|
+
function getValue(row, columnId) {
|
|
58
|
+
if (columnAccessors[columnId]) {
|
|
59
|
+
return columnAccessors[columnId](row);
|
|
60
|
+
}
|
|
61
|
+
return row[columnId];
|
|
62
|
+
}
|
|
63
|
+
function compareValues(a, b) {
|
|
64
|
+
if (a == null && b == null) return 0;
|
|
65
|
+
if (a == null) return -1;
|
|
66
|
+
if (b == null) return 1;
|
|
67
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
68
|
+
return String(a).localeCompare(String(b));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
async fetch(query) {
|
|
72
|
+
let result = [...data];
|
|
73
|
+
const total = data.length;
|
|
74
|
+
if (query.filter?.global) {
|
|
75
|
+
const keyword = query.filter.global.toLowerCase();
|
|
76
|
+
result = result.filter(
|
|
77
|
+
(row) => Object.values(row).some(
|
|
78
|
+
(val) => val != null && String(val).toLowerCase().includes(keyword)
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (query.filter?.columns) {
|
|
83
|
+
for (const [colId, filterVal] of Object.entries(query.filter.columns)) {
|
|
84
|
+
if (filterVal == null || filterVal === "") continue;
|
|
85
|
+
const keyword = String(filterVal).toLowerCase();
|
|
86
|
+
result = result.filter((row) => {
|
|
87
|
+
const val = getValue(row, colId);
|
|
88
|
+
return val != null && String(val).toLowerCase().includes(keyword);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const filtered = result.length;
|
|
93
|
+
if (query.sort) {
|
|
94
|
+
for (const sort of query.sort) {
|
|
95
|
+
result.sort((a, b) => {
|
|
96
|
+
const valA = getValue(a, sort.columnId);
|
|
97
|
+
const valB = getValue(b, sort.columnId);
|
|
98
|
+
const cmp = compareValues(valA, valB);
|
|
99
|
+
return sort.direction === "desc" ? -cmp : cmp;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const start = (query.page - 1) * query.pageSize;
|
|
104
|
+
const end = start + query.pageSize;
|
|
105
|
+
const paged = result.slice(start, end);
|
|
106
|
+
return { data: paged, total, filtered };
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/remote-datasource.ts
|
|
112
|
+
function createRemoteDatasource(options) {
|
|
113
|
+
const {
|
|
114
|
+
url,
|
|
115
|
+
method = "POST",
|
|
116
|
+
headers = {},
|
|
117
|
+
transformQuery,
|
|
118
|
+
transformResponse
|
|
119
|
+
} = options;
|
|
120
|
+
return {
|
|
121
|
+
async fetch(query) {
|
|
122
|
+
const finalQuery = transformQuery ? transformQuery(query) : query;
|
|
123
|
+
let response;
|
|
124
|
+
if (method === "GET") {
|
|
125
|
+
const params = new URLSearchParams();
|
|
126
|
+
params.set("page", String(query.page));
|
|
127
|
+
params.set("pageSize", String(query.pageSize));
|
|
128
|
+
if (query.sort) params.set("sort", JSON.stringify(query.sort));
|
|
129
|
+
if (query.filter) params.set("filter", JSON.stringify(query.filter));
|
|
130
|
+
response = await fetch(`${url}?${params.toString()}`, {
|
|
131
|
+
method: "GET",
|
|
132
|
+
headers: { "Accept": "application/json", ...headers }
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
response = await fetch(url, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
"Content-Type": "application/json",
|
|
139
|
+
"Accept": "application/json",
|
|
140
|
+
...headers
|
|
141
|
+
},
|
|
142
|
+
body: JSON.stringify(finalQuery)
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (!response.ok) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`Trellis datasource request failed: ${response.status}`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const raw = await response.json();
|
|
151
|
+
return transformResponse ? transformResponse(raw) : raw;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
156
|
+
0 && (module.exports = {
|
|
157
|
+
buildQueryFromState,
|
|
158
|
+
createRemoteDatasource,
|
|
159
|
+
createStaticDatasource
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/static-datasource.ts","../src/remote-datasource.ts"],"sourcesContent":["export type {\n TrellisQuery,\n TrellisSortQuery,\n TrellisFilterQuery,\n TrellisResponse,\n TrellisDatasource,\n RemoteDatasourceOptions,\n} from './types';\nexport { buildQueryFromState } from './types';\nexport { createStaticDatasource } from './static-datasource';\nexport { createRemoteDatasource } from './remote-datasource';\n","import type { TrellisAPI } from '@trellisjs/core';\n\n/**\n * Trellis 標準查詢協定 — 前端向後端發送的查詢格式。\n * 簡潔、可移植、不依賴任何後端框架。\n */\nexport interface TrellisQuery {\n /** 目前頁碼(從 1 開始) */\n page: number;\n /** 每頁筆數 */\n pageSize: number;\n /** 排序條件 */\n sort?: TrellisSortQuery[];\n /** 篩選條件 */\n filter?: TrellisFilterQuery;\n}\n\n/** 單一排序條件 */\nexport interface TrellisSortQuery {\n columnId: string;\n direction: 'asc' | 'desc';\n}\n\n/** 篩選條件 */\nexport interface TrellisFilterQuery {\n /** 全域搜尋關鍵字 */\n global?: string;\n /** 單欄篩選值 */\n columns?: Record<string, unknown>;\n}\n\n/**\n * 後端標準回應格式。\n * 所有 server-side 模式的後端都必須回傳這個結構。\n */\nexport interface TrellisResponse<T = Record<string, unknown>> {\n /** 當前頁的資料列 */\n data: T[];\n /** 總筆數(未篩選) */\n total: number;\n /** 篩選後筆數 */\n filtered: number;\n}\n\n/**\n * 資料源介面 — 所有資料源都要實作這個介面。\n */\nexport interface TrellisDatasource<T = Record<string, unknown>> {\n /** 根據查詢條件取得資料 */\n fetch(query: TrellisQuery): Promise<TrellisResponse<T>>;\n}\n\n/**\n * 遠端資料源設定\n */\nexport interface RemoteDatasourceOptions {\n /** API 端點 URL */\n url: string;\n /** HTTP 方法(預設 POST) */\n method?: 'GET' | 'POST';\n /** 自訂請求標頭 */\n headers?: Record<string, string>;\n /** 自訂查詢轉換(發送前處理) */\n transformQuery?: (query: TrellisQuery) => unknown;\n /** 自訂回應轉換(接收後處理) */\n transformResponse?: (response: unknown) => TrellisResponse;\n}\n\n/**\n * 從 Trellis 狀態建構查詢物件\n */\nexport function buildQueryFromState(api: TrellisAPI): TrellisQuery {\n const state = api.getState();\n const query: TrellisQuery = {\n page: state.pagination.page,\n pageSize: state.pagination.pageSize,\n };\n\n if (state.sorting.direction) {\n query.sort = [{\n columnId: state.sorting.columnId,\n direction: state.sorting.direction,\n }];\n }\n\n if (state.filtering.query || Object.keys(state.filtering.columnFilters).length > 0) {\n query.filter = {};\n if (state.filtering.query) {\n query.filter.global = state.filtering.query;\n }\n if (Object.keys(state.filtering.columnFilters).length > 0) {\n query.filter.columns = { ...state.filtering.columnFilters };\n }\n }\n\n return query;\n}\n","import type { TrellisDatasource, TrellisQuery, TrellisResponse } from './types';\n\ninterface StaticDatasourceOptions<T> {\n /** 自訂欄位值提取函式 */\n columnAccessors?: Record<string, (row: T) => unknown>;\n}\n\n/**\n * 建立靜態(本地)資料源。\n * 在本地執行排序、篩選、分頁 — 不需要後端。\n */\nexport function createStaticDatasource<T extends Record<string, unknown>>(\n data: T[],\n options?: StaticDatasourceOptions<T>,\n): TrellisDatasource<T> {\n const columnAccessors = options?.columnAccessors ?? {};\n\n function getValue(row: T, columnId: string): unknown {\n if (columnAccessors[columnId]) {\n return columnAccessors[columnId](row);\n }\n return row[columnId];\n }\n\n function compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n return String(a).localeCompare(String(b));\n }\n\n return {\n async fetch(query: TrellisQuery): Promise<TrellisResponse<T>> {\n let result = [...data];\n const total = data.length;\n\n // 篩選 — 全域搜尋\n if (query.filter?.global) {\n const keyword = query.filter.global.toLowerCase();\n result = result.filter((row) =>\n Object.values(row).some((val) =>\n val != null && String(val).toLowerCase().includes(keyword),\n ),\n );\n }\n\n // 篩選 — 單欄篩選\n if (query.filter?.columns) {\n for (const [colId, filterVal] of Object.entries(query.filter.columns)) {\n if (filterVal == null || filterVal === '') continue;\n const keyword = String(filterVal).toLowerCase();\n result = result.filter((row) => {\n const val = getValue(row, colId);\n return val != null && String(val).toLowerCase().includes(keyword);\n });\n }\n }\n\n const filtered = result.length;\n\n // 排序\n if (query.sort) {\n for (const sort of query.sort) {\n result.sort((a, b) => {\n const valA = getValue(a, sort.columnId);\n const valB = getValue(b, sort.columnId);\n const cmp = compareValues(valA, valB);\n return sort.direction === 'desc' ? -cmp : cmp;\n });\n }\n }\n\n // 分頁\n const start = (query.page - 1) * query.pageSize;\n const end = start + query.pageSize;\n const paged = result.slice(start, end);\n\n return { data: paged, total, filtered };\n },\n };\n}\n","import type {\n TrellisDatasource,\n TrellisQuery,\n TrellisResponse,\n RemoteDatasourceOptions,\n} from './types';\n\n/**\n * 建立遠端 API 資料源。\n * 透過 fetch 向後端發送標準化查詢,並解析回應。\n */\nexport function createRemoteDatasource<T = Record<string, unknown>>(\n options: RemoteDatasourceOptions,\n): TrellisDatasource<T> {\n const {\n url,\n method = 'POST',\n headers = {},\n transformQuery,\n transformResponse,\n } = options;\n\n return {\n async fetch(query: TrellisQuery): Promise<TrellisResponse<T>> {\n const finalQuery = transformQuery ? transformQuery(query) : query;\n\n let response: Response;\n\n if (method === 'GET') {\n const params = new URLSearchParams();\n params.set('page', String(query.page));\n params.set('pageSize', String(query.pageSize));\n if (query.sort) params.set('sort', JSON.stringify(query.sort));\n if (query.filter) params.set('filter', JSON.stringify(query.filter));\n\n response = await fetch(`${url}?${params.toString()}`, {\n method: 'GET',\n headers: { 'Accept': 'application/json', ...headers },\n });\n } else {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n ...headers,\n },\n body: JSON.stringify(finalQuery),\n });\n }\n\n if (!response.ok) {\n throw new Error(\n `Trellis datasource request failed: ${response.status}`,\n );\n }\n\n const raw = await response.json();\n return transformResponse\n ? (transformResponse(raw) as TrellisResponse<T>)\n : (raw as TrellisResponse<T>);\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
function buildQueryFromState(api) {
|
|
3
|
+
const state = api.getState();
|
|
4
|
+
const query = {
|
|
5
|
+
page: state.pagination.page,
|
|
6
|
+
pageSize: state.pagination.pageSize
|
|
7
|
+
};
|
|
8
|
+
if (state.sorting.direction) {
|
|
9
|
+
query.sort = [{
|
|
10
|
+
columnId: state.sorting.columnId,
|
|
11
|
+
direction: state.sorting.direction
|
|
12
|
+
}];
|
|
13
|
+
}
|
|
14
|
+
if (state.filtering.query || Object.keys(state.filtering.columnFilters).length > 0) {
|
|
15
|
+
query.filter = {};
|
|
16
|
+
if (state.filtering.query) {
|
|
17
|
+
query.filter.global = state.filtering.query;
|
|
18
|
+
}
|
|
19
|
+
if (Object.keys(state.filtering.columnFilters).length > 0) {
|
|
20
|
+
query.filter.columns = { ...state.filtering.columnFilters };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return query;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/static-datasource.ts
|
|
27
|
+
function createStaticDatasource(data, options) {
|
|
28
|
+
const columnAccessors = options?.columnAccessors ?? {};
|
|
29
|
+
function getValue(row, columnId) {
|
|
30
|
+
if (columnAccessors[columnId]) {
|
|
31
|
+
return columnAccessors[columnId](row);
|
|
32
|
+
}
|
|
33
|
+
return row[columnId];
|
|
34
|
+
}
|
|
35
|
+
function compareValues(a, b) {
|
|
36
|
+
if (a == null && b == null) return 0;
|
|
37
|
+
if (a == null) return -1;
|
|
38
|
+
if (b == null) return 1;
|
|
39
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
40
|
+
return String(a).localeCompare(String(b));
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
async fetch(query) {
|
|
44
|
+
let result = [...data];
|
|
45
|
+
const total = data.length;
|
|
46
|
+
if (query.filter?.global) {
|
|
47
|
+
const keyword = query.filter.global.toLowerCase();
|
|
48
|
+
result = result.filter(
|
|
49
|
+
(row) => Object.values(row).some(
|
|
50
|
+
(val) => val != null && String(val).toLowerCase().includes(keyword)
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
if (query.filter?.columns) {
|
|
55
|
+
for (const [colId, filterVal] of Object.entries(query.filter.columns)) {
|
|
56
|
+
if (filterVal == null || filterVal === "") continue;
|
|
57
|
+
const keyword = String(filterVal).toLowerCase();
|
|
58
|
+
result = result.filter((row) => {
|
|
59
|
+
const val = getValue(row, colId);
|
|
60
|
+
return val != null && String(val).toLowerCase().includes(keyword);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const filtered = result.length;
|
|
65
|
+
if (query.sort) {
|
|
66
|
+
for (const sort of query.sort) {
|
|
67
|
+
result.sort((a, b) => {
|
|
68
|
+
const valA = getValue(a, sort.columnId);
|
|
69
|
+
const valB = getValue(b, sort.columnId);
|
|
70
|
+
const cmp = compareValues(valA, valB);
|
|
71
|
+
return sort.direction === "desc" ? -cmp : cmp;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const start = (query.page - 1) * query.pageSize;
|
|
76
|
+
const end = start + query.pageSize;
|
|
77
|
+
const paged = result.slice(start, end);
|
|
78
|
+
return { data: paged, total, filtered };
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/remote-datasource.ts
|
|
84
|
+
function createRemoteDatasource(options) {
|
|
85
|
+
const {
|
|
86
|
+
url,
|
|
87
|
+
method = "POST",
|
|
88
|
+
headers = {},
|
|
89
|
+
transformQuery,
|
|
90
|
+
transformResponse
|
|
91
|
+
} = options;
|
|
92
|
+
return {
|
|
93
|
+
async fetch(query) {
|
|
94
|
+
const finalQuery = transformQuery ? transformQuery(query) : query;
|
|
95
|
+
let response;
|
|
96
|
+
if (method === "GET") {
|
|
97
|
+
const params = new URLSearchParams();
|
|
98
|
+
params.set("page", String(query.page));
|
|
99
|
+
params.set("pageSize", String(query.pageSize));
|
|
100
|
+
if (query.sort) params.set("sort", JSON.stringify(query.sort));
|
|
101
|
+
if (query.filter) params.set("filter", JSON.stringify(query.filter));
|
|
102
|
+
response = await fetch(`${url}?${params.toString()}`, {
|
|
103
|
+
method: "GET",
|
|
104
|
+
headers: { "Accept": "application/json", ...headers }
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
response = await fetch(url, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
"Accept": "application/json",
|
|
112
|
+
...headers
|
|
113
|
+
},
|
|
114
|
+
body: JSON.stringify(finalQuery)
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Trellis datasource request failed: ${response.status}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const raw = await response.json();
|
|
123
|
+
return transformResponse ? transformResponse(raw) : raw;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
buildQueryFromState,
|
|
129
|
+
createRemoteDatasource,
|
|
130
|
+
createStaticDatasource
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/static-datasource.ts","../src/remote-datasource.ts"],"sourcesContent":["import type { TrellisAPI } from '@trellisjs/core';\n\n/**\n * Trellis 標準查詢協定 — 前端向後端發送的查詢格式。\n * 簡潔、可移植、不依賴任何後端框架。\n */\nexport interface TrellisQuery {\n /** 目前頁碼(從 1 開始) */\n page: number;\n /** 每頁筆數 */\n pageSize: number;\n /** 排序條件 */\n sort?: TrellisSortQuery[];\n /** 篩選條件 */\n filter?: TrellisFilterQuery;\n}\n\n/** 單一排序條件 */\nexport interface TrellisSortQuery {\n columnId: string;\n direction: 'asc' | 'desc';\n}\n\n/** 篩選條件 */\nexport interface TrellisFilterQuery {\n /** 全域搜尋關鍵字 */\n global?: string;\n /** 單欄篩選值 */\n columns?: Record<string, unknown>;\n}\n\n/**\n * 後端標準回應格式。\n * 所有 server-side 模式的後端都必須回傳這個結構。\n */\nexport interface TrellisResponse<T = Record<string, unknown>> {\n /** 當前頁的資料列 */\n data: T[];\n /** 總筆數(未篩選) */\n total: number;\n /** 篩選後筆數 */\n filtered: number;\n}\n\n/**\n * 資料源介面 — 所有資料源都要實作這個介面。\n */\nexport interface TrellisDatasource<T = Record<string, unknown>> {\n /** 根據查詢條件取得資料 */\n fetch(query: TrellisQuery): Promise<TrellisResponse<T>>;\n}\n\n/**\n * 遠端資料源設定\n */\nexport interface RemoteDatasourceOptions {\n /** API 端點 URL */\n url: string;\n /** HTTP 方法(預設 POST) */\n method?: 'GET' | 'POST';\n /** 自訂請求標頭 */\n headers?: Record<string, string>;\n /** 自訂查詢轉換(發送前處理) */\n transformQuery?: (query: TrellisQuery) => unknown;\n /** 自訂回應轉換(接收後處理) */\n transformResponse?: (response: unknown) => TrellisResponse;\n}\n\n/**\n * 從 Trellis 狀態建構查詢物件\n */\nexport function buildQueryFromState(api: TrellisAPI): TrellisQuery {\n const state = api.getState();\n const query: TrellisQuery = {\n page: state.pagination.page,\n pageSize: state.pagination.pageSize,\n };\n\n if (state.sorting.direction) {\n query.sort = [{\n columnId: state.sorting.columnId,\n direction: state.sorting.direction,\n }];\n }\n\n if (state.filtering.query || Object.keys(state.filtering.columnFilters).length > 0) {\n query.filter = {};\n if (state.filtering.query) {\n query.filter.global = state.filtering.query;\n }\n if (Object.keys(state.filtering.columnFilters).length > 0) {\n query.filter.columns = { ...state.filtering.columnFilters };\n }\n }\n\n return query;\n}\n","import type { TrellisDatasource, TrellisQuery, TrellisResponse } from './types';\n\ninterface StaticDatasourceOptions<T> {\n /** 自訂欄位值提取函式 */\n columnAccessors?: Record<string, (row: T) => unknown>;\n}\n\n/**\n * 建立靜態(本地)資料源。\n * 在本地執行排序、篩選、分頁 — 不需要後端。\n */\nexport function createStaticDatasource<T extends Record<string, unknown>>(\n data: T[],\n options?: StaticDatasourceOptions<T>,\n): TrellisDatasource<T> {\n const columnAccessors = options?.columnAccessors ?? {};\n\n function getValue(row: T, columnId: string): unknown {\n if (columnAccessors[columnId]) {\n return columnAccessors[columnId](row);\n }\n return row[columnId];\n }\n\n function compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n return String(a).localeCompare(String(b));\n }\n\n return {\n async fetch(query: TrellisQuery): Promise<TrellisResponse<T>> {\n let result = [...data];\n const total = data.length;\n\n // 篩選 — 全域搜尋\n if (query.filter?.global) {\n const keyword = query.filter.global.toLowerCase();\n result = result.filter((row) =>\n Object.values(row).some((val) =>\n val != null && String(val).toLowerCase().includes(keyword),\n ),\n );\n }\n\n // 篩選 — 單欄篩選\n if (query.filter?.columns) {\n for (const [colId, filterVal] of Object.entries(query.filter.columns)) {\n if (filterVal == null || filterVal === '') continue;\n const keyword = String(filterVal).toLowerCase();\n result = result.filter((row) => {\n const val = getValue(row, colId);\n return val != null && String(val).toLowerCase().includes(keyword);\n });\n }\n }\n\n const filtered = result.length;\n\n // 排序\n if (query.sort) {\n for (const sort of query.sort) {\n result.sort((a, b) => {\n const valA = getValue(a, sort.columnId);\n const valB = getValue(b, sort.columnId);\n const cmp = compareValues(valA, valB);\n return sort.direction === 'desc' ? -cmp : cmp;\n });\n }\n }\n\n // 分頁\n const start = (query.page - 1) * query.pageSize;\n const end = start + query.pageSize;\n const paged = result.slice(start, end);\n\n return { data: paged, total, filtered };\n },\n };\n}\n","import type {\n TrellisDatasource,\n TrellisQuery,\n TrellisResponse,\n RemoteDatasourceOptions,\n} from './types';\n\n/**\n * 建立遠端 API 資料源。\n * 透過 fetch 向後端發送標準化查詢,並解析回應。\n */\nexport function createRemoteDatasource<T = Record<string, unknown>>(\n options: RemoteDatasourceOptions,\n): TrellisDatasource<T> {\n const {\n url,\n method = 'POST',\n headers = {},\n transformQuery,\n transformResponse,\n } = options;\n\n return {\n async fetch(query: TrellisQuery): Promise<TrellisResponse<T>> {\n const finalQuery = transformQuery ? transformQuery(query) : query;\n\n let response: Response;\n\n if (method === 'GET') {\n const params = new URLSearchParams();\n params.set('page', String(query.page));\n params.set('pageSize', String(query.pageSize));\n if (query.sort) params.set('sort', JSON.stringify(query.sort));\n if (query.filter) params.set('filter', JSON.stringify(query.filter));\n\n response = await fetch(`${url}?${params.toString()}`, {\n method: 'GET',\n headers: { 'Accept': 'application/json', ...headers },\n });\n } else {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n ...headers,\n },\n body: JSON.stringify(finalQuery),\n });\n }\n\n if (!response.ok) {\n throw new Error(\n `Trellis datasource request failed: ${response.status}`,\n );\n }\n\n const raw = await response.json();\n return transformResponse\n ? (transformResponse(raw) as TrellisResponse<T>)\n : (raw as TrellisResponse<T>);\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trellisjs/plugin-datasource",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "資料源插件 — 支援靜態資料與遠端 API",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.mts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:watch": "vitest",
|
|
28
|
+
"clean": "rm -rf dist"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@trellisjs/core": "workspace:*"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@trellisjs/core": "workspace:*",
|
|
35
|
+
"tsup": "^8.4.0",
|
|
36
|
+
"typescript": "^5.7.0",
|
|
37
|
+
"vitest": "^3.1.0"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"sideEffects": false
|
|
41
|
+
}
|