@univerjs/sheets-formula 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -5,12 +5,785 @@
5
5
 
6
6
  ## Introduction
7
7
 
8
- > Formula plugin for Univer Sheets.
8
+ `@univerjs/sheets-formula` provides the ability to edit formulas in spreadsheets, including features such as auto-completion, formula suggestions, drop-down filling for formulas, and copy-paste functionality.
9
+
10
+ :::note
11
+ Formula calculation is one of the core functionalities of spreadsheets, and formula calculation scheduling is done in `@univerjs/sheets`.
12
+ :::
9
13
 
10
14
  ## Usage
11
15
 
12
16
  ### Installation
13
17
 
14
18
  ```shell
15
- npm i @univerjs/sheets-formula
19
+ # Using npm
20
+ npm install @univerjs/sheets-formula
21
+
22
+ # Using pnpm
23
+ pnpm add @univerjs/sheets-formula
24
+ ```
25
+
26
+ ### Add formula preconditions
27
+
28
+ If the officially provided formula does not meet your needs, you can expand the formula yourself. Depending on different needs, we provide multiple ways to support registering one or more custom formulas.
29
+
30
+ You need to first prepare the international copywriting and algorithms required for formula prompts, and then refer to our [Contribution Guide](https://github.com/dream-num/univer/blob/dev/CONTRIBUTING.md) to run the Univer project , and then start adding formulas.
31
+
32
+ ### How to add formulas using Uniscript
33
+
34
+ Using Uniscript scripts, you can quickly and easily register custom formulas in the current Univer instance.
35
+
36
+ As shown in the following case, use `registerFunction` to register the algorithm, name, and description required by a `CUSTOMSUM` formula into the formula plug-in at one time. After execution, the formula can be used. Enter `=CUSTOMSUM` in any blank cell to see the prompt.
37
+
38
+ ```js
39
+ univerAPI.registerFunction({
40
+ calculate: [
41
+ [function (...variants) {
42
+ let sum = 0;
43
+
44
+ for(const variant of variants){
45
+ sum += Number(variant) || 0;
46
+ }
47
+
48
+ return sum;
49
+ }, 'CUSTOMSUM', 'Adds its arguments'],
50
+ // ... more formulas
51
+ ]
52
+ })
53
+ ```
54
+
55
+ Use the `unregisterFunction` method to quickly unregister custom formulas
56
+
57
+ ```ts
58
+ univerAPI.unregisterFunction({
59
+ functionNames: ['CUSTOMSUM']
60
+ })
61
+ ```
62
+
63
+ If you want to provide more complete international content and description, you can also configure the `locales` and `description` fields. As follows.
64
+
65
+ ```js
66
+ const FUNCTION_NAMES_USER = {
67
+ CUSTOMSUM: 'CUSTOMSUM'
68
+ }
69
+ univerAPI.registerFunction({
70
+ locales:{
71
+ 'zhCN': {
72
+ formulaCustom: {
73
+ CUSTOMSUM: {
74
+ description: '将单个值、单元格引用或是区域相加,或者将三者的组合相加。',
75
+ abstract: '求参数的和',
76
+ links: [
77
+ {
78
+ title: '教学',
79
+ url: 'https://support.microsoft.com/zh-cn/office/sum-%E5%87%BD%E6%95%B0-043e1c7d-7726-4e80-8f32-07b23e057f89',
80
+ },
81
+ ],
82
+ functionParameter: {
83
+ number1: {
84
+ name: '数值1',
85
+ detail: '要相加的第一个数字。 该数字可以是 4 之类的数字,B6 之类的单元格引用或 B2:B8 之类的单元格范围。',
86
+ },
87
+ number2: {
88
+ name: '数值2',
89
+ detail: '这是要相加的第二个数字。 可以按照这种方式最多指定 255 个数字。',
90
+ },
91
+ },
92
+ },
93
+ // ... more formulas
94
+ },
95
+ },
96
+ 'enUS':{
97
+ formulaCustom:{
98
+ CUSTOMSUM: {
99
+ description: `You can add individual values, cell references or ranges or a mix of all three.`,
100
+ abstract: `Adds its arguments`,
101
+ links: [
102
+ {
103
+ title: 'Instruction',
104
+ url: 'https://support.microsoft.com/en-us/office/sum-function-043e1c7d-7726-4e80-8f32-07b23e057f89',
105
+ },
106
+ ],
107
+ functionParameter: {
108
+ number1: {
109
+ name: 'number1',
110
+ detail: 'The first number you want to add. The number can be like 4, a cell reference like B6, or a cell range like B2:B8.',
111
+ },
112
+ number2: {
113
+ name: 'number2',
114
+ detail: 'This is the second number you want to add. You can specify up to 255 numbers in this way.',
115
+ },
116
+ },
117
+ },
118
+ }
119
+ }
120
+ },
121
+ description:[
122
+ {
123
+ functionName: FUNCTION_NAMES_USER.CUSTOMSUM,
124
+ aliasFunctionName: 'formulaCustom.CUSTOMSUM.aliasFunctionName',
125
+ functionType: 15,
126
+ description: 'formulaCustom.CUSTOMSUM.description',
127
+ abstract: 'formulaCustom.CUSTOMSUM.abstract',
128
+ functionParameter: [
129
+ {
130
+ name: 'formulaCustom.CUSTOMSUM.functionParameter.number1.name',
131
+ detail: 'formulaCustom.CUSTOMSUM.functionParameter.number1.detail',
132
+ example: 'A1:A20',
133
+ require: 1,
134
+ repeat: 0,
135
+ },
136
+ {
137
+ name: 'formulaCustom.CUSTOMSUM.functionParameter.number2.name',
138
+ detail: 'formulaCustom.CUSTOMSUM.functionParameter.number2.detail',
139
+ example: 'B2:B10',
140
+ require: 0,
141
+ repeat: 1,
142
+ },
143
+ ],
144
+ },
145
+ // ... more formulas
146
+ ],
147
+ calculate: [
148
+ [function (...variants) {
149
+ let sum = 0;
150
+
151
+ for(const variant of variants){
152
+ sum += Number(variant) || 0;
153
+ }
154
+
155
+ return sum;
156
+ }, FUNCTION_NAMES_USER.CUSTOMSUM],
157
+ // ... more formulas
158
+ ]
159
+ })
160
+ ```
161
+
162
+ Note
163
+
164
+ - Multiple languages can be set under `locales`. For naming rules, please refer to [LocaleType](/api/core/enums/LocaleType.html). Translations for multiple formulas can be added under `functionList`. For detailed field descriptions, please refer to the [How to add formulas in UniverFormulaEnginePlugin](./#how-to-add-formulas-in-univerformulaengineplugin) section.
165
+ - `description` sets the description of the custom formula.
166
+ - `calculate` writes the specific algorithm and name mapping of the calculation formula. The input parameter is the content entered by the user when using the formula, which may be a number, a string, a Boolean value, or a range, and the same format is returned.
167
+
168
+ Likewise, if using the `unregisterFunction` method, it is recommended that you remove the internationalization files as well. The example below removes the Chinese and English `formulaCustom` nodes.
169
+
170
+ ```ts
171
+ univerAPI.unregisterFunction({
172
+ localeKeys: {
173
+ 'zhCN': ['formulaCustom'],
174
+ 'enUS': ['formulaCustom'],
175
+ },
176
+ functionNames: ['CUSTOMSUM']
177
+ })
178
+ ```
179
+
180
+ Uniscript uses `@univerjs/facade` under the hood. You can also use Uniscript-like APIs directly in your project. Please refer to [Registering Function](/en-us/guides/facade/register-function).
181
+
182
+ ### How to add formulas when initializing Univer
183
+
184
+ Follow the steps below to implement a custom formula `CUSTOMSUM`.
185
+
186
+ You can create a new `custom-function.ts` file to specifically place custom formula-related modules, or write it directly before `univer` is initialized.
187
+
188
+ 1. Define formula name
189
+
190
+ First, give the formula a name. We require that it cannot be repeated with the name of the existing formula. The existing formula is mainly from [Office Excel](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb) refer to.
191
+
192
+ We collect multiple custom formulas in an enumeration.
193
+
194
+ ```ts
195
+ /**
196
+ * function name
197
+ */
198
+ export enum FUNCTION_NAMES_USER {
199
+ CUSTOMSUM = "CUSTOMSUM",
200
+ }
201
+ ```
202
+
203
+ 2. Define internationalization
204
+
205
+ Define the international content you need. For detailed field descriptions, please refer to the [How to add formulas in UniverFormulaEnginePlugin](./#how-to-add-formulas-in-univerformulaengineplugin) section. Similarly, multiple formulas are distinguished by using the formula name as the `key` value.
206
+
207
+ ```ts
208
+ /**
209
+ *i18n
210
+ */
211
+ export const functionEnUS = {
212
+ formulaCustom: {
213
+ CUSTOMSUM: {
214
+ description: `You can add individual values, cell references or ranges or a mix of all three.`,
215
+ abstract: `Adds its arguments`,
216
+ links: [
217
+ {
218
+ title: 'Instruction',
219
+ url: 'https://support.microsoft.com/en-us/office/sum-function-043e1c7d-7726-4e80-8f32-07b23e057f89',
220
+ },
221
+ ],
222
+ functionParameter: {
223
+ number1: {
224
+ name: 'number1',
225
+ detail: 'The first number you want to add. The number can be like 4, a cell reference like B6, or a cell range like B2:B8.',
226
+ },
227
+ number2: {
228
+ name: 'number2',
229
+ detail: 'This is the second number you want to add. You can specify up to 255 numbers in this way.',
230
+ },
231
+ },
232
+ },
233
+ },
234
+ };
235
+
236
+ export const functionZhCN = {
237
+ formulaCustom: {
238
+ CUSTOMSUM: {
239
+ description: '将单个值、单元格引用或是区域相加,或者将三者的组合相加。',
240
+ abstract: '求参数的和',
241
+ links: [
242
+ {
243
+ title: '教学',
244
+ url: 'https://support.microsoft.com/zh-cn/office/sum-%E5%87%BD%E6%95%B0-043e1c7d-7726-4e80-8f32-07b23e057f89',
245
+ },
246
+ ],
247
+ functionParameter: {
248
+ number1: {
249
+ name: '数值1',
250
+ detail: '要相加的第一个数字。 该数字可以是 4 之类的数字,B6 之类的单元格引用或 B2:B8 之类的单元格范围。',
251
+ },
252
+ number2: {
253
+ name: '数值2',
254
+ detail: '这是要相加的第二个数字。 可以按照这种方式最多指定 255 个数字。',
255
+ },
256
+ },
257
+ },
258
+ },
259
+ };
260
+ ```
261
+
262
+ 3. Registration internationalization
263
+
264
+ Expand the internationalization content you defined in the original internationalization object.
265
+
266
+ ```ts
267
+ export const locales = {
268
+ [LocaleType.EN_US]: {
269
+ ...UniverSheetsEnUS,
270
+ ...UniverDocsUIEnUS,
271
+ ...UniverSheetsUIEnUS,
272
+ ...UniverUiEnUS,
273
+ ...UniverDesignEnUS,
274
+ ...functionEnUS,
275
+ },
276
+ [LocaleType.ZH_CN]: {
277
+ ...functionZhCN,
278
+ },
279
+ };
280
+ ```
281
+
282
+ 4. Definition description
283
+
284
+ The description of the formula mainly configures internationalized fields, which are used for formula search prompts, details panels, etc.
285
+
286
+ ```ts
287
+ import type { IFunctionInfo } from '@univerjs/engine-formula';
288
+ import { FunctionType } from '@univerjs/engine-formula';
289
+
290
+ /**
291
+ * description
292
+ */
293
+ export const FUNCTION_LIST_USER: IFunctionInfo[] = [
294
+ {
295
+ functionName: FUNCTION_NAMES_USER.CUSTOMSUM,
296
+ aliasFunctionName: 'formulaCustom.CUSTOMSUM.aliasFunctionName',
297
+ functionType: FunctionType.User,
298
+ description: 'formulaCustom.CUSTOMSUM.description',
299
+ abstract: 'formulaCustom.CUSTOMSUM.abstract',
300
+ functionParameter: [
301
+ {
302
+ name: 'formulaCustom.CUSTOMSUM.functionParameter.number1.name',
303
+ detail: 'formulaCustom.CUSTOMSUM.functionParameter.number1.detail',
304
+ example: 'A1:A20',
305
+ require: 1,
306
+ repeat: 0,
307
+ },
308
+ {
309
+ name: 'formulaCustom.CUSTOMSUM.functionParameter.number2.name',
310
+ detail: 'formulaCustom.CUSTOMSUM.functionParameter.number2.detail',
311
+ example: 'B2:B10',
312
+ require: 0,
313
+ repeat: 1,
314
+ },
315
+ ],
316
+ },
317
+ ];
318
+ ```
319
+
320
+ 5. Registration description
321
+
322
+ Pass in the description object you defined when registering the formula plug-in.
323
+
324
+ ```ts
325
+ // universal
326
+ univer.registerPlugin(UniverSheetsFormulaPlugin, {
327
+ description: FUNCTION_LIST_USER,
328
+ });
329
+ ```
330
+
331
+ 6. Define formula algorithm
332
+
333
+ Write specific formula calculation logic, map algorithms and formula names.
334
+
335
+ ```ts
336
+ import type { ArrayValueObject, BaseValueObject, IFunctionInfo } from '@univerjs/engine-formula';
337
+ import { BaseFunction, FunctionType, NumberValueObject } from '@univerjs/engine-formula';
338
+
339
+ /**
340
+ * Function algorithm
341
+ */
342
+ export class Customsum extends BaseFunction {
343
+ override calculate(...variants: BaseValueObject[]) {
344
+ let accumulatorAll: BaseValueObject = new NumberValueObject(0);
345
+ for (let i = 0; i < variants.length; i++) {
346
+ let variant = variants[i];
347
+
348
+ if (variant.isError()) {
349
+ return variant;
350
+ }
351
+
352
+ if (accumulatorAll.isError()) {
353
+ return accumulatorAll;
354
+ }
355
+
356
+ if (variant.isArray()) {
357
+ variant = (variant as ArrayValueObject).sum();
358
+ }
359
+
360
+ accumulatorAll = accumulatorAll.plus(variant as BaseValueObject);
361
+ }
362
+
363
+ return accumulatorAll;
364
+ }
365
+ }
366
+
367
+ // Mapping of algorithms and names
368
+ export const functionUser = [[Customsum, FUNCTION_NAMES_USER.CUSTOMSUM]];
369
+ ```
370
+
371
+ 7. Registration formula algorithm
372
+
373
+ Pass in the formula algorithm object you defined in `UniverFormulaEnginePlugin`.
374
+
375
+ ```ts
376
+ univer.registerPlugin(UniverFormulaEnginePlugin, {
377
+ function: functionUser,
378
+ });
379
+ ```
380
+
381
+ Please note: If `UniverFormulaEnginePlugin` is instantiated in `worker`, you need to register the formula algorithm in `UniverFormulaEnginePlugin` in `worker`, otherwise the custom formula cannot be executed.
382
+
383
+ 8. Test
384
+
385
+ At this point, the development of the custom formula is completed, and now it is time to test it. Enter `=CUSTOMSUM` in any blank cell and expect to get a formula prompt. Here is a [Custom Formula Demo](/playground?title=Custom%20Function) for reference.
386
+
387
+ ### How to add formulas in third-party plug-ins
388
+
389
+ If you are developing a Univer plug-in, you can add custom formulas directly to the plug-in to facilitate code management in a plug-in repository.
390
+
391
+ Our internal `UniverFormulaEnginePlugin` plug-in provides a `function.service` specifically for registering formula descriptions and algorithms.
392
+
393
+ First refer to [Custom Plugin](/en-us/guides/extend/write-a-plugin/) to create a new plug-in, and then you can start adding custom formulas.
394
+
395
+ 1. Create a new `custom-function.ts` file in the `common` file and write all the basic modules required for the formula.
396
+
397
+ ```ts
398
+ import type { ArrayValueObject, BaseValueObject, IFunctionInfo } from '@univerjs/engine-formula';
399
+ import { BaseFunction, FunctionType, NumberValueObject } from '@univerjs/engine-formula';
400
+
401
+ /**
402
+ * function name
403
+ */
404
+ export enum FUNCTION_NAMES_USER {
405
+ CUSTOMSUM = 'CUSTOMSUM',
406
+ }
407
+
408
+ /**
409
+ *i18n
410
+ */
411
+ export const functionEnUS = {
412
+ formulaCustom: {
413
+ CUSTOMSUM: {
414
+ description: `You can add individual values, cell references or ranges or a mix of all three.`,
415
+ abstract: `Adds its arguments`,
416
+ links: [
417
+ {
418
+ title: 'Instruction',
419
+ url: 'https://support.microsoft.com/en-us/office/sum-function-043e1c7d-7726-4e80-8f32-07b23e057f89',
420
+ },
421
+ ],
422
+ functionParameter: {
423
+ number1: {
424
+ name: 'number1',
425
+ detail: 'The first number you want to add. The number can be like 4, a cell reference like B6, or a cell range like B2:B8.',
426
+ },
427
+ number2: {
428
+ name: 'number2',
429
+ detail: 'This is the second number you want to add. You can specify up to 255 numbers in this way.',
430
+ },
431
+ },
432
+ },
433
+ },
434
+ };
435
+
436
+ export const functionZhCN = {
437
+ formulaCustom: {
438
+ CUSTOMSUM: {
439
+ description: '将单个值、单元格引用或是区域相加,或者将三者的组合相加。',
440
+ abstract: '求参数的和',
441
+ links: [
442
+ {
443
+ title: '教学',
444
+ url: 'https://support.microsoft.com/zh-cn/office/sum-%E5%87%BD%E6%95%B0-043e1c7d-7726-4e80-8f32-07b23e057f89',
445
+ },
446
+ ],
447
+ functionParameter: {
448
+ number1: {
449
+ name: '数值1',
450
+ detail: '要相加的第一个数字。 该数字可以是 4 之类的数字,B6 之类的单元格引用或 B2:B8 之类的单元格范围。',
451
+ },
452
+ number2: {
453
+ name: '数值2',
454
+ detail: '这是要相加的第二个数字。 可以按照这种方式最多指定 255 个数字。',
455
+ },
456
+ },
457
+ },
458
+ },
459
+ };
460
+
461
+ /**
462
+ * description
463
+ */
464
+ export const FUNCTION_LIST_USER: IFunctionInfo[] = [
465
+ {
466
+ functionName: FUNCTION_NAMES_USER.CUSTOMSUM,
467
+ aliasFunctionName: 'formulaCustom.CUSTOMSUM.aliasFunctionName',
468
+ functionType: FunctionType.User,
469
+ description: 'formulaCustom.CUSTOMSUM.description',
470
+ abstract: 'formulaCustom.CUSTOMSUM.abstract',
471
+ functionParameter: [
472
+ {
473
+ name: 'formulaCustom.CUSTOMSUM.functionParameter.number1.name',
474
+ detail: 'formulaCustom.CUSTOMSUM.functionParameter.number1.detail',
475
+ example: 'A1:A20',
476
+ require: 1,
477
+ repeat: 0,
478
+ },
479
+ {
480
+ name: 'formulaCustom.CUSTOMSUM.functionParameter.number2.name',
481
+ detail: 'formulaCustom.CUSTOMSUM.functionParameter.number2.detail',
482
+ example: 'B2:B10',
483
+ require: 0,
484
+ repeat: 1,
485
+ },
486
+ ],
487
+ },
488
+ ];
489
+
490
+ /**
491
+ * Function algorithm
492
+ */
493
+ export class Customsum extends BaseFunction {
494
+ override calculate(...variants: BaseValueObject[]) {
495
+ let accumulatorAll: BaseValueObject = new NumberValueObject(0);
496
+ for (let i = 0; i < variants.length; i++) {
497
+ let variant = variants[i];
498
+
499
+ if (variant.isError()) {
500
+ return variant;
501
+ }
502
+
503
+ if (accumulatorAll.isError()) {
504
+ return accumulatorAll;
505
+ }
506
+
507
+ if (variant.isArray()) {
508
+ variant = (variant as ArrayValueObject).sum();
509
+ }
510
+
511
+ accumulatorAll = accumulatorAll.plus(variant as BaseValueObject);
512
+ }
513
+
514
+ return accumulatorAll;
515
+ }
516
+ }
517
+
518
+ export const functionUser = [[Customsum, FUNCTION_NAMES_USER.CUSTOMSUM]];
519
+ ```
520
+
521
+ 2. Create a new `custom-description.controller.ts` under the `controllers` folder to register formula internationalization content and description.
522
+
523
+ ```ts
524
+ import { Disposable, LifecycleStages, LocaleService, OnLifecycle } from '@univerjs/core';
525
+ import { Inject } from '@wendellhu/redi';
526
+
527
+ import { FUNCTION_LIST_USER, functionEnUS, functionZhCN } from '../common/custom-function';
528
+ import { IDescriptionService } from '../services/description.service';
529
+
530
+ @OnLifecycle(LifecycleStages.Ready, CustomDescriptionController)
531
+ export class CustomDescriptionController extends Disposable {
532
+ constructor(
533
+ @IDescriptionService private readonly _descriptionService: IDescriptionService,
534
+ @Inject(LocaleService) private readonly _localeService: LocaleService
535
+ ) {
536
+ super();
537
+
538
+ this._initialize();
539
+ }
540
+
541
+ private _initialize(): void {
542
+ this._registerLocales();
543
+ this._registerCustomDescriptions();
544
+ }
545
+
546
+ private _registerLocales() {
547
+ this._localeService.load({
548
+ zhCN: functionZhCN,
549
+ enUS: functionEnUS,
550
+ });
551
+ }
552
+
553
+ private _registerCustomDescriptions() {
554
+ this._descriptionService.registerDescription(FUNCTION_LIST_USER);
555
+ }
556
+ }
557
+ ```
558
+
559
+ 3. Create a new `custom-function.controller.ts` under the `controllers` folder to register formula algorithms.
560
+
561
+ ```ts
562
+ import { Disposable, LifecycleStages, OnLifecycle } from '@univerjs/core';
563
+ import type { BaseFunction, IFunctionNames } from '@univerjs/engine-formula';
564
+ import { IFunctionService } from '@univerjs/engine-formula';
565
+ import { type Ctor } from '@wendellhu/redi';
566
+
567
+ import { functionUser } from '../common/custom-function';
568
+
569
+ @OnLifecycle(LifecycleStages.Ready, CustomFunctionController)
570
+ export class CustomFunctionController extends Disposable {
571
+ constructor(@IFunctionService private readonly _functionService: IFunctionService) {
572
+ super();
573
+
574
+ this._initialize();
575
+ }
576
+
577
+ private _initialize(): void {
578
+ this._registerCustomFunctions();
579
+ }
580
+
581
+ private _registerCustomFunctions() {
582
+ const functions: BaseFunction[] = [...functionUser].map((registerObject) => {
583
+ const Func = registerObject[0] as Ctor<BaseFunction>;
584
+ const name = registerObject[1] as IFunctionNames;
585
+
586
+ return new Func(name);
587
+ });
588
+
589
+ this._functionService.registerExecutors(...functions);
590
+ }
591
+ }
592
+ ```
593
+
594
+ 4. In the plug-in entry file `plugin.ts`, register `custom-description.controller.ts` and `custom-function.controller.ts` into the DI system.
595
+
596
+ ```ts
597
+ initialize(): void {
598
+ // ... other logic
599
+
600
+ const dependencies: Dependency[] = [
601
+ // ... other modules
602
+ [CustomFunctionController],
603
+ [CustomDescriptionController],
604
+ ];
605
+
606
+ dependencies.forEach((dependency) => this._injector.add(dependency));
607
+ }
608
+ ```
609
+
610
+ Start Univer and enter `=CUSTOMSUM` in any blank cell to test this newly added formula.
611
+
612
+ Please note: If `UniverFormulaEnginePlugin` is instantiated in `worker`, you need to register the formula algorithm in `worker`, otherwise the custom formula cannot be executed.
613
+ In addition to registering through the `UniverFormulaEnginePlugin` configuration, the formula algorithm module can also be separately packaged as a plug-in for registration.
614
+
615
+ First of all, there is no need to register `CustomFunctionController` in `plugin.ts`. Create a new `custom-function-plugin.ts` in the same directory, specifically for registering `CustomFunctionController`.
616
+
617
+ ```ts
618
+ import { Plugin, PluginType } from '@univerjs/core';
619
+ import type { Dependency } from '@wendellhu/redi';
620
+ import { Inject, Injector } from '@wendellhu/redi';
621
+
622
+ import { FORMULA_UI_PLUGIN_NAME } from './common/plugin-name';
623
+ import { CustomFunctionController } from './controllers/custom-function.controller';
624
+
625
+ export class UniverSheetsCustomFunctionPlugin extends Plugin {
626
+ static override type = PluginType.Sheet;
627
+
628
+ constructor(@Inject(Injector) override readonly _injector: Injector) {
629
+ super(FORMULA_UI_PLUGIN_NAME);
630
+ }
631
+
632
+ initialize(): void {
633
+ const dependencies: Dependency[] = [[CustomFunctionController]];
634
+
635
+ dependencies.forEach((dependency) => this._injector.add(dependency));
636
+ }
637
+
638
+ override onReady(): void {
639
+ this.initialize();
640
+ }
641
+ }
16
642
  ```
643
+
644
+ Then export it in `index.ts`
645
+
646
+ ```ts
647
+ export { UniverSheetsFormulaPlugin } from './formula-ui-plugin';
648
+ ```
649
+
650
+ Finally, your `worker` entry initializes the plugin.
651
+
652
+ ```ts
653
+ import { UniverSheetsCustomFunctionPlugin } from '@univerjs/sheets-formula';
654
+
655
+ // ...initialize other plugins
656
+ univer.registerPlugin(UniverSheetsCustomFunctionPlugin);
657
+ ```
658
+
659
+ This way you can register the formula in `worker`.
660
+
661
+ ### How to add formulas in UniverFormulaEnginePlugin
662
+
663
+ #### Reference Documentation
664
+
665
+ [Office Excel functions (by category)](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb)
666
+
667
+ #### Categories
668
+
669
+ Detailed API reference [FunctionType](/api/engine-formula/enums/FunctionType.html)
670
+
671
+ - Financial
672
+ - Date
673
+ - Math
674
+ - Statistical
675
+ - Lookup
676
+ - Database
677
+ - Text
678
+ - Logical
679
+ - Information
680
+ - Engineering
681
+ - Cube
682
+ - Compatibility
683
+ - Web
684
+ - Array
685
+ - Univer
686
+ - User
687
+
688
+ #### Requirements
689
+
690
+ To implement a formula, you need to add formula description, internationalization, and formula algorithm. Take the `SUMIF` function as an example for reference.
691
+
692
+ 1. Add Function Name
693
+
694
+ Location: [packages/engine-formula/src/functions/math/function-names.ts](https://github.com/dream-num/univer/blob/dev/packages/engine-formula/src/functions/math/function-names.ts).
695
+
696
+ Each category has a folder containing a `function-names` file to manage all function names in that category. Add the function name, which will be used in the `sheets-formula` plugin.
697
+
698
+ Note that a function in Excel may belong to multiple categories. For example, `FLOOR` appears in Compatibility and Math Functions, and we classify it under the Math category. Other functions are treated similarly, based on the exact classification.
699
+
700
+ > Most Excel functions have already written function names. New functions can be added at the end
701
+
702
+ 2. Internationalization Files
703
+
704
+ Location: [packages/sheets-formula/src/locale/function-list/math/en-US.ts](https://github.com/dream-num/univer/blob/dev/packages/sheets-formula/src/locale/function-list/math/en-US.ts).
705
+
706
+ Internationalization is organized by category, with a file for each category. Refer to the Office function category page for a brief overview.
707
+ ![office excel](./assets/img/office-excel.png)
708
+
709
+ Refer to the Office function details page for function descriptions and parameter descriptions.
710
+ ![sumif](./assets/img/sumif.png)
711
+
712
+ Most function names already have basic description, abstract, links, and parameter structures. It is recommended to modify them based on this foundation. If a function is not present, add it to the end.
713
+
714
+ Requirements:
715
+
716
+ - Use the English names of function parameters as the `key` for translation, e.g., `SUMIF`. Generally, do not modify unless there is an error.
717
+ - Extract the `description` from the content, as some Excel descriptions are lengthy and need simplification.
718
+ - `abstract` and `links` generally do not need modification.
719
+ - `aliasFunctionName` is optional; most formulas do not need to be filled (or can be set for aliases in specific countries). Currently, there is no documentation for formula aliases. Currently I have found a function translation plug-in that may provide similar functions [Excel Functions Translator](https://support.microsoft.com/en-us/office/excel-functions-translator-f262d0c0-991c-485b-89b6-32cc8d326889)
720
+ - `functionParameter` needs a name for each parameter. We recommend varying names based on the parameter's meaning, e.g., use `number` for a numeric parameter (if there is only one) or `number1`, `number2` for multiple numeric parameters. Use `range` for a range, `criteria` for conditions, and `sum_range` for the sum range (separated by `_` for multiple words).
721
+ - Some Chinese translations in the Office function documentation are machine-translated and may be unclear. Modify as needed. For example, `单元格参考` (Cell Reference) should be translated as `单元格引用`. Numeric type parameters are uniformly translated as: `数值`.
722
+ - Do not end `abstract` with a period (used in the search list when users input cells), but end `description` and `detail` with a period (used in descriptions).
723
+ - Capitalize the first letter of English sentences.
724
+ - Ensure that all existing internationalization files are filled. Currently, there are only Chinese, English, and Japanese translations (languages can be switched at the bottom of the Excel introduction page).
725
+
726
+ 3. Formula Descriptions
727
+
728
+ `SUMIF` belongs to the `math` category, and the description is in [packages/sheets-formula/src/services/function-list/math.ts](https://github.com/dream-num/univer/blob/dev/packages/sheets-formula/src/services/function-list/math.ts), which manages all functions in the `math` category.
729
+
730
+ Most function names already have basic description structure. It is recommended to modify them based on this foundation. If a function is not present, add it to the end.
731
+
732
+ Requirements:
733
+
734
+ - Add the formula to the `FUNCTION_LIST_MATH` array. It is recommended to keep the order consistent with the internationalization file for easy management and retrieval.
735
+ - Reference the previously defined `FUNCTION_NAMES_MATH` enum for the `functionName`.
736
+ - `aliasFunctionName` is also optional; if there are no aliases in the internationalization file, you do not need to add them here.
737
+ - Pay attention to the correspondence between internationalization fields and function and parameter names.
738
+ - Modify function parameter information, including the `example` parameter example (e.g., for a range, use `"A1:A20"`; for conditions, use `">5"`), the `require` parameter (1 for required, 0 for optional), and the `repeat` parameter (1 for allowed, 0 for not allowed). For detailed information, refer to the interface [IFunctionParam](https://github.com/dream-num/univer/blob/dev/packages/engine-formula/src/basics/function.ts).
739
+
740
+ 4. Formula Algorithm
741
+
742
+ Location: [packages/engine-formula/src/functions/math/sumif/index.ts](https://github.com/dream-num/univer/blob/dev/packages/engine-formula/src/functions/math/sumif/index.ts).
743
+
744
+ Create a new folder for the formula under the current formula category, with one folder per formula. Then create an `index.ts` file to write the formula algorithm. Use camel case for the formula `class` name, considering the formula as one word. If a formula contains `_` or `.`, treat it as two words, such as:
745
+
746
+ - `SUMIF` => `Sumif`
747
+ - `NETWORKDAYS.INTL` => `Networkdays_Intl`
748
+ - `ARRAY_CONSTRAIN` => `Array_Constrain`
749
+
750
+ Create a `__tests__` folder at the same level to write unit tests. After writing, remember to add the formula algorithm and function name mapping in the `function-map` file in the category directory to register the formula algorithm.
751
+
752
+ Location: [packages/engine-formula/src/functions/math/function-map.ts](https://github.com/dream-num/univer/blob/dev/packages/engine-formula/src/functions/math/function-map.ts).
753
+
754
+ 5. Unit Tests
755
+
756
+ Location: [packages/engine-formula/src/functions/math/sumif/\_\_tests\_\_/index.spec.ts](https://github.com/dream-num/univer/blob/dev/packages/engine-formula/src/functions/math/sumif/__tests__/index.spec.ts)
757
+
758
+ Note:
759
+
760
+ - Supplement `sheetData` according to the formula's calculation needs, construct `cellData` based on the calculated data, and determine `rowCount` and `columnCount`.
761
+ - Manually initialize the formula with `new Sumif(FUNCTION_NAMES_MATH.SUMIF)`.
762
+ - Manually build the formula parameters for each test, and execute `calculate` at the end.
763
+ - Single formula tests are generally used for testing the algorithm of the current formula. If testing nested formulas with multiple formulas is needed, manually nest them or go to the `/packages/engine-formula/src/functions/__tests__` directory to execute complex nested formulas.
764
+
765
+ 6. Functional Tests
766
+
767
+ Start Univer in development mode, test formulas on the interface, and preconstruct data.
768
+
769
+ - In any blank cell, enter `=sumif`. Expect a search prompt list to appear.
770
+ - After selecting `SUMIF` or entering `=sumif(`, trigger the formula details popup and carefully check the contents.
771
+ - Select the data range, trigger the calculation, and check if the formula calculation result is correct.
772
+
773
+ #### Considerations for Formula Implementation
774
+
775
+ - Any formula's input and output can be `A1`, `A1:B10`, etc. When researching Excel, consider all cases, such as `=SIN(A1:B10)`, which expands to the calculated range.
776
+ - For example, the `XLOOKUP` function requires at least one of the rows or columns of its two inputs to be of equal size for matrix calculation.
777
+ - For example, the `SUMIF` function, although commonly used for summation, can expand based on the second parameter.
778
+ ![sumif array](./assets/img/sumif-array.png)
779
+ ![sumif array result](./assets/img/sumif-array-result.png)
780
+ - Excel formula calculation is becoming more like numpy, for example:
781
+ ![numpy](./assets/img/numpy.png)
782
+ - For numerical calculations in formulas, use built-in methods and try to avoid obtaining values for manual calculation. Because formula parameters can be values, arrays, or references. You can refer to existing `sum` and `minus` functions.
783
+ - Precision issues: The formula introduces `big.js`, and using built-in methods will call this library. However, it is nearly 100 times slower than native calculations. Therefore, for methods like `sin`, it is advisable to use native implementations.
784
+ - For custom calculations, use the `product` function, suitable for calculating two input parameters. Call `map` to iterate over the values for changes to a parameter's own values.
785
+
786
+ #### Formula Basic Tools
787
+
788
+ 1. `ValueObjectFactory` is used to automatically recognize parameter formats and create a parameter instance. Use `RangeReferenceObject` to create parameter instances for range-type data.
789
+ 2. The array `toArrayValueObject` can be operated directly with values to get a new array.