Skip to content

Commit

Permalink
feat(formual): add text function
Browse files Browse the repository at this point in the history
  • Loading branch information
Dushusir committed Mar 15, 2024
1 parent 37acb36 commit a2cfdf3
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 19 deletions.
4 changes: 4 additions & 0 deletions packages/engine-formula/src/basics/object-class-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export class ObjectClassType extends Disposable {
return this.pattern;
}

/**
* Only used in NumberValueObject
* @param pattern
*/
setPattern(pattern: string) {
this.pattern = pattern;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/engine-formula/src/engine/ast-node/suffix-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ export class SuffixNode extends BaseAstNode {
) as FunctionVariantType;

// set number format
result.setPattern('0.00%');
if ((result as NumberValueObject).isNumber()) {
result.setPattern('0.00%');
}
} else if (this._operatorString === suffixToken.POUND) {
result = this._handlerPound(value);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,10 @@ export class BaseReferenceObject extends ObjectClassType {
}

const resultObjectValue = this.getCellValueObject(cell);
const isNumber = resultObjectValue.isNumber();

const pattern = this._numfmtItemData[unitId]?.[sheetId]?.[r]?.[c];
pattern && resultObjectValue.setPattern(pattern);
pattern && isNumber && resultObjectValue.setPattern(pattern);

result = callback(resultObjectValue, r, c);

Expand All @@ -183,12 +184,13 @@ export class BaseReferenceObject extends ObjectClassType {
}

const cellValueObject = this.getCellValueObject(cell);
const isNumber = cellValueObject.isNumber();

// Set numfmt pattern
const unitId = this._forcedUnitId || this._defaultUnitId;
const sheetId = this._forcedSheetId || this._defaultSheetId;
const numfmtItem = this._numfmtItemData[unitId]?.[sheetId]?.[startRow]?.[startColumn];
numfmtItem && cellValueObject.setPattern(numfmtItem);
numfmtItem && isNumber && cellValueObject.setPattern(numfmtItem);

return cellValueObject;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,10 @@ export class NumberValueObject extends BaseValueObject {
return (o as BaseValueObject).plus(this);
}
const object = this.minusBy(valueObject.getValue());

// = 1 - #NAME? gets #NAME?, = 1 - #VALUE! gets #VALUE!
if (object.isError()) {
return this;
return object;
}

// Set number format
Expand All @@ -458,6 +460,11 @@ export class NumberValueObject extends BaseValueObject {
}
const object = this.multiplyBy(valueObject.getValue());

// = 1 * #NAME? gets #NAME?, = 1 * #VALUE! gets #VALUE!
if (object.isError()) {
return object;
}

// Set number format
object.setPattern(this.getPattern() || valueObject.getPattern());

Expand All @@ -474,6 +481,11 @@ export class NumberValueObject extends BaseValueObject {
}
const object = this.dividedBy(valueObject.getValue());

// = 1 / #NAME? gets #NAME?, = 1 / #VALUE! gets #VALUE!
if (object.isError()) {
return object;
}

// Set number format
object.setPattern(this.getPattern() || valueObject.getPattern());

Expand Down
4 changes: 2 additions & 2 deletions packages/engine-formula/src/functions/lookup/index/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ import { BaseFunction } from '../../base-function';

export class Index extends BaseFunction {
override calculate(reference: BaseValueObject, rowNum: BaseValueObject, columnNum?: BaseValueObject, areaNum?: BaseValueObject) {
if (reference == null) {
if (reference == null || rowNum == null) {
return ErrorValueObject.create(ErrorType.NA);
}

if (reference.isError()) {
return reference;
}

if (rowNum?.isError()) {
if (rowNum.isError()) {
return rowNum;
}

Expand Down
6 changes: 5 additions & 1 deletion packages/engine-formula/src/functions/text/function-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@

import { Concatenate } from './concatenate';
import { FUNCTION_NAMES_TEXT } from './function-names';
import { Text } from './text';

export const functionText = [[Concatenate, FUNCTION_NAMES_TEXT.CONCATENATE]];
export const functionText = [
[Concatenate, FUNCTION_NAMES_TEXT.CONCATENATE],
[Text, FUNCTION_NAMES_TEXT.TEXT],
];
141 changes: 141 additions & 0 deletions packages/engine-formula/src/functions/text/text/__test__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, expect, it } from 'vitest';

import { FUNCTION_NAMES_TEXT } from '../../function-names';
import { Text } from '..';
import { NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import type { ArrayValueObject } from '../../../../engine/value-object/array-value-object';
import { transformToValue } from '../../../../engine/value-object/array-value-object';

describe('Test text function', () => {
const textFunction = new Text(FUNCTION_NAMES_TEXT.TEXT);

// function getArrayValueObjectPattern(arrayValue: ArrayValueObject) {
// return arrayValue.mapValue((value) => {
// return value.getPattern();
// })
// }

describe('Text', () => {
it('Text is single cell, format text is single cell', () => {
const text1 = NumberValueObject.create(111);
const formatText = StringValueObject.create('$#,##0.00');
const result = textFunction.calculate(text1, formatText);
const resultArray = result.getArrayValue();
expect(transformToValue(resultArray)).toStrictEqual([[111]]);
expect((result as ArrayValueObject).getFirstCell().getPattern()).toStrictEqual('$#,##0.00');
});

// it('Text1 is single cell, text2 is array', () => {
// const text1 = StringValueObject.create('a');
// const text2 = ArrayValueObject.create({
// calculateValueList: transformToValueObject([
// [1, 2, 3],
// [2, 3, 4],
// [3, 4, 5],
// ]),
// rowCount: 3,
// columnCount: 3,
// unitId: '',
// sheetId: '',
// row: 0,
// column: 0,
// });
// const result = textFunction.calculate(text1, text2);
// expect(transformToValue(result.getArrayValue())).toStrictEqual([['a1', 'a2', 'a3'], ['a2', 'a3', 'a4'], ['a3', 'a4', 'a5']]);
// });

// it('Text1 is array, text2 is single cell', () => {
// const text1 = ArrayValueObject.create({
// calculateValueList: transformToValueObject([
// [1, 2, 3],
// [2, 3, 4],
// [3, 4, 5],
// ]),
// rowCount: 3,
// columnCount: 3,
// unitId: '',
// sheetId: '',
// row: 0,
// column: 0,
// });
// const text2 = StringValueObject.create('a');
// const result = textFunction.calculate(text1, text2);
// expect(transformToValue(result.getArrayValue())).toStrictEqual([['1a', '2a', '3a'], ['2a', '3a', '4a'], ['3a', '4a', '5a']]);
// });

// it('Text1 is 3*1 array, text2 is 1*3 array', () => {
// const text1 = ArrayValueObject.create({
// calculateValueList: transformToValueObject([
// ['a'],
// ['b'],
// ['c'],
// ]),
// rowCount: 3,
// columnCount: 1,
// unitId: '',
// sheetId: '',
// row: 0,
// column: 0,
// });
// const text2 = ArrayValueObject.create({
// calculateValueList: transformToValueObject([
// [1, 2, 3],
// ]),
// rowCount: 1,
// columnCount: 3,
// unitId: '',
// sheetId: '',
// row: 0,
// column: 0,
// });
// const result = textFunction.calculate(text1, text2);
// expect(transformToValue(result.getArrayValue())).toStrictEqual([['a1', 'a2', 'a3'], ['b1', 'b2', 'b3'], ['c1', 'c2', 'c3']]);
// });

// it('Text1 is 2*2 array, text2 is 3*3 array', () => {
// const text1 = ArrayValueObject.create({
// calculateValueList: transformToValueObject([
// ['a', 'd'],
// [0, null],
// ]),
// rowCount: 2,
// columnCount: 2,
// unitId: '',
// sheetId: '',
// row: 0,
// column: 0,
// });
// const text2 = ArrayValueObject.create({
// calculateValueList: transformToValueObject([
// [1, 2, 3],
// [0, null, 4],
// [3, 4, 5],
// ]),
// rowCount: 3,
// columnCount: 3,
// unitId: '',
// sheetId: '',
// row: 0,
// column: 0,
// });
// const result = textFunction.calculate(text1, text2);
// expect(transformToValue(result.getArrayValue())).toStrictEqual([['a1', 'd2', '#N/A'], ['00', '', '#N/A'], ['#N/A', '#N/A', '#N/A']]);
// });
});
});
83 changes: 83 additions & 0 deletions packages/engine-formula/src/functions/text/text/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ErrorType } from '../../../basics/error-type';
import { expandArrayValueObject } from '../../../engine/utils/array-object';
import type { ArrayValueObject } from '../../../engine/value-object/array-value-object';
import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object';
import { BaseFunction } from '../../base-function';

export class Text extends BaseFunction {
override calculate(text: BaseValueObject, formatText: BaseValueObject) {
if (text == null || formatText == null) {
return ErrorValueObject.create(ErrorType.NA);
}

if (text.isError()) {
return text;
}

if (formatText.isError()) {
return formatText;
}

// get max row length
const maxRowLength = Math.max(
text.isArray() ? (text as ArrayValueObject).getRowCount() : 1,
formatText.isArray() ? (formatText as ArrayValueObject).getRowCount() : 1
);

// get max column length
const maxColumnLength = Math.max(
text.isArray() ? (text as ArrayValueObject).getColumnCount() : 1,
formatText.isArray() ? (formatText as ArrayValueObject).getColumnCount() : 1
);

const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text);
const formatTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, formatText);

return textArray.map((textValue, rowIndex, columnIndex) => {
if (textValue.isError() || textValue.isString() || textValue.isBoolean()) {
return textValue;
}

let formatTextValue = formatTextArray.get(rowIndex, columnIndex);

if (formatTextValue.isError()) {
return formatTextValue;
}

if (formatTextValue.isBoolean()) {
return ErrorValueObject.create(ErrorType.VALUE);
}

if (formatTextValue.isNull()) {
formatTextValue = StringValueObject.create(' ');
}

const formatTextValueString = `${formatTextValue.getValue()}`;

if (textValue.isNull()) {
textValue = NumberValueObject.create(0);
}

const textValueNumber = textValue.getValue() as number;

return NumberValueObject.create(textValueNumber, formatTextValueString);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,8 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: 'value', detail: 'A numeric value that you want to be converted into text.' },
formatText: { name: 'format_text', detail: 'A text string that defines the formatting that you want to be applied to the supplied value.' },
},
},
TEXTAFTER: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: '', detail: 'テキストに変換する数値。' },
formatText: { name: '数値形式', detail: '指定された値に適用する書式を定義するテキスト文字列。' },
},
},
TEXTAFTER: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,8 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: '', detail: '要转换为文本的数值。' },
formatText: { name: '数字格式', detail: '一个文本字符串,定义要应用于所提供值的格式。' },
},
},
TEXTAFTER: {
Expand Down
12 changes: 6 additions & 6 deletions packages/sheets-formula/src/services/function-list/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,16 +750,16 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [
abstract: 'formula.functionList.TEXT.abstract',
functionParameter: [
{
name: 'formula.functionList.TEXT.functionParameter.number1.name',
detail: 'formula.functionList.TEXT.functionParameter.number1.detail',
example: 'A1:A20',
name: 'formula.functionList.TEXT.functionParameter.value.name',
detail: 'formula.functionList.TEXT.functionParameter.value.detail',
example: '1.23',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.TEXT.functionParameter.number2.name',
detail: 'formula.functionList.TEXT.functionParameter.number2.detail',
example: 'A1:A20',
name: 'formula.functionList.TEXT.functionParameter.formatText.name',
detail: 'formula.functionList.TEXT.functionParameter.formatText.detail',
example: '"$0.00"',
require: 1,
repeat: 0,
},
Expand Down

0 comments on commit a2cfdf3

Please sign in to comment.