@nocobase/plugin-flow-engine 2.1.0-alpha.6 → 2.1.0-alpha.8

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.
Files changed (53) hide show
  1. package/dist/ai/docs/runjs/context/block-model.md +35 -35
  2. package/dist/ai/docs/runjs/context/collection-field.md +53 -51
  3. package/dist/ai/docs/runjs/context/collection.md +39 -39
  4. package/dist/ai/docs/runjs/context/data-source-manager.md +40 -30
  5. package/dist/ai/docs/runjs/context/data-source.md +52 -44
  6. package/dist/ai/docs/runjs/context/element.md +44 -38
  7. package/dist/ai/docs/runjs/context/exit-all.md +37 -35
  8. package/dist/ai/docs/runjs/context/exit.md +38 -35
  9. package/dist/ai/docs/runjs/context/filter-manager.md +36 -30
  10. package/dist/ai/docs/runjs/context/form.md +57 -57
  11. package/dist/ai/docs/runjs/context/get-model.md +22 -21
  12. package/dist/ai/docs/runjs/context/get-value.md +20 -19
  13. package/dist/ai/docs/runjs/context/get-var.md +61 -55
  14. package/dist/ai/docs/runjs/context/i18n.md +17 -14
  15. package/dist/ai/docs/runjs/context/import-async.md +333 -45
  16. package/dist/ai/docs/runjs/context/init-resource.md +20 -20
  17. package/dist/ai/docs/runjs/context/libs.md +31 -31
  18. package/dist/ai/docs/runjs/context/location.md +34 -31
  19. package/dist/ai/docs/runjs/context/logger.md +41 -40
  20. package/dist/ai/docs/runjs/context/make-resource.md +27 -26
  21. package/dist/ai/docs/runjs/context/message.md +42 -41
  22. package/dist/ai/docs/runjs/context/modal.md +44 -44
  23. package/dist/ai/docs/runjs/context/model.md +36 -33
  24. package/dist/ai/docs/runjs/context/notification.md +41 -40
  25. package/dist/ai/docs/runjs/context/off.md +14 -14
  26. package/dist/ai/docs/runjs/context/on.md +30 -29
  27. package/dist/ai/docs/runjs/context/open-view.md +40 -40
  28. package/dist/ai/docs/runjs/context/render.md +37 -32
  29. package/dist/ai/docs/runjs/context/request.md +46 -45
  30. package/dist/ai/docs/runjs/context/require-async.md +28 -25
  31. package/dist/ai/docs/runjs/context/resource.md +34 -34
  32. package/dist/ai/docs/runjs/context/route.md +36 -34
  33. package/dist/ai/docs/runjs/context/router.md +43 -31
  34. package/dist/ai/docs/runjs/context/set-value.md +18 -17
  35. package/dist/ai/docs/runjs/context/sql.md +7 -15
  36. package/dist/ai/docs/runjs/context/t.md +20 -17
  37. package/dist/ai/docs/runjs/context/view.md +49 -46
  38. package/dist/ai/docs/runjs/document.md +1 -0
  39. package/dist/ai/docs/runjs/import-modules.md +32 -32
  40. package/dist/ai/docs/runjs/index.md +13 -13
  41. package/dist/ai/docs/runjs/jsx.md +19 -19
  42. package/dist/ai/docs/runjs/model/form-block-model.md +1 -3
  43. package/dist/ai/docs/runjs/render.md +15 -15
  44. package/dist/ai/docs/runjs/resource/api-resource.md +53 -53
  45. package/dist/ai/docs/runjs/resource/multi-record-resource.md +64 -64
  46. package/dist/ai/docs/runjs/resource/single-record-resource.md +55 -55
  47. package/dist/ai/docs/runjs/resource/sql-resource.md +57 -57
  48. package/dist/ai/docs/runjs/window.md +5 -5
  49. package/dist/externalVersion.js +10 -10
  50. package/dist/node_modules/ses/package.json +1 -1
  51. package/dist/node_modules/zod/package.json +1 -1
  52. package/dist/server/collections/flowsql.js +1 -0
  53. package/package.json +2 -2
@@ -1,16 +1,16 @@
1
1
  # ctx.importAsync()
2
2
 
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.
3
+ Dynamically load **ESM modules** or **CSS** via URL, applicable to various RunJS scenarios. Use `ctx.importAsync()` when third-party ESM libraries are required, and `ctx.requireAsync()` for UMD/AMD libraries. Passing a `.css` address will load and inject the styles into the page.
4
4
 
5
5
  ## Use Cases
6
6
 
7
7
  | Scenario | Description |
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 |
8
+ |------|------|
9
+ | **JSBlock** | Dynamically load ESM libraries such as Vue, ECharts, or Tabulator to implement custom charts, tables, dashboards, etc. |
10
+ | **JSField / JSItem / JSColumn** | Load lightweight ESM utility libraries (e.g., dayjs plugins) to assist in rendering. |
11
+ | **Workflow / Action Events** | Load dependencies on demand before executing business logic. |
12
12
 
