@nocobase/plugin-flow-engine 2.1.0-alpha.9 → 2.1.0-beta.10
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/ai/docs/runjs/context/block-model.md +35 -35
- package/dist/ai/docs/runjs/context/collection-field.md +51 -53
- package/dist/ai/docs/runjs/context/collection.md +39 -39
- package/dist/ai/docs/runjs/context/data-source-manager.md +30 -40
- package/dist/ai/docs/runjs/context/data-source.md +44 -52
- package/dist/ai/docs/runjs/context/element.md +38 -44
- package/dist/ai/docs/runjs/context/exit-all.md +35 -37
- package/dist/ai/docs/runjs/context/exit.md +35 -38
- package/dist/ai/docs/runjs/context/filter-manager.md +30 -36
- package/dist/ai/docs/runjs/context/form.md +57 -57
- package/dist/ai/docs/runjs/context/get-model.md +21 -22
- package/dist/ai/docs/runjs/context/get-value.md +19 -20
- package/dist/ai/docs/runjs/context/get-var.md +55 -61
- package/dist/ai/docs/runjs/context/i18n.md +14 -17
- package/dist/ai/docs/runjs/context/import-async.md +45 -333
- package/dist/ai/docs/runjs/context/init-resource.md +20 -20
- package/dist/ai/docs/runjs/context/libs.md +31 -31
- package/dist/ai/docs/runjs/context/location.md +31 -34
- package/dist/ai/docs/runjs/context/logger.md +40 -41
- package/dist/ai/docs/runjs/context/make-resource.md +26 -27
- package/dist/ai/docs/runjs/context/message.md +41 -42
- package/dist/ai/docs/runjs/context/modal.md +44 -44
- package/dist/ai/docs/runjs/context/model.md +33 -36
- package/dist/ai/docs/runjs/context/notification.md +40 -41
- package/dist/ai/docs/runjs/context/off.md +14 -14
- package/dist/ai/docs/runjs/context/on.md +29 -30
- package/dist/ai/docs/runjs/context/open-view.md +40 -40
- package/dist/ai/docs/runjs/context/render.md +32 -37
- package/dist/ai/docs/runjs/context/request.md +45 -46
- package/dist/ai/docs/runjs/context/require-async.md +25 -28
- package/dist/ai/docs/runjs/context/resource.md +34 -34
- package/dist/ai/docs/runjs/context/route.md +34 -36
- package/dist/ai/docs/runjs/context/router.md +31 -43
- package/dist/ai/docs/runjs/context/set-value.md +17 -18
- package/dist/ai/docs/runjs/context/t.md +17 -20
- package/dist/ai/docs/runjs/context/view.md +46 -49
- package/dist/ai/docs/runjs/document.md +0 -1
- package/dist/ai/docs/runjs/import-modules.md +32 -32
- package/dist/ai/docs/runjs/index.md +13 -13
- package/dist/ai/docs/runjs/jsx.md +19 -19
- package/dist/ai/docs/runjs/model/form-block-model.md +3 -1
- package/dist/ai/docs/runjs/render.md +15 -15
- package/dist/ai/docs/runjs/resource/api-resource.md +53 -53
- package/dist/ai/docs/runjs/resource/multi-record-resource.md +64 -64
- package/dist/ai/docs/runjs/resource/single-record-resource.md +55 -55
- package/dist/ai/docs/runjs/resource/sql-resource.md +57 -57
- package/dist/ai/docs/runjs/window.md +5 -5
- package/dist/externalVersion.js +10 -10
- package/dist/node_modules/ses/package.json +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# ctx.importAsync()
|
|
2
2
|
|
|
3
|
-
Dynamically
|
|
3
|
+
Dynamically loads **ESM modules** or **CSS** by URL; use in all RunJS scenarios. For third-party ESM use `ctx.importAsync()`; for UMD/AMD use `ctx.requireAsync()`. Pass a `.css` URL to load and inject styles.
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
7
|
| Scenario | Description |
|
|
8
|
-
|
|
9
|
-
| **JSBlock** |
|
|
10
|
-
| **JSField / JSItem / JSColumn** | Load
|
|
11
|
-
| **
|
|
8
|
+
|----------|-------------|
|
|
9
|
+
| **JSBlock** | Load Vue, ECharts, Tabulator, etc. for custom charts, tables, boards |
|
|
10
|
+
| **JSField / JSItem / JSColumn** | Load small ESM utils (e.g. dayjs plugins) for rendering |
|
|
11
|
+
| **Event flow / action events** | Load dependencies then run logic |
|
|
12
12
|
|
|
13
|
-
## Type
|
|
13
|
+
## Type
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
importAsync<T = any>(url: string): Promise<T>;
|
|
@@ -19,134 +19,90 @@ importAsync<T = any>(url: string): Promise<T>;
|
|
|
19
19
|
## Parameters
|
|
20
20
|
|
|
21
21
|
| Parameter | Type | Description |
|
|
22
|
-
|
|
23
|
-
| `url` | `string` |
|
|
22
|
+
|-----------|------|-------------|
|
|
23
|
+
| `url` | `string` | ESM or CSS URL. Supports shorthand `<package>@<version>` or subpath `<package>@<version>/<path>` (e.g. `vue@3.4.0`, `dayjs@1/plugin/relativeTime.js`), resolved with configured CDN prefix; full URLs also supported. For `.css` URLs, loads and injects styles. For React-dependent libs add `?deps=react@18.2.0,react-dom@18.2.0` to share the same React instance. |
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Returns
|
|
26
26
|
|
|
27
|
-
-
|
|
27
|
+
- Resolved module namespace object (Promise value).
|
|
28
28
|
|
|
29
|
-
## URL
|
|
29
|
+
## URL format
|
|
30
30
|
|
|
31
|
-
- **ESM and CSS**:
|
|
32
|
-
- **Shorthand
|
|
33
|
-
- **?deps**:
|
|
34
|
-
- **Self-hosted CDN**:
|
|
35
|
-
- **ESM_CDN_BASE_URL**:
|
|
36
|
-
- **ESM_CDN_SUFFIX**: Optional suffix (e.g
|
|
37
|
-
-
|
|
31
|
+
- **ESM and CSS**: Besides ESM, you can load CSS (pass a `.css` URL; it is injected into the page).
|
|
32
|
+
- **Shorthand**: When not configured, **https://esm.sh** is used as CDN prefix. E.g. `vue@3.4.0` → `https://esm.sh/vue@3.4.0`.
|
|
33
|
+
- **?deps**: For React-dependent libs (e.g. `@dnd-kit/core`, `react-big-calendar`) add `?deps=react@18.2.0,react-dom@18.2.0` to avoid Invalid hook call from multiple React instances.
|
|
34
|
+
- **Self-hosted CDN**: Set env vars for intranet or custom service:
|
|
35
|
+
- **ESM_CDN_BASE_URL**: ESM CDN base (default `https://esm.sh`)
|
|
36
|
+
- **ESM_CDN_SUFFIX**: Optional suffix (e.g. jsDelivr `/+esm`)
|
|
37
|
+
- Reference: [nocobase/esm-server](https://github.com/nocobase/esm-server)
|
|
38
38
|
|
|
39
|
-
##
|
|
39
|
+
## vs ctx.requireAsync()
|
|
40
40
|
|
|
41
|
-
- **ctx.importAsync()**: Loads **ESM
|
|
42
|
-
- **ctx.requireAsync()**: Loads **UMD/AMD**
|
|
41
|
+
- **ctx.importAsync()**: Loads **ESM**; returns module namespace; good for Vue, dayjs, etc. (ESM builds).
|
|
42
|
+
- **ctx.requireAsync()**: Loads **UMD/AMD** or global scripts; good for ECharts, FullCalendar (UMD), etc. If a lib has ESM, prefer `ctx.importAsync()`.
|
|
43
43
|
|
|
44
44
|
## Examples
|
|
45
45
|
|
|
46
|
-
### Basic
|
|
47
|
-
|
|
48
|
-
Demonstrates the most basic usage of dynamically loading ESM modules and CSS by package name or full URL.
|
|
46
|
+
### Basic
|
|
49
47
|
|
|
50
48
|
```javascript
|
|
51
49
|
const Vue = await ctx.importAsync('vue@3.4.0');
|
|
52
|
-
// Equivalent to loading from https://esm.sh/vue@3.4.0
|
|
53
50
|
|
|
54
51
|
const relativeTime = await ctx.importAsync('dayjs@1/plugin/relativeTime.js');
|
|
55
|
-
// With subpath (e.g., dayjs plugin)
|
|
56
52
|
|
|
57
53
|
const pkg = await ctx.importAsync('https://cdn.example.com/my-module.js');
|
|
58
|
-
// Full URL
|
|
59
54
|
|
|
60
55
|
await ctx.importAsync('https://cdn.example.com/theme.css');
|
|
61
|
-
// Load CSS and inject into the page
|
|
62
56
|
```
|
|
63
57
|
|
|
64
|
-
### ECharts
|
|
65
|
-
|
|
66
|
-
Use ECharts to draw a sales overview chart with bar and line graphs.
|
|
58
|
+
### ECharts
|
|
67
59
|
|
|
68
60
|
```ts
|
|
69
|
-
// 1. Dynamically load ECharts module
|
|
70
61
|
const echarts = await ctx.importAsync('echarts@5.4.3');
|
|
71
62
|
|
|
72
|
-
// 2. Create chart container and render
|
|
73
63
|
const chartEl = document.createElement('div');
|
|
74
64
|
chartEl.style.width = '100%';
|
|
75
65
|
chartEl.style.height = '400px';
|
|
76
66
|
ctx.render(chartEl);
|
|
77
67
|
|
|
78
|
-
// 3. Initialize ECharts instance
|
|
79
68
|
const chart = echarts.init(chartEl);
|
|
80
69
|
|
|
81
|
-
// 4. Configure chart
|
|
82
70
|
const option = {
|
|
83
|
-
title: {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
trigger: 'axis',
|
|
89
|
-
},
|
|
90
|
-
legend: {
|
|
91
|
-
data: ['Sales', 'Profit'],
|
|
92
|
-
top: '10%',
|
|
93
|
-
},
|
|
94
|
-
xAxis: {
|
|
95
|
-
type: 'category',
|
|
96
|
-
data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
|
|
97
|
-
},
|
|
98
|
-
yAxis: {
|
|
99
|
-
type: 'value',
|
|
100
|
-
},
|
|
71
|
+
title: { text: 'Sales Overview', left: 'center' },
|
|
72
|
+
tooltip: { trigger: 'axis' },
|
|
73
|
+
legend: { data: ['Sales', 'Profit'], top: '10%' },
|
|
74
|
+
xAxis: { type: 'category', data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'] },
|
|
75
|
+
yAxis: { type: 'value' },
|
|
101
76
|
series: [
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
type: 'bar',
|
|
105
|
-
data: [120, 200, 150, 80, 70, 110],
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: 'Profit',
|
|
109
|
-
type: 'line',
|
|
110
|
-
data: [20, 40, 30, 15, 12, 25],
|
|
111
|
-
},
|
|
77
|
+
{ name: 'Sales', type: 'bar', data: [120, 200, 150, 80, 70, 110] },
|
|
78
|
+
{ name: 'Profit', type: 'line', data: [20, 40, 30, 15, 12, 25] },
|
|
112
79
|
],
|
|
113
80
|
};
|
|
114
81
|
|
|
115
|
-
// 5. Set option and render chart
|
|
116
82
|
chart.setOption(option);
|
|
117
83
|
|
|
118
|
-
|
|
119
|
-
window.addEventListener('resize', () => {
|
|
120
|
-
chart.resize();
|
|
121
|
-
});
|
|
84
|
+
window.addEventListener('resize', () => chart.resize());
|
|
122
85
|
|
|
123
|
-
// 7. Optional: Event listener
|
|
124
86
|
chart.on('click', (params) => {
|
|
125
87
|
ctx.message.info(`Clicked ${params.seriesName} on ${params.name}, value: ${params.value}`);
|
|
126
88
|
});
|
|
127
89
|
```
|
|
128
90
|
|
|
129
|
-
### Tabulator
|
|
130
|
-
|
|
131
|
-
Demonstrates rendering a data table with pagination and row click events in a block using Tabulator.
|
|
91
|
+
### Tabulator
|
|
132
92
|
|
|
133
93
|
```ts
|
|
134
|
-
// 1. Load Tabulator styles
|
|
135
94
|
await ctx.importAsync('tabulator-tables@6.2.5/dist/css/tabulator.min.css');
|
|
136
95
|
|
|
137
|
-
// 2. Dynamically load Tabulator module
|
|
138
96
|
const { TabulatorFull } = await ctx.importAsync('tabulator-tables@6.2.5');
|
|
139
97
|
|
|
140
|
-
// 3. Create table container and render
|
|
141
98
|
const tableEl = document.createElement('div');
|
|
142
99
|
ctx.render(tableEl);
|
|
143
100
|
|
|
144
|
-
// 4. Initialize Tabulator table
|
|
145
101
|
const table = new TabulatorFull(tableEl, {
|
|
146
102
|
data: [
|
|
147
|
-
{ id: 1, name: 'Alice', age: 25, city: '
|
|
148
|
-
{ id: 2, name: 'Bob', age: 30, city: '
|
|
149
|
-
{ id: 3, name: 'Charlie', age: 28, city: '
|
|
103
|
+
{ id: 1, name: 'Alice', age: 25, city: 'Beijing' },
|
|
104
|
+
{ id: 2, name: 'Bob', age: 30, city: 'Shanghai' },
|
|
105
|
+
{ id: 3, name: 'Charlie', age: 28, city: 'Guangzhou' },
|
|
150
106
|
],
|
|
151
107
|
columns: [
|
|
152
108
|
{ title: 'ID', field: 'id', width: 80 },
|
|
@@ -159,30 +115,22 @@ const table = new TabulatorFull(tableEl, {
|
|
|
159
115
|
paginationSize: 10,
|
|
160
116
|
});
|
|
161
117
|
|
|
162
|
-
// 5. Optional: Event listener
|
|
163
118
|
table.on('rowClick', (e, row) => {
|
|
164
119
|
const rowData = row.getData();
|
|
165
120
|
ctx.message.info(`Row clicked: ${rowData.name}`);
|
|
166
121
|
});
|
|
167
122
|
```
|
|
168
123
|
|
|
169
|
-
### FullCalendar (ESM)
|
|
170
|
-
|
|
171
|
-
Shows how to load FullCalendar and its plugins via ESM and render a basic monthly view calendar.
|
|
124
|
+
### FullCalendar (ESM)
|
|
172
125
|
|
|
173
126
|
```ts
|
|
174
|
-
// 1. Dynamically load FullCalendar core module
|
|
175
127
|
const { Calendar } = await ctx.importAsync('@fullcalendar/core@6.1.20');
|
|
176
|
-
|
|
177
|
-
// 2. Dynamically load dayGrid plugin
|
|
178
128
|
const dayGridPlugin = await ctx.importAsync('@fullcalendar/daygrid@6.1.20');
|
|
179
129
|
|
|
180
|
-
// 3. Create calendar container and render
|
|
181
130
|
const calendarEl = document.createElement('div');
|
|
182
131
|
calendarEl.id = 'calendar';
|
|
183
132
|
ctx.render(calendarEl);
|
|
184
133
|
|
|
185
|
-
// 4. Initialize and render calendar
|
|
186
134
|
const calendar = new Calendar(calendarEl, {
|
|
187
135
|
plugins: [dayGridPlugin.default || dayGridPlugin],
|
|
188
136
|
headerToolbar: {
|
|
@@ -195,160 +143,22 @@ const calendar = new Calendar(calendarEl, {
|
|
|
195
143
|
calendar.render();
|
|
196
144
|
```
|
|
197
145
|
|
|
198
|
-
### dnd-kit
|
|
146
|
+
### dnd-kit (with ?deps)
|
|
199
147
|
|
|
200
|
-
|
|
148
|
+
For React-based libs like `@dnd-kit/core`, use `?deps=react@18.2.0,react-dom@18.2.0` so the same React instance is used and hooks work:
|
|
201
149
|
|
|
202
150
|
```ts
|
|
203
|
-
// 1. Load React, react-dom, @dnd-kit/core (?deps ensures same React instance to avoid Invalid hook call)
|
|
204
151
|
const React = await ctx.importAsync('react@18.2.0');
|
|
205
152
|
const { createRoot } = await ctx.importAsync('react-dom@18.2.0/client');
|
|
206
153
|
const core = await ctx.importAsync('@dnd-kit/core@6.3.1?deps=react@18.2.0,react-dom@18.2.0');
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
function DraggableBox() {
|
|
210
|
-
const { attributes, listeners, setNodeRef, transform } = useDraggable({ id: 'box' });
|
|
211
|
-
const style = {
|
|
212
|
-
padding: 12,
|
|
213
|
-
marginBottom: 8,
|
|
214
|
-
background: '#e6f7ff',
|
|
215
|
-
cursor: 'grab',
|
|
216
|
-
transform: transform ? 'translate3d(' + transform.x + 'px,' + transform.y + 'px,0)' : undefined,
|
|
217
|
-
};
|
|
218
|
-
return React.createElement('div', { ref: setNodeRef, style, ...attributes, ...listeners }, 'Drag me');
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function DropZone() {
|
|
222
|
-
const { setNodeRef, isOver } = useDroppable({ id: 'zone' });
|
|
223
|
-
return React.createElement(
|
|
224
|
-
'div',
|
|
225
|
-
{
|
|
226
|
-
ref: setNodeRef,
|
|
227
|
-
style: { padding: 24, background: isOver ? '#b7eb8f' : '#f5f5f5', borderRadius: 8, minHeight: 80 },
|
|
228
|
-
},
|
|
229
|
-
'Drop here',
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function App() {
|
|
234
|
-
const sensors = useSensors(useSensor(PointerSensor));
|
|
235
|
-
function onDragEnd(e) {
|
|
236
|
-
if (e.over && e.over.id === 'zone') ctx.message.success('Dropped in zone');
|
|
237
|
-
}
|
|
238
|
-
return React.createElement(
|
|
239
|
-
DndContext,
|
|
240
|
-
{ sensors, collisionDetection: closestCenter, onDragEnd },
|
|
241
|
-
React.createElement(
|
|
242
|
-
'div',
|
|
243
|
-
{ style: { maxWidth: 280 } },
|
|
244
|
-
React.createElement(DraggableBox),
|
|
245
|
-
React.createElement(DropZone),
|
|
246
|
-
),
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// 2. Render
|
|
251
|
-
ctx.render(<App />);
|
|
154
|
+
// Use core (DndContext, useDraggable, useDroppable, etc.) with React
|
|
252
155
|
```
|
|
253
156
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
### dnd-kit Sortable List Example
|
|
257
|
-
|
|
258
|
-
Implements a vertical sortable list using dnd-kit's core, sortable, and utilities.
|
|
259
|
-
|
|
260
|
-
```ts
|
|
261
|
-
// 1. Load React and dnd-kit related packages (?deps ensures same React instance)
|
|
262
|
-
const React = await ctx.importAsync('react@18.2.0');
|
|
263
|
-
const { createRoot } = await ctx.importAsync('react-dom@18.2.0/client');
|
|
264
|
-
const dndCore = await ctx.importAsync('@dnd-kit/core@6.3.1?deps=react@18.2.0,react-dom@18.2.0');
|
|
265
|
-
const dndSortable = await ctx.importAsync('@dnd-kit/sortable@10.0.0?deps=react@18.2.0,react-dom@18.2.0');
|
|
266
|
-
const dndUtils = await ctx.importAsync('@dnd-kit/utilities@3.2.2');
|
|
267
|
-
|
|
268
|
-
const { useState } = React;
|
|
269
|
-
const { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors } = dndCore;
|
|
270
|
-
const {
|
|
271
|
-
arrayMove,
|
|
272
|
-
SortableContext,
|
|
273
|
-
sortableKeyboardCoordinates,
|
|
274
|
-
verticalListSortingStrategy,
|
|
275
|
-
useSortable,
|
|
276
|
-
} = dndSortable;
|
|
277
|
-
const { CSS } = dndUtils;
|
|
278
|
-
|
|
279
|
-
// 2. SortableItem component (must be inside SortableContext)
|
|
280
|
-
function SortableItem(props) {
|
|
281
|
-
const { id, label } = props;
|
|
282
|
-
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });
|
|
283
|
-
const style = {
|
|
284
|
-
transform: CSS.Transform.toString(transform),
|
|
285
|
-
transition,
|
|
286
|
-
padding: '12px 16px',
|
|
287
|
-
marginBottom: 8,
|
|
288
|
-
background: '#f5f5f5',
|
|
289
|
-
borderRadius: 6,
|
|
290
|
-
cursor: 'grab',
|
|
291
|
-
};
|
|
292
|
-
return React.createElement('div', { ref: setNodeRef, style, ...attributes, ...listeners }, label);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// 3. App: DndContext + SortableContext + Drag end handler
|
|
296
|
-
const labels = { 1: 'First', 2: 'Second', 3: 'Third', 4: 'Fourth' };
|
|
297
|
-
function App() {
|
|
298
|
-
const [items, setItems] = useState([1, 2, 3, 4]);
|
|
299
|
-
const sensors = useSensors(
|
|
300
|
-
useSensor(PointerSensor),
|
|
301
|
-
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
function handleDragEnd(event) {
|
|
305
|
-
const { active, over } = event;
|
|
306
|
-
if (over && active.id !== over.id) {
|
|
307
|
-
setItems((prev) => {
|
|
308
|
-
const oldIndex = prev.indexOf(active.id);
|
|
309
|
-
const newIndex = prev.indexOf(over.id);
|
|
310
|
-
return arrayMove(prev, oldIndex, newIndex);
|
|
311
|
-
});
|
|
312
|
-
ctx.message.success('List reordered');
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return React.createElement(
|
|
317
|
-
DndContext,
|
|
318
|
-
{
|
|
319
|
-
sensors,
|
|
320
|
-
collisionDetection: closestCenter,
|
|
321
|
-
onDragEnd: handleDragEnd,
|
|
322
|
-
},
|
|
323
|
-
React.createElement(
|
|
324
|
-
SortableContext,
|
|
325
|
-
{ items, strategy: verticalListSortingStrategy },
|
|
326
|
-
React.createElement(
|
|
327
|
-
'div',
|
|
328
|
-
{ style: { maxWidth: 320 } },
|
|
329
|
-
items.map((id) => React.createElement(SortableItem, { key: id, id, label: labels[id] })),
|
|
330
|
-
),
|
|
331
|
-
),
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// 4. Create container and mount React
|
|
336
|
-
const rootEl = document.createElement('div');
|
|
337
|
-
ctx.render(rootEl);
|
|
338
|
-
createRoot(rootEl).render(React.createElement(App));
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
This example uses `@dnd-kit/core`, `@dnd-kit/sortable`, and `@dnd-kit/utilities` to implement a sortable list that updates its order and displays a "List reordered" message after dragging.
|
|
342
|
-
|
|
343
|
-
### react-big-calendar Example
|
|
344
|
-
|
|
345
|
-
Renders a calendar component supporting event display in the current block using `react-big-calendar` and `date-fns` for localization.
|
|
157
|
+
### react-big-calendar
|
|
346
158
|
|
|
347
159
|
```tsx
|
|
348
|
-
// 1. Load styles (ctx.importAsync uses ctx.loadCSS for .css files)
|
|
349
160
|
await ctx.importAsync('react-big-calendar@1.11.4/lib/css/react-big-calendar.css');
|
|
350
161
|
|
|
351
|
-
// 2. Load React, react-dom, react-big-calendar, date-fns, and locale (ensuring same React instance)
|
|
352
162
|
const React = await ctx.importAsync('react@18.2.0');
|
|
353
163
|
const { Calendar, dateFnsLocalizer } = await ctx.importAsync('react-big-calendar@1.11.4?deps=react@18.2.0,react-dom@18.2.0');
|
|
354
164
|
const { format, parse, startOfWeek, getDay } = await ctx.importAsync('date-fns@2.30.0');
|
|
@@ -367,7 +177,6 @@ const events = [
|
|
|
367
177
|
{ title: 'Meeting', start: new Date(2026, 0, 29, 10, 0), end: new Date(2026, 0, 29, 11, 0) },
|
|
368
178
|
];
|
|
369
179
|
|
|
370
|
-
// 3. Render React Calendar
|
|
371
180
|
ctx.render(
|
|
372
181
|
<Calendar
|
|
373
182
|
localizer={localizer}
|
|
@@ -379,110 +188,13 @@ ctx.render(
|
|
|
379
188
|
);
|
|
380
189
|
```
|
|
381
190
|
|
|
382
|
-
### frappe-gantt Example
|
|
383
|
-
|
|
384
|
-
Uses `frappe-gantt` to render a Gantt chart view showing task start/end times and progress.
|
|
385
|
-
|
|
386
|
-
```ts
|
|
387
|
-
// 1. Dynamically load Gantt styles and constructor
|
|
388
|
-
// Depends on ESM_CDN_BASE_URL (default https://esm.sh), shorthand paths can be used
|
|
389
|
-
await ctx.importAsync('frappe-gantt@1.0.4/dist/frappe-gantt.css');
|
|
390
|
-
const Gantt = await ctx.importAsync('frappe-gantt@1.0.4');
|
|
391
|
-
|
|
392
|
-
// 2. Prepare task data
|
|
393
|
-
let tasks = [
|
|
394
|
-
{
|
|
395
|
-
id: '1',
|
|
396
|
-
name: 'Redesign website',
|
|
397
|
-
start: '2016-12-28',
|
|
398
|
-
end: '2016-12-31',
|
|
399
|
-
progress: 20,
|
|
400
|
-
},
|
|
401
|
-
{
|
|
402
|
-
id: '2',
|
|
403
|
-
name: 'Develop new feature',
|
|
404
|
-
start: '2017-01-01',
|
|
405
|
-
end: '2017-01-05',
|
|
406
|
-
progress: 40,
|
|
407
|
-
dependencies: '1',
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
id: '3',
|
|
411
|
-
name: 'QA & testing',
|
|
412
|
-
start: '2017-01-06',
|
|
413
|
-
end: '2017-01-10',
|
|
414
|
-
progress: 10,
|
|
415
|
-
dependencies: '2',
|
|
416
|
-
},
|
|
417
|
-
];
|
|
418
|
-
|
|
419
|
-
// 3. Create container and render
|
|
420
|
-
const ganttEl = document.createElement('div');
|
|
421
|
-
ganttEl.id = 'gantt';
|
|
422
|
-
ganttEl.style.height = '400px';
|
|
423
|
-
ctx.render(ganttEl);
|
|
424
|
-
|
|
425
|
-
// 4. Initialize Gantt chart
|
|
426
|
-
let gantt = new Gantt('#gantt', tasks, {
|
|
427
|
-
view_mode: 'Day', // View granularity: 'Quarter Day' | 'Half Day' | 'Day' | 'Week' | 'Month'
|
|
428
|
-
language: 'en',
|
|
429
|
-
bar_height: 24,
|
|
430
|
-
padding: 18,
|
|
431
|
-
custom_popup_html(task) {
|
|
432
|
-
return `
|
|
433
|
-
<div class="details-container">
|
|
434
|
-
<h5>${task.name}</h5>
|
|
435
|
-
<p>Start: ${task._start.toISOString().slice(0, 10)}</p>
|
|
436
|
-
<p>End: ${task._end.toISOString().slice(0, 10)}</p>
|
|
437
|
-
<p>Progress: ${task.progress}%</p>
|
|
438
|
-
</div>
|
|
439
|
-
`;
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### @asseinfo/react-kanban Example
|
|
445
|
-
|
|
446
|
-
Utilizes `@asseinfo/react-kanban` to render a basic Kanban board with columns like Backlog and Doing within a block.
|
|
447
|
-
|
|
448
|
-
```ts
|
|
449
|
-
// 1. Load styles (ctx.importAsync directly loads .css)
|
|
450
|
-
await ctx.importAsync('@asseinfo/react-kanban@2.2.0/dist/styles.css');
|
|
451
|
-
|
|
452
|
-
// 2. Load React, react-dom, @asseinfo/react-kanban (?deps ensures same React instance)
|
|
453
|
-
const React = await ctx.importAsync('react@18.2.0');
|
|
454
|
-
const { default: Board } = await ctx.importAsync('@asseinfo/react-kanban@2.2.0?deps=react@18.2.0,react-dom@18.2.0');
|
|
455
|
-
|
|
456
|
-
const board = {
|
|
457
|
-
columns: [
|
|
458
|
-
{
|
|
459
|
-
id: 1,
|
|
460
|
-
title: 'Backlog',
|
|
461
|
-
cards: [
|
|
462
|
-
{ id: 1, title: 'Add card', description: 'Add capability to add a card in a column' },
|
|
463
|
-
],
|
|
464
|
-
},
|
|
465
|
-
{
|
|
466
|
-
id: 2,
|
|
467
|
-
title: 'Doing',
|
|
468
|
-
cards: [
|
|
469
|
-
{ id: 2, title: 'Drag-n-drop support', description: 'Move a card between the columns' },
|
|
470
|
-
],
|
|
471
|
-
},
|
|
472
|
-
],
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
// 4. Mount the board
|
|
476
|
-
ctx.render(<Board initialBoard={board} />);
|
|
477
|
-
```
|
|
478
|
-
|
|
479
191
|
## Notes
|
|
480
192
|
|
|
481
|
-
-
|
|
482
|
-
- When a
|
|
483
|
-
- For
|
|
193
|
+
- Depends on network/CDN; for intranet set **ESM_CDN_BASE_URL** to your own service.
|
|
194
|
+
- When a lib has both ESM and UMD, prefer `ctx.importAsync()` for better module semantics.
|
|
195
|
+
- For React-dependent libs always add `?deps=react@18.2.0,react-dom@18.2.0` (or the version your app uses) to avoid Invalid hook call.
|
|
484
196
|
|
|
485
197
|
## Related
|
|
486
198
|
|
|
487
|
-
- [ctx.requireAsync()](./require-async.md):
|
|
488
|
-
- [ctx.render()](./render.md):
|
|
199
|
+
- [ctx.requireAsync()](./require-async.md): load UMD/AMD or global scripts; good for ECharts, FullCalendar (UMD)
|
|
200
|
+
- [ctx.render()](./render.md): render into container
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# ctx.initResource()
|
|
2
2
|
|
|
3
|
-
**Initializes** the
|
|
3
|
+
**Initializes** the current context’s resource: if `ctx.resource` does not exist, creates one of the given type and binds it; otherwise uses the existing one. After that you can use `ctx.resource`.
|
|
4
4
|
|
|
5
5
|
## Use Cases
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Typically used only in **JSBlock** (standalone block). Most blocks and popups already have `ctx.resource`; JSBlock does not, so call `ctx.initResource(type)` first, then use `ctx.resource`.
|
|
8
8
|
|
|
9
|
-
## Type
|
|
9
|
+
## Type
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
initResource(
|
|
@@ -18,18 +18,18 @@ initResource(
|
|
|
18
18
|
|-----------|------|-------------|
|
|
19
19
|
| `type` | `string` | Resource type: `'APIResource'`, `'SingleRecordResource'`, `'MultiRecordResource'`, `'SQLResource'` |
|
|
20
20
|
|
|
21
|
-
**Returns**: The resource instance in the current context (i.e
|
|
21
|
+
**Returns**: The resource instance in the current context (i.e. `ctx.resource`).
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Relation to ctx.makeResource()
|
|
24
24
|
|
|
25
25
|
| Method | Behavior |
|
|
26
26
|
|--------|----------|
|
|
27
|
-
| `ctx.initResource(type)` | Creates and binds if `ctx.resource`
|
|
28
|
-
| `ctx.makeResource(type)` |
|
|
27
|
+
| `ctx.initResource(type)` | Creates and binds if `ctx.resource` is missing; otherwise returns existing. Ensures `ctx.resource` is set |
|
|
28
|
+
| `ctx.makeResource(type)` | Creates a new instance and returns it; **does not** set `ctx.resource`. Use when you need multiple resources or a temporary one |
|
|
29
29
|
|
|
30
30
|
## Examples
|
|
31
31
|
|
|
32
|
-
### List
|
|
32
|
+
### List data (MultiRecordResource)
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
ctx.initResource('MultiRecordResource');
|
|
@@ -39,17 +39,17 @@ const rows = ctx.resource.getData();
|
|
|
39
39
|
ctx.render(<pre>{JSON.stringify(rows, null, 2)}</pre>);
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
### Single
|
|
42
|
+
### Single record (SingleRecordResource)
|
|
43
43
|
|
|
44
44
|
```ts
|
|
45
45
|
ctx.initResource('SingleRecordResource');
|
|
46
46
|
ctx.resource.setResourceName('users');
|
|
47
|
-
ctx.resource.setFilterByTk(1);
|
|
47
|
+
ctx.resource.setFilterByTk(1);
|
|
48
48
|
await ctx.resource.refresh();
|
|
49
49
|
const record = ctx.resource.getData();
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
### Specify
|
|
52
|
+
### Specify data source
|
|
53
53
|
|
|
54
54
|
```ts
|
|
55
55
|
ctx.initResource('MultiRecordResource');
|
|
@@ -60,15 +60,15 @@ await ctx.resource.refresh();
|
|
|
60
60
|
|
|
61
61
|
## Notes
|
|
62
62
|
|
|
63
|
-
- In most
|
|
64
|
-
-
|
|
65
|
-
- After
|
|
63
|
+
- In most blocks (form, table, detail, etc.) and popups, `ctx.resource` is already bound; no need to call `ctx.initResource`.
|
|
64
|
+
- Only in contexts like JSBlock that have no resource by default do you need to initialize.
|
|
65
|
+
- After init, call `setResourceName(name)` and then `refresh()` to load data.
|
|
66
66
|
|
|
67
67
|
## Related
|
|
68
68
|
|
|
69
|
-
- [ctx.resource](./resource.md)
|
|
70
|
-
- [ctx.makeResource()](./make-resource.md)
|
|
71
|
-
- [MultiRecordResource](../resource/multi-record-resource.md)
|
|
72
|
-
- [SingleRecordResource](../resource/single-record-resource.md)
|
|
73
|
-
- [APIResource](../resource/api-resource.md)
|
|
74
|
-
- [SQLResource](../resource/sql-resource.md)
|
|
69
|
+
- [ctx.resource](./resource.md): resource in current context
|
|
70
|
+
- [ctx.makeResource()](./make-resource.md): create resource without binding to `ctx.resource`
|
|
71
|
+
- [MultiRecordResource](../resource/multi-record-resource.md)
|
|
72
|
+
- [SingleRecordResource](../resource/single-record-resource.md)
|
|
73
|
+
- [APIResource](../resource/api-resource.md)
|
|
74
|
+
- [SQLResource](../resource/sql-resource.md)
|