@fctc/widget-logic 1.9.2 → 1.9.4
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +154 -409
- package/dist/index.mjs +122 -385
- package/dist/widget.d.mts +7 -30
- package/dist/widget.d.ts +7 -30
- package/dist/widget.js +154 -409
- package/dist/widget.mjs +122 -383
- package/package.json +1 -1
package/dist/widget.mjs
CHANGED
|
@@ -4978,6 +4978,7 @@ var many2oneFieldController = (props) => {
|
|
|
4978
4978
|
options: fieldOptions,
|
|
4979
4979
|
showDetail
|
|
4980
4980
|
} = props;
|
|
4981
|
+
const { env } = (0, provider_exports.useEnv)();
|
|
4981
4982
|
const [options, setOptions] = useState7([]);
|
|
4982
4983
|
const [inputValue, setInputValue] = useState7("");
|
|
4983
4984
|
const [debouncedInputValue] = useDebounce(inputValue, 1e3);
|
|
@@ -4987,13 +4988,12 @@ var many2oneFieldController = (props) => {
|
|
|
4987
4988
|
const [domainObject, setDomainObject] = useState7(null);
|
|
4988
4989
|
const actionData = sessionStorageUtils.getActionData();
|
|
4989
4990
|
const { menuList } = (0, store_exports.useAppSelector)(store_exports.selectNavbar);
|
|
4990
|
-
const { context } = (0, store_exports.useAppSelector)(store_exports.selectEnv);
|
|
4991
4991
|
const initValue = methods?.getValues(name);
|
|
4992
4992
|
const optionsObject = (0, utils_exports.evalJSONContext)(fieldOptions) || {};
|
|
4993
4993
|
const contextObject = {
|
|
4994
4994
|
...(0, utils_exports.evalJSONContext)(actionData?.context) || {},
|
|
4995
4995
|
...fieldContext,
|
|
4996
|
-
...context
|
|
4996
|
+
...env?.context
|
|
4997
4997
|
};
|
|
4998
4998
|
const { useGetSelection: useGetSelection3 } = (0, provider_exports.useService)();
|
|
4999
4999
|
const data = {
|
|
@@ -5165,15 +5165,9 @@ var many2oneButtonController = (props) => {
|
|
|
5165
5165
|
|
|
5166
5166
|
// src/widget/basic/many2many-field/controller.ts
|
|
5167
5167
|
import { useEffect as useEffect11, useMemo as useMemo10, useState as useState8 } from "react";
|
|
5168
|
-
import {
|
|
5169
|
-
useAppDispatch as useAppDispatch5,
|
|
5170
|
-
setFirstDomain,
|
|
5171
|
-
setViewDataStore,
|
|
5172
|
-
setPage,
|
|
5173
|
-
setGroupByDomain
|
|
5174
|
-
} from "@fctc/interface-logic/store";
|
|
5175
5168
|
import {
|
|
5176
5169
|
evalJSONContext as evalJSONContext4,
|
|
5170
|
+
evalJSONDomain as evalJSONDomain4,
|
|
5177
5171
|
formatSortingString as formatSortingString2
|
|
5178
5172
|
} from "@fctc/interface-logic/utils";
|
|
5179
5173
|
var many2manyFieldController = (props) => {
|
|
@@ -5181,45 +5175,41 @@ var many2manyFieldController = (props) => {
|
|
|
5181
5175
|
relation,
|
|
5182
5176
|
domain,
|
|
5183
5177
|
context,
|
|
5178
|
+
options,
|
|
5184
5179
|
tab,
|
|
5185
|
-
model,
|
|
5186
|
-
aid,
|
|
5187
5180
|
setSelectedRowKeys: setSelectedRowKeys4,
|
|
5188
|
-
fields,
|
|
5189
|
-
setFields,
|
|
5190
5181
|
groupByDomain,
|
|
5191
|
-
page,
|
|
5192
|
-
options,
|
|
5193
5182
|
sessionStorageUtils
|
|
5194
5183
|
} = props;
|
|
5195
|
-
const appDispatch = useAppDispatch5();
|
|
5196
|
-
const actionData = sessionStorageUtils.getActionData();
|
|
5197
|
-
const [debouncedPage] = useDebounce(page, 500);
|
|
5198
|
-
const [order, setOrder] = useState8();
|
|
5199
|
-
const [isLoadedData, setIsLoadedData] = useState8(false);
|
|
5200
|
-
const [domainMany2Many, setDomainMany2Many] = useState8(domain);
|
|
5201
5184
|
const { env } = (0, provider_exports.useEnv)();
|
|
5202
5185
|
const { useGetView: useGetView2, useGetListData: useGetListData3, useGetFormView } = (0, provider_exports.useService)();
|
|
5186
|
+
const [order, setOrder] = useState8();
|
|
5187
|
+
const [isLoadedData, setIsLoadedData] = useState8(false);
|
|
5188
|
+
const [page, setPage] = useState8(0);
|
|
5189
|
+
const [domainMany2Many, setDomainMany2Many] = useState8(null);
|
|
5190
|
+
const [debouncedPage] = useDebounce(page, 500);
|
|
5191
|
+
const actionData = sessionStorageUtils.getActionData();
|
|
5192
|
+
const contextObject = { ...env.context, ...context };
|
|
5203
5193
|
const viewParams = {
|
|
5204
5194
|
model: relation,
|
|
5205
5195
|
views: [
|
|
5206
5196
|
[false, "list"],
|
|
5207
5197
|
[false, "search"]
|
|
5208
5198
|
],
|
|
5209
|
-
context
|
|
5199
|
+
context: contextObject
|
|
5210
5200
|
};
|
|
5211
5201
|
const { data: viewResponse } = useGetView2(viewParams, actionData);
|
|
5212
5202
|
const baseModel = useMemo10(
|
|
5213
5203
|
() => ({
|
|
5214
5204
|
name: String(relation),
|
|
5215
5205
|
view: viewResponse || {},
|
|
5216
|
-
actContext:
|
|
5206
|
+
actContext: contextObject,
|
|
5217
5207
|
fields: [
|
|
5218
5208
|
...Object.values(viewResponse?.views?.list?.fields ?? {}),
|
|
5219
5209
|
...tab?.fields ? tab.fields : []
|
|
5220
5210
|
]
|
|
5221
5211
|
}),
|
|
5222
|
-
[
|
|
5212
|
+
[relation, viewResponse]
|
|
5223
5213
|
);
|
|
5224
5214
|
const initModel = (0, hooks_exports.useModel)();
|
|
5225
5215
|
const modelInstance = useMemo10(() => {
|
|
@@ -5238,26 +5228,15 @@ var many2manyFieldController = (props) => {
|
|
|
5238
5228
|
const optionsObject = tab?.options ? evalJSONContext4(tab?.options) : (options ? evalJSONContext4(options) : {}) || {};
|
|
5239
5229
|
const fetchData = async () => {
|
|
5240
5230
|
try {
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
const modalData = viewResponse?.views?.list?.fields.map((field) => ({
|
|
5245
|
-
...viewResponse?.models?.[String(model)]?.[field?.name],
|
|
5246
|
-
...field
|
|
5247
|
-
}));
|
|
5248
|
-
if (!fields?.[`${aid}_${relation}_popupmany2many`] && modalData) {
|
|
5249
|
-
setFields({
|
|
5250
|
-
...fields,
|
|
5251
|
-
[`${aid}_${relation}_popupmany2many`]: modalData
|
|
5252
|
-
});
|
|
5253
|
-
}
|
|
5254
|
-
appDispatch(setPage(0));
|
|
5231
|
+
const domainParse = typeof domain === "string" ? evalJSONDomain4(domain, contextObject) : Array.isArray(domain) ? domain : [];
|
|
5232
|
+
setDomainMany2Many(domainParse);
|
|
5233
|
+
setPage(0);
|
|
5255
5234
|
} catch (err) {
|
|
5256
5235
|
console.log(err);
|
|
5257
5236
|
}
|
|
5258
5237
|
};
|
|
5259
5238
|
const queryKey = [
|
|
5260
|
-
`view-${relation}
|
|
5239
|
+
`view-${relation}`,
|
|
5261
5240
|
specification,
|
|
5262
5241
|
domainMany2Many,
|
|
5263
5242
|
debouncedPage,
|
|
@@ -5270,7 +5249,7 @@ var many2manyFieldController = (props) => {
|
|
|
5270
5249
|
domain: domainMany2Many,
|
|
5271
5250
|
offset: debouncedPage * 10,
|
|
5272
5251
|
limit: 10,
|
|
5273
|
-
context,
|
|
5252
|
+
context: contextObject,
|
|
5274
5253
|
fields: groupByDomain?.fields,
|
|
5275
5254
|
groupby: [groupByDomain?.contexts[0]?.group_by],
|
|
5276
5255
|
sort: order ? order : default_order ? formatSortingString2(default_order) : ""
|
|
@@ -5278,8 +5257,8 @@ var many2manyFieldController = (props) => {
|
|
|
5278
5257
|
const enabled = isLoadedData && !!specification && !!relation && !!domainMany2Many && !!viewResponse;
|
|
5279
5258
|
const {
|
|
5280
5259
|
data: dataResponse,
|
|
5281
|
-
isLoading
|
|
5282
|
-
isFetched
|
|
5260
|
+
isLoading,
|
|
5261
|
+
isFetched,
|
|
5283
5262
|
isPlaceholderData
|
|
5284
5263
|
} = useGetListData3(data, queryKey, enabled);
|
|
5285
5264
|
useEffect11(() => {
|
|
@@ -5287,12 +5266,7 @@ var many2manyFieldController = (props) => {
|
|
|
5287
5266
|
fetchData();
|
|
5288
5267
|
}
|
|
5289
5268
|
return () => {
|
|
5290
|
-
|
|
5291
|
-
setFields((prevFields) => ({
|
|
5292
|
-
...prevFields,
|
|
5293
|
-
[`${aid}_${relation}_popupmany2many`]: null
|
|
5294
|
-
}));
|
|
5295
|
-
appDispatch(setPage(0));
|
|
5269
|
+
setPage(0);
|
|
5296
5270
|
setSelectedRowKeys4([]);
|
|
5297
5271
|
setDomainMany2Many(null);
|
|
5298
5272
|
setIsLoadedData(false);
|
|
@@ -5300,44 +5274,19 @@ var many2manyFieldController = (props) => {
|
|
|
5300
5274
|
}, [viewResponse]);
|
|
5301
5275
|
const { rows, columns, typeTable } = tableController({
|
|
5302
5276
|
data: {
|
|
5303
|
-
fields:
|
|
5277
|
+
fields: viewResponse?.views?.list?.fields,
|
|
5304
5278
|
records: dataResponse?.records ?? dataResponse?.groups,
|
|
5305
5279
|
dataModel: viewResponse?.models?.[String(relation)],
|
|
5306
|
-
context:
|
|
5280
|
+
context: contextObject,
|
|
5307
5281
|
typeTable: dataResponse?.groups ? "group" : "list"
|
|
5308
5282
|
}
|
|
5309
5283
|
});
|
|
5310
|
-
const dataFormView = {
|
|
5311
|
-
id: null,
|
|
5312
|
-
model: relation,
|
|
5313
|
-
context
|
|
5314
|
-
};
|
|
5315
|
-
const {
|
|
5316
|
-
refetch,
|
|
5317
|
-
data: dataFormViewResponse,
|
|
5318
|
-
isSuccess
|
|
5319
|
-
} = useGetFormView({
|
|
5320
|
-
data: dataFormView,
|
|
5321
|
-
queryKey: [`form-view-action-${relation}`],
|
|
5322
|
-
enabled: false
|
|
5323
|
-
});
|
|
5324
|
-
useEffect11(() => {
|
|
5325
|
-
if (isSuccess && dataFormViewResponse) {
|
|
5326
|
-
sessionStorage.setItem("actionData", JSON.stringify(dataFormViewResponse));
|
|
5327
|
-
window.location.href = `/form/menu?model=${relation}`;
|
|
5328
|
-
}
|
|
5329
|
-
}, [isSuccess]);
|
|
5330
5284
|
useEffect11(() => {
|
|
5331
5285
|
if (domainMany2Many && !isLoadedData) {
|
|
5332
5286
|
setIsLoadedData(true);
|
|
5333
5287
|
}
|
|
5334
5288
|
}, [domainMany2Many]);
|
|
5335
5289
|
const handleCreateNewOnPage = async () => {
|
|
5336
|
-
try {
|
|
5337
|
-
refetch();
|
|
5338
|
-
} catch (error) {
|
|
5339
|
-
console.log(error);
|
|
5340
|
-
}
|
|
5341
5290
|
};
|
|
5342
5291
|
return {
|
|
5343
5292
|
handleCreateNewOnPage,
|
|
@@ -5345,9 +5294,13 @@ var many2manyFieldController = (props) => {
|
|
|
5345
5294
|
rows,
|
|
5346
5295
|
columns,
|
|
5347
5296
|
typeTable,
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
isPlaceholderData
|
|
5297
|
+
isLoading,
|
|
5298
|
+
isFetched,
|
|
5299
|
+
isPlaceholderData,
|
|
5300
|
+
setPage,
|
|
5301
|
+
page,
|
|
5302
|
+
domain: domainMany2Many,
|
|
5303
|
+
setDomain: setDomainMany2Many
|
|
5351
5304
|
};
|
|
5352
5305
|
};
|
|
5353
5306
|
|
|
@@ -5356,7 +5309,7 @@ import { useMemo as useMemo11 } from "react";
|
|
|
5356
5309
|
import { WIDGETAVATAR, WIDGETCOLOR } from "@fctc/interface-logic/constants";
|
|
5357
5310
|
import { getEnv as getEnv7 } from "@fctc/interface-logic/environment";
|
|
5358
5311
|
import { useGetSelection as useGetSelection2 } from "@fctc/interface-logic/hooks";
|
|
5359
|
-
import { evalJSONContext as evalJSONContext5, evalJSONDomain as
|
|
5312
|
+
import { evalJSONContext as evalJSONContext5, evalJSONDomain as evalJSONDomain5 } from "@fctc/interface-logic/utils";
|
|
5360
5313
|
var many2manyTagsController = (props) => {
|
|
5361
5314
|
const {
|
|
5362
5315
|
relation,
|
|
@@ -5370,7 +5323,7 @@ var many2manyTagsController = (props) => {
|
|
|
5370
5323
|
const env = getEnv7();
|
|
5371
5324
|
const addtionalFields = optionsFields ? evalJSONContext5(optionsFields) : null;
|
|
5372
5325
|
const domainObject = useMemo11(
|
|
5373
|
-
() =>
|
|
5326
|
+
() => evalJSONDomain5(domain, JSON.parse(JSON.stringify(formValues || {}))),
|
|
5374
5327
|
[domain, formValues]
|
|
5375
5328
|
);
|
|
5376
5329
|
const data = {
|
|
@@ -5413,7 +5366,7 @@ var many2manyTagsController = (props) => {
|
|
|
5413
5366
|
|
|
5414
5367
|
// src/widget/basic/status-bar-field/controller.ts
|
|
5415
5368
|
import { useState as useState9 } from "react";
|
|
5416
|
-
import { evalJSONDomain as
|
|
5369
|
+
import { evalJSONDomain as evalJSONDomain6 } from "@fctc/interface-logic/utils";
|
|
5417
5370
|
var durationController = (props) => {
|
|
5418
5371
|
const { relation, domain, formValues, name, id, model, onRefetch } = props;
|
|
5419
5372
|
const specification = {
|
|
@@ -5429,7 +5382,7 @@ var durationController = (props) => {
|
|
|
5429
5382
|
const listDataProps = {
|
|
5430
5383
|
model: relation,
|
|
5431
5384
|
specification,
|
|
5432
|
-
domain:
|
|
5385
|
+
domain: evalJSONDomain6(domain, JSON.parse(JSON.stringify(formValues))),
|
|
5433
5386
|
limit: 10,
|
|
5434
5387
|
offset: 0,
|
|
5435
5388
|
fields: "",
|
|
@@ -5529,98 +5482,63 @@ var priorityFieldController = (props) => {
|
|
|
5529
5482
|
};
|
|
5530
5483
|
};
|
|
5531
5484
|
|
|
5532
|
-
// src/widget/basic/
|
|
5533
|
-
import { useState as useState10 } from "react";
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
onChange: fieldOnChange,
|
|
5540
|
-
onBlur,
|
|
5541
|
-
value,
|
|
5542
|
-
isDirty,
|
|
5543
|
-
props
|
|
5544
|
-
}) => {
|
|
5545
|
-
const { name, defaultValue = 0, onChange } = props;
|
|
5546
|
-
const [input, setInput] = useState10(
|
|
5547
|
-
convertFloatToTime(value ?? defaultValue)
|
|
5548
|
-
);
|
|
5549
|
-
const [formattedTime, setFormattedTime] = useState10("");
|
|
5550
|
-
const [errors, setErrors] = useState10("");
|
|
5551
|
-
const handleInputChange = (e) => {
|
|
5552
|
-
const raw = e.target.value.replace(/[^\d:]/g, "");
|
|
5553
|
-
setInput(raw);
|
|
5554
|
-
const timeRegex = /^(\d{1,2}):?(\d{0,2})$/;
|
|
5555
|
-
const match = raw.match(timeRegex);
|
|
5556
|
-
if (!match) {
|
|
5557
|
-
setErrors("\u0110\u1ECBnh d\u1EA1ng kh\xF4ng h\u1EE3p l\u1EC7");
|
|
5558
|
-
setFormattedTime("");
|
|
5559
|
-
return;
|
|
5560
|
-
}
|
|
5561
|
-
let hours = parseInt(match[1] ?? "0", 10);
|
|
5562
|
-
let minutes = parseInt(match[2] ?? "0", 10);
|
|
5563
|
-
if (isNaN(hours)) hours = 0;
|
|
5564
|
-
if (isNaN(minutes)) minutes = 0;
|
|
5565
|
-
if (hours >= 24) {
|
|
5566
|
-
hours = 0;
|
|
5567
|
-
}
|
|
5568
|
-
if (minutes >= 60) {
|
|
5569
|
-
minutes = 0;
|
|
5570
|
-
}
|
|
5571
|
-
const formatted = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
|
|
5572
|
-
setErrors("");
|
|
5573
|
-
setFormattedTime(formatted);
|
|
5574
|
-
fieldOnChange(formatted);
|
|
5485
|
+
// src/widget/basic/download-file-field/controller.ts
|
|
5486
|
+
import { useId, useState as useState10 } from "react";
|
|
5487
|
+
var downloadFileController = () => {
|
|
5488
|
+
const inputId = useId();
|
|
5489
|
+
const [file, setFile] = useState10(null);
|
|
5490
|
+
const handleFileChange = (e) => {
|
|
5491
|
+
setFile(e.target.files[0]);
|
|
5575
5492
|
};
|
|
5576
|
-
const
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
}
|
|
5585
|
-
} else {
|
|
5586
|
-
setInput("00:00");
|
|
5587
|
-
fieldOnChange(0);
|
|
5588
|
-
if (onChange) {
|
|
5589
|
-
onChange(name ?? "", 0);
|
|
5590
|
-
}
|
|
5591
|
-
setErrors("");
|
|
5592
|
-
}
|
|
5593
|
-
onBlur();
|
|
5493
|
+
const handleFileDownload = () => {
|
|
5494
|
+
const url = URL.createObjectURL(file);
|
|
5495
|
+
const link = document.createElement("a");
|
|
5496
|
+
link.href = url;
|
|
5497
|
+
link.download = file.name;
|
|
5498
|
+
document.body.appendChild(link);
|
|
5499
|
+
link.click();
|
|
5500
|
+
document.body.removeChild(link);
|
|
5594
5501
|
};
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5502
|
+
return {
|
|
5503
|
+
inputId,
|
|
5504
|
+
file,
|
|
5505
|
+
handleFileChange,
|
|
5506
|
+
handleFileDownload
|
|
5507
|
+
};
|
|
5508
|
+
};
|
|
5509
|
+
|
|
5510
|
+
// src/widget/basic/download-binary-field/controller.ts
|
|
5511
|
+
var downLoadBinaryController = (props) => {
|
|
5512
|
+
const { value, defaultValue, formValues } = props;
|
|
5513
|
+
const handleFileDownload = async (e) => {
|
|
5514
|
+
e.stopPropagation();
|
|
5515
|
+
await downloadFile(value || defaultValue, formValues?.name);
|
|
5516
|
+
};
|
|
5517
|
+
const downloadFile = async (url, filename) => {
|
|
5518
|
+
try {
|
|
5519
|
+
const response = await fetch(url);
|
|
5520
|
+
if (response) {
|
|
5521
|
+
const blob = await response.blob();
|
|
5522
|
+
const urlBlob = window.URL.createObjectURL(blob);
|
|
5523
|
+
const link = document.createElement("a");
|
|
5524
|
+
link.href = urlBlob;
|
|
5525
|
+
link.download = filename || "downloaded-file";
|
|
5526
|
+
document.body.appendChild(link);
|
|
5527
|
+
link.click();
|
|
5528
|
+
document.body.removeChild(link);
|
|
5529
|
+
window.URL.revokeObjectURL(urlBlob);
|
|
5610
5530
|
}
|
|
5531
|
+
} catch (error) {
|
|
5532
|
+
console.error("File download failed:", error);
|
|
5611
5533
|
}
|
|
5612
5534
|
};
|
|
5613
5535
|
return {
|
|
5614
|
-
|
|
5615
|
-
handleBlur,
|
|
5616
|
-
handleKeyDown,
|
|
5617
|
-
input,
|
|
5618
|
-
errors
|
|
5536
|
+
handleFileDownload
|
|
5619
5537
|
};
|
|
5620
5538
|
};
|
|
5621
5539
|
|
|
5622
|
-
// src/widget/basic/
|
|
5623
|
-
|
|
5540
|
+
// src/widget/basic/date-field/controller.ts
|
|
5541
|
+
var import_moment = __toESM(require_moment());
|
|
5624
5542
|
|
|
5625
5543
|
// src/utils/i18n.ts
|
|
5626
5544
|
import { initReactI18next } from "react-i18next";
|
|
@@ -6388,184 +6306,7 @@ i18n.use(LanguageDetector).use(initReactI18next).init({
|
|
|
6388
6306
|
});
|
|
6389
6307
|
var i18n_default = i18n;
|
|
6390
6308
|
|
|
6391
|
-
// src/widget/basic/float-field/controller.ts
|
|
6392
|
-
var floatController = ({
|
|
6393
|
-
onChange,
|
|
6394
|
-
value,
|
|
6395
|
-
props
|
|
6396
|
-
}) => {
|
|
6397
|
-
const { name, required, methods, onChange: handleOnchange, string } = props;
|
|
6398
|
-
const { setError, clearErrors } = methods;
|
|
6399
|
-
const [inputValue, setInputValue] = useState11(
|
|
6400
|
-
value !== void 0 && value !== null ? useFormatFloatNumber(value) : ""
|
|
6401
|
-
);
|
|
6402
|
-
useEffect12(() => {
|
|
6403
|
-
if (value !== void 0 && value !== null && value !== parseFloat(inputValue?.replace(/,/g, ""))) {
|
|
6404
|
-
setInputValue(useFormatFloatNumber(value));
|
|
6405
|
-
clearErrors(name);
|
|
6406
|
-
} else if (value === null || value === void 0) {
|
|
6407
|
-
setInputValue("");
|
|
6408
|
-
}
|
|
6409
|
-
}, [value, name, clearErrors]);
|
|
6410
|
-
const isDirtyRef = useRef4(false);
|
|
6411
|
-
const inputRef = useRef4(null);
|
|
6412
|
-
const lastCommittedValueRef = useRef4(null);
|
|
6413
|
-
const handleInputChange = (e) => {
|
|
6414
|
-
const newValue = e.target.value;
|
|
6415
|
-
const valueWithoutCommas = newValue.replace(/,/g, "");
|
|
6416
|
-
if (/^[0-9]*[.,]?[0-9]*$/.test(valueWithoutCommas) || newValue === "") {
|
|
6417
|
-
const parts = valueWithoutCommas.split(".");
|
|
6418
|
-
let integerPart = parts[0] || "";
|
|
6419
|
-
const decimalPart = parts[1] || "";
|
|
6420
|
-
if (decimalPart.length > 100) return;
|
|
6421
|
-
if (integerPart) {
|
|
6422
|
-
integerPart = Number(integerPart).toLocaleString("en-US");
|
|
6423
|
-
}
|
|
6424
|
-
const formattedValue = decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
|
|
6425
|
-
setInputValue(formattedValue);
|
|
6426
|
-
const parsedValue = parseFloat(valueWithoutCommas.replace(",", "."));
|
|
6427
|
-
if (!isNaN(parsedValue)) {
|
|
6428
|
-
if (parsedValue < 0) {
|
|
6429
|
-
setError(name, {
|
|
6430
|
-
type: "validate",
|
|
6431
|
-
message: i18n_default.t("invalid_number")
|
|
6432
|
-
});
|
|
6433
|
-
} else {
|
|
6434
|
-
onChange(parsedValue);
|
|
6435
|
-
clearErrors(name);
|
|
6436
|
-
isDirtyRef.current = true;
|
|
6437
|
-
}
|
|
6438
|
-
} else {
|
|
6439
|
-
onChange(null);
|
|
6440
|
-
clearErrors(name);
|
|
6441
|
-
}
|
|
6442
|
-
}
|
|
6443
|
-
};
|
|
6444
|
-
const handleInputMouseLeave = () => {
|
|
6445
|
-
if (!isDirtyRef.current) {
|
|
6446
|
-
inputRef.current?.blur();
|
|
6447
|
-
return;
|
|
6448
|
-
}
|
|
6449
|
-
const rawValue = inputValue.replace(/,/g, "");
|
|
6450
|
-
const parsedValue = parseFloat(rawValue);
|
|
6451
|
-
if (rawValue === "" || rawValue === ".") {
|
|
6452
|
-
if (required) {
|
|
6453
|
-
setError(name, {
|
|
6454
|
-
type: "required",
|
|
6455
|
-
message: `${string} ${i18n_default.t("must_required")}`
|
|
6456
|
-
});
|
|
6457
|
-
}
|
|
6458
|
-
onChange(null);
|
|
6459
|
-
setInputValue("");
|
|
6460
|
-
lastCommittedValueRef.current = null;
|
|
6461
|
-
} else if (!isNaN(parsedValue)) {
|
|
6462
|
-
if (parsedValue < 0) {
|
|
6463
|
-
setError(name, {
|
|
6464
|
-
type: "validate",
|
|
6465
|
-
message: i18n_default.t("invalid_number")
|
|
6466
|
-
});
|
|
6467
|
-
setInputValue("");
|
|
6468
|
-
lastCommittedValueRef.current = null;
|
|
6469
|
-
} else {
|
|
6470
|
-
if (lastCommittedValueRef.current !== parsedValue) {
|
|
6471
|
-
const parts = rawValue.split(".");
|
|
6472
|
-
let integerPart = parts[0];
|
|
6473
|
-
const decimalPart = parts[1] || "";
|
|
6474
|
-
integerPart = Number(integerPart).toLocaleString("en-US");
|
|
6475
|
-
const formattedValue = decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
|
|
6476
|
-
onChange(parsedValue);
|
|
6477
|
-
setInputValue(formattedValue);
|
|
6478
|
-
handleOnchange?.(name ?? "", parsedValue);
|
|
6479
|
-
clearErrors(name);
|
|
6480
|
-
lastCommittedValueRef.current = parsedValue;
|
|
6481
|
-
}
|
|
6482
|
-
}
|
|
6483
|
-
} else {
|
|
6484
|
-
setError(name, {
|
|
6485
|
-
type: "validate",
|
|
6486
|
-
message: i18n_default.t("invalid_number")
|
|
6487
|
-
});
|
|
6488
|
-
setInputValue("");
|
|
6489
|
-
lastCommittedValueRef.current = null;
|
|
6490
|
-
}
|
|
6491
|
-
isDirtyRef.current = false;
|
|
6492
|
-
inputRef.current?.blur();
|
|
6493
|
-
};
|
|
6494
|
-
return {
|
|
6495
|
-
handleInputMouseLeave,
|
|
6496
|
-
handleInputChange,
|
|
6497
|
-
useFormatFloatNumber,
|
|
6498
|
-
inputRef,
|
|
6499
|
-
inputValue
|
|
6500
|
-
};
|
|
6501
|
-
};
|
|
6502
|
-
var useFormatFloatNumber = (value) => {
|
|
6503
|
-
if (value === void 0 || value === null || value === "") return "";
|
|
6504
|
-
const numValue = typeof value === "string" ? parseFloat(value.replace(/,/g, "")) : value;
|
|
6505
|
-
if (isNaN(numValue)) return "";
|
|
6506
|
-
return numValue.toLocaleString("en-US", {
|
|
6507
|
-
minimumFractionDigits: numValue % 1 === 0 ? 0 : 1,
|
|
6508
|
-
maximumFractionDigits: 20
|
|
6509
|
-
});
|
|
6510
|
-
};
|
|
6511
|
-
|
|
6512
|
-
// src/widget/basic/download-file-field/controller.ts
|
|
6513
|
-
import { useId, useState as useState12 } from "react";
|
|
6514
|
-
var downloadFileController = () => {
|
|
6515
|
-
const inputId = useId();
|
|
6516
|
-
const [file, setFile] = useState12(null);
|
|
6517
|
-
const handleFileChange = (e) => {
|
|
6518
|
-
setFile(e.target.files[0]);
|
|
6519
|
-
};
|
|
6520
|
-
const handleFileDownload = () => {
|
|
6521
|
-
const url = URL.createObjectURL(file);
|
|
6522
|
-
const link = document.createElement("a");
|
|
6523
|
-
link.href = url;
|
|
6524
|
-
link.download = file.name;
|
|
6525
|
-
document.body.appendChild(link);
|
|
6526
|
-
link.click();
|
|
6527
|
-
document.body.removeChild(link);
|
|
6528
|
-
};
|
|
6529
|
-
return {
|
|
6530
|
-
inputId,
|
|
6531
|
-
file,
|
|
6532
|
-
handleFileChange,
|
|
6533
|
-
handleFileDownload
|
|
6534
|
-
};
|
|
6535
|
-
};
|
|
6536
|
-
|
|
6537
|
-
// src/widget/basic/download-binary-field/controller.ts
|
|
6538
|
-
var downLoadBinaryController = (props) => {
|
|
6539
|
-
const { value, defaultValue, formValues } = props;
|
|
6540
|
-
const handleFileDownload = async (e) => {
|
|
6541
|
-
e.stopPropagation();
|
|
6542
|
-
await downloadFile(value || defaultValue, formValues?.name);
|
|
6543
|
-
};
|
|
6544
|
-
const downloadFile = async (url, filename) => {
|
|
6545
|
-
try {
|
|
6546
|
-
const response = await fetch(url);
|
|
6547
|
-
if (response) {
|
|
6548
|
-
const blob = await response.blob();
|
|
6549
|
-
const urlBlob = window.URL.createObjectURL(blob);
|
|
6550
|
-
const link = document.createElement("a");
|
|
6551
|
-
link.href = urlBlob;
|
|
6552
|
-
link.download = filename || "downloaded-file";
|
|
6553
|
-
document.body.appendChild(link);
|
|
6554
|
-
link.click();
|
|
6555
|
-
document.body.removeChild(link);
|
|
6556
|
-
window.URL.revokeObjectURL(urlBlob);
|
|
6557
|
-
}
|
|
6558
|
-
} catch (error) {
|
|
6559
|
-
console.error("File download failed:", error);
|
|
6560
|
-
}
|
|
6561
|
-
};
|
|
6562
|
-
return {
|
|
6563
|
-
handleFileDownload
|
|
6564
|
-
};
|
|
6565
|
-
};
|
|
6566
|
-
|
|
6567
6309
|
// src/widget/basic/date-field/controller.ts
|
|
6568
|
-
var import_moment = __toESM(require_moment());
|
|
6569
6310
|
var DURATIONS = {
|
|
6570
6311
|
PAST: "past",
|
|
6571
6312
|
NOW: "now",
|
|
@@ -6677,11 +6418,11 @@ var dateFieldController = (props) => {
|
|
|
6677
6418
|
};
|
|
6678
6419
|
|
|
6679
6420
|
// src/widget/basic/copy-link-button/controller.ts
|
|
6680
|
-
import { useState as
|
|
6421
|
+
import { useState as useState11 } from "react";
|
|
6681
6422
|
import { copyTextToClipboard } from "@fctc/interface-logic/utils";
|
|
6682
6423
|
var copyLinkButtonController = (props) => {
|
|
6683
6424
|
const { value, defaultValue } = props;
|
|
6684
|
-
const [isCopied, setIsCopied] =
|
|
6425
|
+
const [isCopied, setIsCopied] = useState11(false);
|
|
6685
6426
|
const handleCopyToClipboard = async (value2) => {
|
|
6686
6427
|
await copyTextToClipboard(value2);
|
|
6687
6428
|
setIsCopied(true);
|
|
@@ -6729,16 +6470,16 @@ var colorFieldController = (props) => {
|
|
|
6729
6470
|
};
|
|
6730
6471
|
|
|
6731
6472
|
// src/widget/basic/binary-field/controller.ts
|
|
6732
|
-
import { useEffect as
|
|
6473
|
+
import { useEffect as useEffect12, useId as useId2, useRef as useRef4, useState as useState12 } from "react";
|
|
6733
6474
|
import { isBase64Image } from "@fctc/interface-logic/utils";
|
|
6734
6475
|
var binaryFieldController = (props) => {
|
|
6735
6476
|
const { name, methods, readonly = false, value } = props;
|
|
6736
6477
|
const inputId = useId2();
|
|
6737
|
-
const [selectedImage, setSelectedImage] =
|
|
6738
|
-
const [initialImage, setInitialImage] =
|
|
6739
|
-
const [isInsideTable, setIsInsideTable] =
|
|
6478
|
+
const [selectedImage, setSelectedImage] = useState12(null);
|
|
6479
|
+
const [initialImage, setInitialImage] = useState12(value || null);
|
|
6480
|
+
const [isInsideTable, setIsInsideTable] = useState12(false);
|
|
6740
6481
|
const { setValue } = methods;
|
|
6741
|
-
const binaryRef =
|
|
6482
|
+
const binaryRef = useRef4(null);
|
|
6742
6483
|
const convertUrlToBase64 = async (url) => {
|
|
6743
6484
|
try {
|
|
6744
6485
|
const response = await fetch(url);
|
|
@@ -6800,14 +6541,14 @@ var binaryFieldController = (props) => {
|
|
|
6800
6541
|
else if (base64.startsWith("UklGR")) mimeType = "image/webp";
|
|
6801
6542
|
return mimeType ? `data:${mimeType};base64,${base64}` : null;
|
|
6802
6543
|
};
|
|
6803
|
-
|
|
6544
|
+
useEffect12(() => {
|
|
6804
6545
|
return () => {
|
|
6805
6546
|
if (selectedImage) {
|
|
6806
6547
|
URL.revokeObjectURL(selectedImage);
|
|
6807
6548
|
}
|
|
6808
6549
|
};
|
|
6809
6550
|
}, [selectedImage]);
|
|
6810
|
-
|
|
6551
|
+
useEffect12(() => {
|
|
6811
6552
|
if (binaryRef.current) {
|
|
6812
6553
|
const isInsideTable2 = !!binaryRef.current.closest("table");
|
|
6813
6554
|
setIsInsideTable(isInsideTable2);
|
|
@@ -6827,8 +6568,8 @@ var binaryFieldController = (props) => {
|
|
|
6827
6568
|
};
|
|
6828
6569
|
|
|
6829
6570
|
// src/widget/advance/table/table-body/controller.ts
|
|
6830
|
-
import { useAppDispatch as
|
|
6831
|
-
import { useEffect as
|
|
6571
|
+
import { useAppDispatch as useAppDispatch5, setSelectedRowKeys } from "@fctc/interface-logic/store";
|
|
6572
|
+
import { useEffect as useEffect13, useMemo as useMemo12 } from "react";
|
|
6832
6573
|
var tableBodyController = (props) => {
|
|
6833
6574
|
const {
|
|
6834
6575
|
checkedAll,
|
|
@@ -6840,7 +6581,7 @@ var tableBodyController = (props) => {
|
|
|
6840
6581
|
selectedRowKeysRef,
|
|
6841
6582
|
onClickRow
|
|
6842
6583
|
} = props;
|
|
6843
|
-
const appDispatch =
|
|
6584
|
+
const appDispatch = useAppDispatch5();
|
|
6844
6585
|
const checked = useMemo12(() => {
|
|
6845
6586
|
if (!row?.id) return false;
|
|
6846
6587
|
if (selectedRowKeys?.includes(row.id)) {
|
|
@@ -6862,7 +6603,7 @@ var tableBodyController = (props) => {
|
|
|
6862
6603
|
const handleClickRow = (col, row2) => {
|
|
6863
6604
|
onClickRow(col, row2);
|
|
6864
6605
|
};
|
|
6865
|
-
|
|
6606
|
+
useEffect13(() => {
|
|
6866
6607
|
if (!row?.id) return;
|
|
6867
6608
|
if (isAutoSelect) {
|
|
6868
6609
|
if (checkboxRef?.current === "uncheck") {
|
|
@@ -6880,7 +6621,7 @@ var tableBodyController = (props) => {
|
|
|
6880
6621
|
}
|
|
6881
6622
|
}
|
|
6882
6623
|
}, [isAutoSelect]);
|
|
6883
|
-
|
|
6624
|
+
useEffect13(() => {
|
|
6884
6625
|
if (!checkedAll) {
|
|
6885
6626
|
checkboxRef.current = "enabled";
|
|
6886
6627
|
false;
|
|
@@ -6895,14 +6636,14 @@ var tableBodyController = (props) => {
|
|
|
6895
6636
|
|
|
6896
6637
|
// src/widget/advance/table/table-head/controller.ts
|
|
6897
6638
|
import {
|
|
6898
|
-
useAppDispatch as
|
|
6639
|
+
useAppDispatch as useAppDispatch6,
|
|
6899
6640
|
useAppSelector as useAppSelector4,
|
|
6900
6641
|
selectSearch as selectSearch3,
|
|
6901
6642
|
setSelectedRowKeys as setSelectedRowKeys2
|
|
6902
6643
|
} from "@fctc/interface-logic/store";
|
|
6903
6644
|
var tableHeadController = (props) => {
|
|
6904
6645
|
const { typeTable, rows, selectedRowKeysRef } = props;
|
|
6905
|
-
const appDispatch =
|
|
6646
|
+
const appDispatch = useAppDispatch6();
|
|
6906
6647
|
const { groupByDomain } = useAppSelector4(selectSearch3);
|
|
6907
6648
|
const handleCheckBoxAll = (event) => {
|
|
6908
6649
|
if (event?.target?.checked && typeTable === "list") {
|
|
@@ -6936,7 +6677,7 @@ var tableHeadController = (props) => {
|
|
|
6936
6677
|
};
|
|
6937
6678
|
|
|
6938
6679
|
// src/widget/advance/table/table-view/controller.ts
|
|
6939
|
-
import { useEffect as
|
|
6680
|
+
import { useEffect as useEffect14, useMemo as useMemo13, useRef as useRef5, useState as useState13 } from "react";
|
|
6940
6681
|
import {
|
|
6941
6682
|
useAppSelector as useAppSelector5,
|
|
6942
6683
|
selectSearch as selectSearch4,
|
|
@@ -6944,8 +6685,8 @@ import {
|
|
|
6944
6685
|
} from "@fctc/interface-logic/store";
|
|
6945
6686
|
import { domainHelper } from "@fctc/interface-logic/utils";
|
|
6946
6687
|
var tableController = ({ data }) => {
|
|
6947
|
-
const [rows, setRows] =
|
|
6948
|
-
const [columns, setColumns] =
|
|
6688
|
+
const [rows, setRows] = useState13(data.records || []);
|
|
6689
|
+
const [columns, setColumns] = useState13([]);
|
|
6949
6690
|
const dataModelFields = data.fields?.map((field) => {
|
|
6950
6691
|
return {
|
|
6951
6692
|
...data.dataModel?.[field?.name],
|
|
@@ -6973,7 +6714,7 @@ var tableController = ({ data }) => {
|
|
|
6973
6714
|
return item.display_name ? { ...transformedItem, item: item.display_name } : transformedItem;
|
|
6974
6715
|
});
|
|
6975
6716
|
};
|
|
6976
|
-
|
|
6717
|
+
useEffect14(() => {
|
|
6977
6718
|
setRows(transformData(data.records || null));
|
|
6978
6719
|
}, [data.records]);
|
|
6979
6720
|
const handleGetColumns = () => {
|
|
@@ -6994,7 +6735,7 @@ var tableController = ({ data }) => {
|
|
|
6994
6735
|
}
|
|
6995
6736
|
return cols;
|
|
6996
6737
|
};
|
|
6997
|
-
|
|
6738
|
+
useEffect14(() => {
|
|
6998
6739
|
const columns2 = handleGetColumns();
|
|
6999
6740
|
setColumns(columns2);
|
|
7000
6741
|
}, [data.records]);
|
|
@@ -7019,7 +6760,7 @@ var tableController = ({ data }) => {
|
|
|
7019
6760
|
};
|
|
7020
6761
|
|
|
7021
6762
|
// src/widget/advance/table/table-group/controller.ts
|
|
7022
|
-
import { useEffect as
|
|
6763
|
+
import { useEffect as useEffect15, useMemo as useMemo14, useState as useState14 } from "react";
|
|
7023
6764
|
import {
|
|
7024
6765
|
useOdooDataTransform,
|
|
7025
6766
|
useGetListData as useGetListData2
|
|
@@ -7028,7 +6769,7 @@ import {
|
|
|
7028
6769
|
useAppSelector as useAppSelector6,
|
|
7029
6770
|
selectSearch as selectSearch5,
|
|
7030
6771
|
selectList as selectList4,
|
|
7031
|
-
useAppDispatch as
|
|
6772
|
+
useAppDispatch as useAppDispatch7,
|
|
7032
6773
|
setSelectedRowKeys as setSelectedRowKeys3
|
|
7033
6774
|
} from "@fctc/interface-logic/store";
|
|
7034
6775
|
|
|
@@ -7058,14 +6799,14 @@ var tableGroupController = (props) => {
|
|
|
7058
6799
|
setIsAutoSelect,
|
|
7059
6800
|
selectedRowKeysRef
|
|
7060
6801
|
} = props;
|
|
7061
|
-
const [pageGroup, setPageGroup] =
|
|
6802
|
+
const [pageGroup, setPageGroup] = useState14(0);
|
|
7062
6803
|
const { groupByDomain, selectedTags } = useAppSelector6(selectSearch5);
|
|
7063
6804
|
const { selectedRowKeys } = useAppSelector6(selectList4);
|
|
7064
|
-
const appDispatch =
|
|
6805
|
+
const appDispatch = useAppDispatch7();
|
|
7065
6806
|
const { toDataJS } = useOdooDataTransform();
|
|
7066
6807
|
const initVal = toDataJS(row, viewData, model);
|
|
7067
|
-
const [isShowGroup, setIsShowGroup] =
|
|
7068
|
-
const [colEmptyGroup, setColEmptyGroup] =
|
|
6808
|
+
const [isShowGroup, setIsShowGroup] = useState14(false);
|
|
6809
|
+
const [colEmptyGroup, setColEmptyGroup] = useState14({
|
|
7069
6810
|
fromStart: 1,
|
|
7070
6811
|
fromEnd: 1
|
|
7071
6812
|
});
|
|
@@ -7124,7 +6865,7 @@ var tableGroupController = (props) => {
|
|
|
7124
6865
|
}
|
|
7125
6866
|
});
|
|
7126
6867
|
const leftPadding = level > 1 ? level * 8 + "px" : "0px";
|
|
7127
|
-
|
|
6868
|
+
useEffect15(() => {
|
|
7128
6869
|
if (isShowGroup && selectedTags?.length > 0) {
|
|
7129
6870
|
setIsShowGroup(false);
|
|
7130
6871
|
}
|
|
@@ -7158,7 +6899,7 @@ var tableGroupController = (props) => {
|
|
|
7158
6899
|
}
|
|
7159
6900
|
toggleShowGroup();
|
|
7160
6901
|
};
|
|
7161
|
-
|
|
6902
|
+
useEffect15(() => {
|
|
7162
6903
|
if (!isQueryFetched || !rowsGroup || !checkedAll || allIdsNull || typeTableGroup === "group") {
|
|
7163
6904
|
return;
|
|
7164
6905
|
}
|
|
@@ -7202,10 +6943,10 @@ var import_moment2 = __toESM(require_moment());
|
|
|
7202
6943
|
import { SearchType } from "@fctc/interface-logic/constants";
|
|
7203
6944
|
import {
|
|
7204
6945
|
domainHelper as domainHelper2,
|
|
7205
|
-
evalJSONDomain as
|
|
6946
|
+
evalJSONDomain as evalJSONDomain7,
|
|
7206
6947
|
validateAndParseDate
|
|
7207
6948
|
} from "@fctc/interface-logic/utils";
|
|
7208
|
-
import { useCallback as useCallback3, useEffect as
|
|
6949
|
+
import { useCallback as useCallback3, useEffect as useEffect16, useState as useState15 } from "react";
|
|
7209
6950
|
var searchController = ({
|
|
7210
6951
|
viewData,
|
|
7211
6952
|
actionData,
|
|
@@ -7214,12 +6955,12 @@ var searchController = ({
|
|
|
7214
6955
|
setSearchMap,
|
|
7215
6956
|
searchMap
|
|
7216
6957
|
}) => {
|
|
7217
|
-
const [filterBy, setFilterBy] =
|
|
7218
|
-
const [searchBy, setSearchBy] =
|
|
7219
|
-
const [groupBy, setGroupBy] =
|
|
7220
|
-
const [selectedTags, setSelectedTags] =
|
|
7221
|
-
const [searchString, setSearchString] =
|
|
7222
|
-
const domainAction = actionData?.domain ? Array.isArray(actionData?.domain) ? [...actionData?.domain] :
|
|
6958
|
+
const [filterBy, setFilterBy] = useState15(null);
|
|
6959
|
+
const [searchBy, setSearchBy] = useState15(null);
|
|
6960
|
+
const [groupBy, setGroupBy] = useState15(null);
|
|
6961
|
+
const [selectedTags, setSelectedTags] = useState15(null);
|
|
6962
|
+
const [searchString, setSearchString] = useState15("");
|
|
6963
|
+
const domainAction = actionData?.domain ? Array.isArray(actionData?.domain) ? [...actionData?.domain] : evalJSONDomain7(actionData?.domain, contextSearch) : [];
|
|
7223
6964
|
const aid = actionData?.id;
|
|
7224
6965
|
const model = actionData?.res_model;
|
|
7225
6966
|
const clearSearch = () => {
|
|
@@ -7265,7 +7006,7 @@ var searchController = ({
|
|
|
7265
7006
|
}
|
|
7266
7007
|
}
|
|
7267
7008
|
};
|
|
7268
|
-
|
|
7009
|
+
useEffect16(() => {
|
|
7269
7010
|
clearSearch();
|
|
7270
7011
|
fetchData();
|
|
7271
7012
|
}, [aid, model, viewData]);
|
|
@@ -7411,7 +7152,7 @@ var searchController = ({
|
|
|
7411
7152
|
},
|
|
7412
7153
|
[searchMap]
|
|
7413
7154
|
);
|
|
7414
|
-
|
|
7155
|
+
useEffect16(() => {
|
|
7415
7156
|
setSelectedTags(null);
|
|
7416
7157
|
setTagSearch(searchMap);
|
|
7417
7158
|
}, [searchMap]);
|
|
@@ -7475,8 +7216,6 @@ export {
|
|
|
7475
7216
|
downLoadBinaryController,
|
|
7476
7217
|
downloadFileController,
|
|
7477
7218
|
durationController,
|
|
7478
|
-
floatController,
|
|
7479
|
-
floatTimeFiledController,
|
|
7480
7219
|
many2manyFieldController,
|
|
7481
7220
|
many2manyTagsController,
|
|
7482
7221
|
many2oneButtonController,
|