13
- ## Type
13
+ ## Type Definition
14
14
 
15
15
  ```ts
16
16
  importAsync<T = any>(url: string): Promise<T>;
@@ -19,90 +19,134 @@ importAsync<T = any>(url: string): Promise<T>;
19
19
  ## Parameters
20
20
 
21
21
  | Parameter | Type | Description |
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. |
22
+ |------|------|------|
23
+ | `url` | `string` | The address of the ESM module or CSS. Supports shorthand `<package>@<version>` or subpaths `<package>@<version>/<file-path>` (e.g., `vue@3.4.0`, `dayjs@1/plugin/relativeTime.js`), which will be concatenated with the CDN prefix according to the configuration. Full URLs are also supported. When a `.css` file is passed, it will be loaded and injected as a style. For libraries depending on React, you can append `?deps=react@18.2.0,react-dom@18.2.0` to ensure they share the same React instance with the page. |
24
24
 
25
- ## Returns
25
+ ## Return Value
26
26
 
27
- - Resolved module namespace object (Promise value).
27
+ - A Promise that resolves to the module's namespace object.
28
28
 
29
- ## URL format
29
+ ## URL Format Description
30
30
 
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)
31
+ - **ESM and CSS**: In addition to ESM modules, loading CSS is also supported (pass a `.css` URL to load and inject it into the page).
32
+ - **Shorthand Format**: Defaults to **https://esm.sh** as the CDN prefix if not configured. For example, `vue@3.4.0` actually requests `https://esm.sh/vue@3.4.0`.
33
+ - **?deps**: Libraries that depend on React (such as `@dnd-kit/core`, `react-big-calendar`) should append `?deps=react@18.2.0,react-dom@18.2.0` to avoid conflicts with the page's React instance, which could lead to "Invalid hook call" errors.
34
+ - **Self-hosted CDN**: You can specify an internal network or self-hosted service via environment variables:
35
+ - **ESM_CDN_BASE_URL**: The base URL for the ESM CDN (default is `https://esm.sh`).
36
+ - **ESM_CDN_SUFFIX**: Optional suffix (e.g., `/+esm` for jsDelivr).
37
+ - For self-hosted services, refer to: [nocobase/esm-server](https://github.com/nocobase/esm-server)
38
38
 
39
- ## vs ctx.requireAsync()
39
+ ## Difference from ctx.requireAsync()
40
40
 
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()`.
41
+ - **ctx.importAsync()**: Loads **ESM modules** and returns the module namespace. Suitable for modern libraries (ESM builds like Vue, dayjs, etc.).
42
+ - **ctx.requireAsync()**: Loads **UMD/AMD** modules or scripts that attach to the global scope. Often used for UMD libraries like ECharts or FullCalendar. If a library provides both ESM and UMD, `ctx.importAsync()` is preferred.
43
43
 
44
44
  ## Examples
45
45
 
46
- ### Basic
46
+ ### Basic Usage
47
+
48
+ Demonstrates the most basic usage of dynamically loading ESM modules and CSS by package name or full URL.
47
49
 
48
50
  ```javascript
49
51
  const Vue = await ctx.importAsync('vue@3.4.0');
52
+ // Equivalent to loading from https://esm.sh/vue@3.4.0
50
53
 
51
54
  const relativeTime = await ctx.importAsync('dayjs@1/plugin/relativeTime.js');
55
+ // With subpath (e.g., dayjs plugin)
52
56
 
53
57
  const pkg = await ctx.importAsync('https://cdn.example.com/my-module.js');
58
+ // Full URL
54
59
 
55
60
  await ctx.importAsync('https://cdn.example.com/theme.css');
61
+ // Load CSS and inject into the page
56
62
  ```
57
63
 
58
- ### ECharts
64
+ ### ECharts Example
65
+
66
+ Use ECharts to draw a sales overview chart with bar and line graphs.
59
67
 
60
68
  ```ts
69
+ // 1. Dynamically load ECharts module
61
70
  const echarts = await ctx.importAsync('echarts@5.4.3');
62
71
 
72
+ // 2. Create chart container and render
63
73
  const chartEl = document.createElement('div');
64
74
  chartEl.style.width = '100%';
65
75
  chartEl.style.height = '400px';
66
76
  ctx.render(chartEl);
67
77
 
78
+ // 3. Initialize ECharts instance
68
79
  const chart = echarts.init(chartEl);
69
80
 
81
+ // 4. Configure chart
70
82
  const option = {
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' },
83
+ title: {
84
+ text: 'Sales Overview',
85
+ left: 'center',
86
+ },
87
+ tooltip: {
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
+ },
76
101
  series: [
77
- { name: 'Sales', type: 'bar', data: [120, 200, 150, 80, 70, 110] },
78
- { name: 'Profit', type: 'line', data: [20, 40, 30, 15, 12, 25] },
102
+ {
103
+ name: 'Sales',
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
+ },
79
112
  ],
80
113
  };
81
114
 
115
+ // 5. Set option and render chart
82
116
  chart.setOption(option);
83
117
 
84
- window.addEventListener('resize', () => chart.resize());
118
+ // 6. Optional: Responsive sizing
119
+ window.addEventListener('resize', () => {
120
+ chart.resize();
121
+ });
85
122
 
123
+ // 7. Optional: Event listener
86
124
  chart.on('click', (params) => {
87
125
  ctx.message.info(`Clicked ${params.seriesName} on ${params.name}, value: ${params.value}`);
88
126
  });
89
127
  ```
90
128
 
91
- ### Tabulator
129
+ ### Tabulator Example
130
+
131
+ Demonstrates rendering a data table with pagination and row click events in a block using Tabulator.
92
132
 
93
133
  ```ts
134
+ // 1. Load Tabulator styles
94
135
  await ctx.importAsync('tabulator-tables@6.2.5/dist/css/tabulator.min.css');
95
136
 
137
+ // 2. Dynamically load Tabulator module
96
138
  const { TabulatorFull } = await ctx.importAsync('tabulator-tables@6.2.5');
97
139
 
140
+ // 3. Create table container and render
98
141
  const tableEl = document.createElement('div');
99
142
  ctx.render(tableEl);
100
143
 
144
+ // 4. Initialize Tabulator table
101
145
  const table = new TabulatorFull(tableEl, {
102
146
  data: [
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' },
147
+ { id: 1, name: 'Alice', age: 25, city: 'New York' },
148
+ { id: 2, name: 'Bob', age: 30, city: 'London' },
149
+ { id: 3, name: 'Charlie', age: 28, city: 'Paris' },
106
150
  ],
107
151
  columns: [
108
152
  { title: 'ID', field: 'id', width: 80 },
@@ -115,22 +159,30 @@ const table = new TabulatorFull(tableEl, {
115
159
  paginationSize: 10,
116
160
  });
117
161
 
162
+ // 5. Optional: Event listener
118
163
  table.on('rowClick', (e, row) => {
119
164
  const rowData = row.getData();
120
165
  ctx.message.info(`Row clicked: ${rowData.name}`);
121
166
  });
122
167
  ```
123
168
 
124
- ### FullCalendar (ESM)
169
+ ### FullCalendar (ESM) Example
170
+
171
+ Shows how to load FullCalendar and its plugins via ESM and render a basic monthly view calendar.
125
172
 
126
173
  ```ts
174
+ // 1. Dynamically load FullCalendar core module
127
175
  const { Calendar } = await ctx.importAsync('@fullcalendar/core@6.1.20');
176
+
177
+ // 2. Dynamically load dayGrid plugin
128
178
  const dayGridPlugin = await ctx.importAsync('@fullcalendar/daygrid@6.1.20');
129
179
 
180
+ // 3. Create calendar container and render
130
181
  const calendarEl = document.createElement('div');
131
182
  calendarEl.id = 'calendar';
132
183
  ctx.render(calendarEl);
133
184
 
185
+ // 4. Initialize and render calendar
134
186
  const calendar = new Calendar(calendarEl, {
135
187
  plugins: [dayGridPlugin.default || dayGridPlugin],
136
188
  headerToolbar: {
@@ -143,22 +195,160 @@ const calendar = new Calendar(calendarEl, {
143
195
  calendar.render();
144
196
  ```
145
197
 
146
- ### dnd-kit (with ?deps)
198
+ ### dnd-kit Simple Drag-and-Drop Example
147
199
 
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:
200
+ Uses `@dnd-kit/core` to implement a minimal example of dragging a Box to a target area within a block.
149
201
 
150
202
  ```ts
203
+ // 1. Load React, react-dom, @dnd-kit/core (?deps ensures same React instance to avoid Invalid hook call)
151
204
  const React = await ctx.importAsync('react@18.2.0');
152
205
  const { createRoot } = await ctx.importAsync('react-dom@18.2.0/client');
153
206
  const core = await ctx.importAsync('@dnd-kit/core@6.3.1?deps=react@18.2.0,react-dom@18.2.0');
154
- // Use core (DndContext, useDraggable, useDroppable, etc.) with React
207
+ const { DndContext, closestCenter, PointerSensor, useSensor, useSensors, useDraggable, useDroppable } = core;
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 />);
155
252
  ```
156
253
 
157
- ### react-big-calendar
254
+ This example relies only on `@dnd-kit/core` to trigger a notification when a Box is dropped into a specific area, demonstrating the simplest drag-and-drop interaction combining `ctx.importAsync` and React in RunJS.
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.
158
346
 
159
347
  ```tsx
348
+ // 1. Load styles (ctx.importAsync uses ctx.loadCSS for .css files)
160
349
  await ctx.importAsync('react-big-calendar@1.11.4/lib/css/react-big-calendar.css');
161
350
 
351
+ // 2. Load React, react-dom, react-big-calendar, date-fns, and locale (ensuring same React instance)
162
352
  const React = await ctx.importAsync('react@18.2.0');
163
353
  const { Calendar, dateFnsLocalizer } = await ctx.importAsync('react-big-calendar@1.11.4?deps=react@18.2.0,react-dom@18.2.0');
164
354
  const { format, parse, startOfWeek, getDay } = await ctx.importAsync('date-fns@2.30.0');
@@ -177,6 +367,7 @@ const events = [
177
367
  { title: 'Meeting', start: new Date(2026, 0, 29, 10, 0), end: new Date(2026, 0, 29, 11, 0) },
178
368
  ];
179
369
 
370
+ // 3. Render React Calendar
180
371
  ctx.render(
181
372
  <Calendar
182
373
  localizer={localizer}
@@ -188,13 +379,110 @@ ctx.render(
188
379
  );
189
380
  ```
190
381
 
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
+
191
479
  ## Notes
192
480
 
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.
481
+ - This feature depends on an external network or CDN. In internal network environments, **ESM_CDN_BASE_URL** must be configured to point to a self-hosted service.
482
+ - When a library provides both ESM and UMD, prefer `ctx.importAsync()` for better module semantics.
483
+ - For libraries depending on React, ensure you append `?deps=react@18.2.0,react-dom@18.2.0`. The version must match the React version used by the page, otherwise, an "Invalid hook call" error may occur.
196
484
 
197
485
  ## Related
198
486
 
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
487
+ - [ctx.requireAsync()](./require-async.md): Load UMD/AMD or globally attached scripts, suitable for UMD libraries like ECharts and FullCalendar.
488
+ - [ctx.render()](./render.md): Render content into a container.
@@ -1,12 +1,12 @@
1
1
  # ctx.initResource()
2
2
 
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`.
3
+ **Initializes** the resource for the current context. If `ctx.resource` does not already exist, it creates one of the specified type and binds it to the context; if it already exists, it is used directly. Afterward, it can be accessed via `ctx.resource`.
4
4
 
5
5
  ## Use Cases
6
6
 
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`.
7
+ Generally used in **JSBlock** (independent block) scenarios. Most blocks, popups, and other components have `ctx.resource` pre-bound and do not require manual calls. JSBlock has no resource by default, so you must call `ctx.initResource(type)` before loading data via `ctx.resource`.
8
8
 
9
- ## Type
9
+ ## Type Definition
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. `ctx.resource`).
21
+ **Returns**: The resource instance in the current context (i.e., `ctx.resource`).
22
22
 
23
- ## Relation to ctx.makeResource()
23
+ ## Difference from ctx.makeResource()
24
24
 
25
25
  | Method | Behavior |
26
26
  |--------|----------|
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 |
27
+ | `ctx.initResource(type)` | Creates and binds if `ctx.resource` does not exist; returns the existing one if it does. Ensures `ctx.resource` is available. |
28
+ | `ctx.makeResource(type)` | Only creates and returns a new instance, does **not** write to `ctx.resource`. Suitable for scenarios requiring multiple independent resources or temporary use. |
29
29
 
30
30
  ## Examples
31
31
 
32
- ### List data (MultiRecordResource)
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 record (SingleRecordResource)
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); // Specify primary key
48
48
  await ctx.resource.refresh();
49
49
  const record = ctx.resource.getData();
50
50
  ```
51
51
 
52
- ### Specify data source
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 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.
63
+ - In most block scenarios (Forms, Tables, Details, etc.) and popups, `ctx.resource` is already pre-bound by the runtime environment, so calling `ctx.initResource` is unnecessary.
64
+ - Manual initialization is only required in contexts like JSBlock where there is no default resource.
65
+ - After initialization, you must call `setResourceName(name)` to specify the collection, and then call `refresh()` to load the data.
66
66
 
67
67
  ## Related
68
68
 
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)
69
+ - [ctx.resource](./resource.md) — The resource instance in the current context
70
+ - [ctx.makeResource()](./make-resource.md) Creates a new resource instance without binding it to `ctx.resource`
71
+ - [MultiRecordResource](../resource/multi-record-resource.md) — Multiple records/List
72
+ - [SingleRecordResource](../resource/single-record-resource.md) — Single record
73
+ - [APIResource](../resource/api-resource.md) — General API resource
74
+ - [SQLResource](../resource/sql-resource.md) — SQL query resource