snapshot of https://github.com/dream-num/univer.git (dev, 2026-09-16; blobs > 131072B omitted)
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled
This commit is contained in:
41
packages/sheets-sort-ui/README.md
Normal file
41
packages/sheets-sort-ui/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# @univerjs/sheets-sort-ui
|
||||
|
||||
[](https://npmjs.com/package/@univerjs/sheets-sort-ui)
|
||||
[](https://npmjs.com/package/@univerjs/sheets-sort-ui)
|
||||
[](https://npmjs.com/package/@univerjs/sheets-sort-ui)
|
||||
|
||||
`@univerjs/sheets-sort-ui` adds sorting menus and panels for Univer Sheets.
|
||||
|
||||
## Package Overview
|
||||
|
||||
| Package | UMD global | CSS | Locales | Facade entry |
|
||||
| --- | --- | :---: | :---: | :---: |
|
||||
| `@univerjs/sheets-sort-ui` | `UniverSheetsSortUi` | Yes | Yes | No |
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @univerjs/sheets-sort-ui
|
||||
# or
|
||||
npm install @univerjs/sheets-sort-ui
|
||||
```
|
||||
|
||||
Keep all `@univerjs/*` packages on the same version.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import '@univerjs/sheets-sort-ui/lib/index.css';
|
||||
import EnUS from '@univerjs/sheets-sort-ui/locale/en-US';
|
||||
import { UniverSheetsSortUIPlugin } from '@univerjs/sheets-sort-ui';
|
||||
|
||||
univer.registerPlugin(UniverSheetsSortUIPlugin);
|
||||
|
||||
// Merge EnUS into your Univer locale map when this package contributes UI text.
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Documentation](https://docs.univer.ai)
|
||||
- [NPM package](https://npmjs.com/package/@univerjs/sheets-sort-ui)
|
||||
- [GitHub repository](https://github.com/dream-num/univer)
|
||||
96
packages/sheets-sort-ui/package.json
Normal file
96
packages/sheets-sort-ui/package.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"name": "@univerjs/sheets-sort-ui",
|
||||
"version": "1.0.0-rc.0",
|
||||
"private": false,
|
||||
"description": "Sorting menus and panels for Univer Sheets.",
|
||||
"author": "DreamNum Co., Ltd. <developer@univer.ai>",
|
||||
"license": "Apache-2.0",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/univer"
|
||||
},
|
||||
"homepage": "https://univer.ai",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dream-num/univer"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/dream-num/univer/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"univer",
|
||||
"sheets",
|
||||
"sort",
|
||||
"ui",
|
||||
"plugin"
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*",
|
||||
"./locale/*": "./src/locale/*.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "./lib/es/index.js",
|
||||
"module": "./lib/es/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./lib/es/index.js",
|
||||
"require": "./lib/cjs/index.js",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./lib/es/*",
|
||||
"require": "./lib/cjs/*",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./locale/*": {
|
||||
"import": "./lib/es/locale/*.js",
|
||||
"require": "./lib/cjs/locale/*.js",
|
||||
"types": "./lib/types/locale/*.d.ts"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build:bundle": "univer-cli build",
|
||||
"build:types": "tsc -p tsconfig.node.json",
|
||||
"build": "pnpm run build:bundle && pnpm run build:types"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/icons": "1.38.0",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-sort": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@univerjs-infra/shared": "workspace:*",
|
||||
"@univerjs/sheets-formula-ui": "workspace:*",
|
||||
"postcss": "^8.5.28",
|
||||
"react": "18.3.1",
|
||||
"rxjs": "^7.8.2",
|
||||
"tailwindcss": "3.4.18",
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^5.0.0"
|
||||
}
|
||||
}
|
||||
1
packages/sheets-sort-ui/postcss.config.mjs
Normal file
1
packages/sheets-sort-ui/postcss.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@univerjs-infra/shared/postcss';
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { Dependency, IWorkbookData, Workbook } from '@univerjs/core';
|
||||
import { ILogService, Inject, Injector, IUniverInstanceService, LocaleService, LocaleType, LogLevel, Plugin, Tools, Univer, UniverInstanceType } from '@univerjs/core';
|
||||
import { FormulaCurrentConfigService, FormulaDataModel, FormulaRuntimeService, HyperlinkEngineFormulaService, IFormulaCurrentConfigService, IFormulaRuntimeService, IHyperlinkEngineFormulaService, LexerTreeBuilder } from '@univerjs/engine-formula';
|
||||
import { SheetInterceptorService, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { FormulaReorderController } from '@univerjs/sheets-formula-ui';
|
||||
import { SheetsSortService } from '@univerjs/sheets-sort';
|
||||
import { SheetsSortController } from '@univerjs/sheets-sort/controllers/sheets-sort.controller.js';
|
||||
import zhCN from '../../../locale/zh-CN';
|
||||
import { SheetsSortUIService } from '../../../services/sheets-sort-ui.service';
|
||||
|
||||
const TEST_WORKBOOK_DATA_DEMO: IWorkbookData = {
|
||||
id: 'test',
|
||||
appVersion: '3.0.0-alpha',
|
||||
sheets: {
|
||||
sheet1: {
|
||||
id: 'sheet1',
|
||||
name: 'sheet1',
|
||||
cellData: {
|
||||
0: {
|
||||
9: { // J1
|
||||
v: 1,
|
||||
},
|
||||
10: { // K1
|
||||
v: 2,
|
||||
},
|
||||
11: { // L1
|
||||
f: '=J1/K1',
|
||||
},
|
||||
},
|
||||
1: {
|
||||
9: { // J2
|
||||
v: 2,
|
||||
},
|
||||
10: { // K2
|
||||
v: 2,
|
||||
},
|
||||
11: { // L2
|
||||
f: '=J2/K2',
|
||||
},
|
||||
},
|
||||
2: {
|
||||
9: { // J3
|
||||
v: 3,
|
||||
},
|
||||
10: { // K3
|
||||
v: 2,
|
||||
},
|
||||
11: { // L3
|
||||
f: '=J3/K3',
|
||||
},
|
||||
},
|
||||
3: {
|
||||
9: { // J4
|
||||
v: 4,
|
||||
},
|
||||
10: { // K4
|
||||
v: 2,
|
||||
},
|
||||
11: { // L4
|
||||
f: '=J4/K4',
|
||||
},
|
||||
},
|
||||
4: {
|
||||
9: { // J5
|
||||
v: 5,
|
||||
},
|
||||
10: { // K5
|
||||
v: 2,
|
||||
},
|
||||
11: { // L5
|
||||
f: '=J5/K5',
|
||||
},
|
||||
},
|
||||
5: {
|
||||
9: { // J6
|
||||
v: 6,
|
||||
},
|
||||
10: { // K6
|
||||
v: 2,
|
||||
},
|
||||
11: { // L6
|
||||
f: '=J6/K6',
|
||||
},
|
||||
},
|
||||
6: {
|
||||
9: { // J7
|
||||
v: 7,
|
||||
},
|
||||
10: { // K7
|
||||
v: 2,
|
||||
},
|
||||
11: { // L7
|
||||
f: '=SUM(J7:K8)',
|
||||
},
|
||||
},
|
||||
7: {
|
||||
9: { // J8
|
||||
v: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
locale: LocaleType.ZH_CN,
|
||||
name: '',
|
||||
sheetOrder: [],
|
||||
styles: {},
|
||||
};
|
||||
|
||||
export interface ITestBed {
|
||||
univer: Univer;
|
||||
get: Injector['get'];
|
||||
sheet: Workbook;
|
||||
}
|
||||
|
||||
export function createCommandTestBed(workbookData?: IWorkbookData, dependencies?: Dependency[]): ITestBed {
|
||||
const univer = new Univer();
|
||||
const injector = univer.__getInjector();
|
||||
|
||||
class TestPlugin extends Plugin {
|
||||
static override pluginName = 'test-plugin';
|
||||
static override type = UniverInstanceType.UNIVER_SHEET;
|
||||
|
||||
constructor(
|
||||
_config: undefined,
|
||||
@Inject(Injector) override readonly _injector: Injector
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override onStarting(): void {
|
||||
const injector = this._injector;
|
||||
injector.add([SheetsSelectionsService]);
|
||||
injector.add([SheetInterceptorService]);
|
||||
injector.add([FormulaDataModel]);
|
||||
injector.add([LexerTreeBuilder]);
|
||||
injector.add([IHyperlinkEngineFormulaService, { useClass: HyperlinkEngineFormulaService }]);
|
||||
injector.add([IFormulaRuntimeService, { useClass: FormulaRuntimeService }]);
|
||||
injector.add([IFormulaCurrentConfigService, { useClass: FormulaCurrentConfigService }]);
|
||||
injector.add([SheetsSortController]);
|
||||
injector.add([SheetsSortService]);
|
||||
injector.add([SheetsSortUIService]);
|
||||
injector.add([FormulaReorderController]);
|
||||
|
||||
dependencies?.forEach((d) => injector.add(d));
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this._injector.get(SheetsSortController);
|
||||
this._injector.get(FormulaReorderController);
|
||||
}
|
||||
}
|
||||
|
||||
univer.registerPlugin(TestPlugin);
|
||||
const sheet = univer.createUnit<IWorkbookData, Workbook>(UniverInstanceType.UNIVER_SHEET, Tools.deepClone(workbookData ?? TEST_WORKBOOK_DATA_DEMO));
|
||||
|
||||
const univerInstanceService = injector.get(IUniverInstanceService);
|
||||
univerInstanceService.focusUnit('test');
|
||||
const logService = injector.get(ILogService);
|
||||
|
||||
logService.setLogLevel(LogLevel.SILENT); // change this to `LogLevel.VERBOSE` to debug tests via logs
|
||||
|
||||
const localeService = injector.get(LocaleService);
|
||||
localeService.load({ zhCN });
|
||||
|
||||
return {
|
||||
univer,
|
||||
get: injector.get.bind(injector),
|
||||
sheet,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { ICellData, Injector, Nullable, Univer, Workbook } from '@univerjs/core';
|
||||
import { ICommandService, IConfirmService, IUniverInstanceService, RANGE_TYPE, TestConfirmService, UniverInstanceType } from '@univerjs/core';
|
||||
import { ReorderRangeCommand, ReorderRangeMutation, SetRangeValuesMutation, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { SheetsSortUIService } from '../../../services/sheets-sort-ui.service';
|
||||
import { createCommandTestBed } from './create-command-test-bed';
|
||||
|
||||
describe('Test "Sort Range Commands"', () => {
|
||||
let univer: Univer;
|
||||
let get: Injector['get'];
|
||||
let commandService: ICommandService;
|
||||
let sortService: SheetsSortUIService;
|
||||
let getValues: (
|
||||
startRow: number,
|
||||
startColumn: number,
|
||||
endRow: number,
|
||||
endColumn: number
|
||||
) => Array<Array<Nullable<ICellData>>> | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
const testBed = createCommandTestBed(undefined, [
|
||||
[IConfirmService, { useClass: TestConfirmService }],
|
||||
]);
|
||||
univer = testBed.univer;
|
||||
get = testBed.get;
|
||||
|
||||
commandService = get(ICommandService);
|
||||
commandService.registerCommand(ReorderRangeCommand);
|
||||
commandService.registerCommand(SetRangeValuesMutation);
|
||||
commandService.registerCommand(ReorderRangeMutation);
|
||||
|
||||
sortService = get(SheetsSortUIService);
|
||||
|
||||
getValues = (
|
||||
startRow: number,
|
||||
startColumn: number,
|
||||
endRow: number,
|
||||
endColumn: number
|
||||
): Array<Array<Nullable<ICellData>>> | undefined =>
|
||||
get(IUniverInstanceService)
|
||||
.getUnit<Workbook>('test', UniverInstanceType.UNIVER_SHEET)
|
||||
?.getSheetBySheetId('sheet1')
|
||||
?.getRange(startRow, startColumn, endRow, endColumn)
|
||||
.getValues();
|
||||
});
|
||||
|
||||
afterEach(() => univer.dispose());
|
||||
|
||||
describe('sort range has formula', () => {
|
||||
it('desc case: ', async () => {
|
||||
// J1
|
||||
let J1Cell = getValues(0, 9, 0, 9)?.[0]?.[0];
|
||||
expect(J1Cell?.v).toStrictEqual(1);
|
||||
// L1
|
||||
let L1Cell = getValues(0, 11, 0, 11)?.[0]?.[0];
|
||||
expect(L1Cell?.f).toStrictEqual('=J1/K1');
|
||||
// L7
|
||||
let L7Cell = getValues(6, 11, 6, 11)?.[0]?.[0];
|
||||
expect(L7Cell?.f).toStrictEqual('=SUM(J7:K8)');
|
||||
|
||||
const selectionManager = get(SheetsSelectionsService);
|
||||
selectionManager.addSelections([
|
||||
{
|
||||
range: {
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 6,
|
||||
endColumn: 11,
|
||||
rangeType: RANGE_TYPE.NORMAL,
|
||||
},
|
||||
primary: {
|
||||
actualRow: 0,
|
||||
actualColumn: 9,
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 6,
|
||||
endColumn: 11,
|
||||
isMerged: false,
|
||||
isMergedMainCell: false,
|
||||
},
|
||||
style: null,
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await sortService.triggerSortDirectly(false, false);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
// J1
|
||||
J1Cell = getValues(0, 9, 0, 9)?.[0]?.[0];
|
||||
expect(J1Cell?.v).toStrictEqual(7);
|
||||
// L1
|
||||
L1Cell = getValues(0, 11, 0, 11)?.[0]?.[0];
|
||||
expect(L1Cell?.f).toStrictEqual('=SUM(J1:K2)');
|
||||
// L7
|
||||
L7Cell = getValues(6, 11, 6, 11)?.[0]?.[0];
|
||||
expect(L7Cell?.f).toStrictEqual('=J7/K7');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { ICellData, Injector, Nullable, Univer } from '@univerjs/core';
|
||||
import { ICommandService, IConfirmService, RANGE_TYPE, TestConfirmService } from '@univerjs/core';
|
||||
import { ReorderRangeCommand, ReorderRangeMutation, SetRangeValuesMutation, SetSelectionsOperation, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { SheetsSortUIService } from '../../../services/sheets-sort-ui.service';
|
||||
import {
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
} from '../sheets-sort.command';
|
||||
import { createCommandTestBed } from './create-command-test-bed';
|
||||
|
||||
describe('Sheets sort commands integration', () => {
|
||||
let univer: Univer;
|
||||
let get: Injector['get'];
|
||||
let commandService: ICommandService;
|
||||
let getValues: (
|
||||
startRow: number,
|
||||
startColumn: number,
|
||||
endRow: number,
|
||||
endColumn: number
|
||||
) => Array<Array<Nullable<ICellData>>> | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
const testBed = createCommandTestBed(undefined, [
|
||||
[IConfirmService, { useClass: TestConfirmService }],
|
||||
]);
|
||||
|
||||
univer = testBed.univer;
|
||||
get = testBed.get;
|
||||
commandService = get(ICommandService);
|
||||
|
||||
[
|
||||
ReorderRangeCommand,
|
||||
ReorderRangeMutation,
|
||||
SetRangeValuesMutation,
|
||||
SetSelectionsOperation,
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
].forEach((command) => commandService.registerCommand(command));
|
||||
|
||||
getValues = (startRow, startColumn, endRow, endColumn) =>
|
||||
testBed.sheet
|
||||
.getSheetBySheetId('sheet1')
|
||||
?.getRange(startRow, startColumn, endRow, endColumn)
|
||||
.getValues();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
univer.dispose();
|
||||
});
|
||||
|
||||
function selectSortRange() {
|
||||
get(SheetsSelectionsService).addSelections([{
|
||||
range: {
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 6,
|
||||
endColumn: 11,
|
||||
rangeType: RANGE_TYPE.NORMAL,
|
||||
},
|
||||
primary: {
|
||||
actualRow: 0,
|
||||
actualColumn: 9,
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 6,
|
||||
endColumn: 11,
|
||||
isMerged: false,
|
||||
isMergedMainCell: false,
|
||||
},
|
||||
style: null,
|
||||
}]);
|
||||
}
|
||||
|
||||
function expectAscendingSortResult() {
|
||||
expect(getValues(0, 9, 0, 9)?.[0]?.[0]?.v).toBe(1);
|
||||
expect(getValues(6, 9, 6, 9)?.[0]?.[0]?.v).toBe(7);
|
||||
expect(getValues(0, 11, 0, 11)?.[0]?.[0]?.f).toBe('=J1/K1');
|
||||
expect(getValues(6, 11, 6, 11)?.[0]?.[0]?.f).toBe('=SUM(J7:K8)');
|
||||
}
|
||||
|
||||
function expectDescendingSortResult() {
|
||||
expect(getValues(0, 9, 0, 9)?.[0]?.[0]?.v).toBe(7);
|
||||
expect(getValues(6, 9, 6, 9)?.[0]?.[0]?.v).toBe(1);
|
||||
expect(getValues(0, 11, 0, 11)?.[0]?.[0]?.f).toBe('=SUM(J1:K2)');
|
||||
expect(getValues(6, 11, 6, 11)?.[0]?.[0]?.f).toBe('=J7/K7');
|
||||
}
|
||||
|
||||
function expectAscendingExtendedSortResult() {
|
||||
expect(getValues(0, 9, 0, 9)?.[0]?.[0]?.v).toBe(1);
|
||||
expect(getValues(1, 9, 1, 9)?.[0]?.[0]?.v).toBe(1);
|
||||
expect(getValues(7, 9, 7, 9)?.[0]?.[0]?.v).toBe(7);
|
||||
expect(get(SheetsSelectionsService).getCurrentLastSelection()?.range).toMatchObject({
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 7,
|
||||
endColumn: 11,
|
||||
});
|
||||
}
|
||||
|
||||
function expectDescendingExtendedSortResult() {
|
||||
expect(getValues(0, 9, 0, 9)?.[0]?.[0]?.v).toBe(7);
|
||||
expect(getValues(6, 9, 6, 9)?.[0]?.[0]?.v).toBe(1);
|
||||
expect(getValues(7, 9, 7, 9)?.[0]?.[0]?.v).toBe(1);
|
||||
expect(get(SheetsSelectionsService).getCurrentLastSelection()?.range).toMatchObject({
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 7,
|
||||
endColumn: 11,
|
||||
});
|
||||
}
|
||||
|
||||
it('sorts the selected range ascending through executeCommand', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeAscCommand.id)).toBe(true);
|
||||
expectAscendingSortResult();
|
||||
});
|
||||
|
||||
it('sorts the selected range descending through executeCommand and rewrites formulas', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeDescCommand.id)).toBe(true);
|
||||
expectDescendingSortResult();
|
||||
});
|
||||
|
||||
it('sorts ascending from the extended range menu command', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeAscExtCommand.id)).toBe(true);
|
||||
expectAscendingExtendedSortResult();
|
||||
});
|
||||
|
||||
it('sorts descending from the extended range menu command', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeDescExtCommand.id)).toBe(true);
|
||||
expectDescendingExtendedSortResult();
|
||||
});
|
||||
|
||||
it('sorts ascending from the context menu command', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeAscInCtxMenuCommand.id)).toBe(true);
|
||||
expectAscendingSortResult();
|
||||
});
|
||||
|
||||
it('sorts descending from the context menu command', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeDescInCtxMenuCommand.id)).toBe(true);
|
||||
expectDescendingSortResult();
|
||||
});
|
||||
|
||||
it('sorts ascending from the extended context menu command', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeAscExtInCtxMenuCommand.id)).toBe(true);
|
||||
expectAscendingExtendedSortResult();
|
||||
});
|
||||
|
||||
it('sorts descending from the extended context menu command', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeDescExtInCtxMenuCommand.id)).toBe(true);
|
||||
expectDescendingExtendedSortResult();
|
||||
});
|
||||
|
||||
it('opens custom sort panel for the selected range', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeCustomCommand.id)).toBe(true);
|
||||
|
||||
const customSortState = get(SheetsSortUIService).customSortState();
|
||||
expect(customSortState?.show).toBe(true);
|
||||
expect(customSortState?.location).toMatchObject({
|
||||
unitId: 'test',
|
||||
subUnitId: 'sheet1',
|
||||
colIndex: 9,
|
||||
range: {
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 6,
|
||||
endColumn: 11,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('opens custom sort panel from the context menu command', async () => {
|
||||
selectSortRange();
|
||||
|
||||
expect(await commandService.executeCommand(SortRangeCustomInCtxMenuCommand.id)).toBe(true);
|
||||
|
||||
const customSortState = get(SheetsSortUIService).customSortState();
|
||||
expect(customSortState?.show).toBe(true);
|
||||
expect(customSortState?.location?.range).toMatchObject({
|
||||
startRow: 0,
|
||||
startColumn: 9,
|
||||
endRow: 6,
|
||||
endColumn: 11,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { IAccessor, ICommand } from '@univerjs/core';
|
||||
import { CommandType } from '@univerjs/core';
|
||||
import { SheetsSortUIService } from '../../services/sheets-sort-ui.service';
|
||||
|
||||
export const SortRangeAscCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-asc',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(true, false);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeAscExtCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-asc-ext',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(true, true);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeDescCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-desc',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(false, false);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeDescExtCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-desc-ext',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(false, true);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeCustomCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-custom',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortCustomize();
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeAscInCtxMenuCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-asc-ctx',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(true, false);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeAscExtInCtxMenuCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-asc-ext-ctx',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(true, true);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeDescInCtxMenuCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-desc-ctx',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(false, false);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeDescExtInCtxMenuCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-desc-ext-ctx',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortDirectly(false, true);
|
||||
},
|
||||
};
|
||||
|
||||
export const SortRangeCustomInCtxMenuCommand: ICommand = {
|
||||
id: 'sheet.command.sort-range-custom-ctx',
|
||||
type: CommandType.COMMAND,
|
||||
handler: async (accessor: IAccessor) => {
|
||||
const sortService = accessor.get(SheetsSortUIService);
|
||||
return await sortService.triggerSortCustomize();
|
||||
},
|
||||
};
|
||||
27
packages/sheets-sort-ui/src/config/config.ts
Normal file
27
packages/sheets-sort-ui/src/config/config.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { MenuConfig } from '@univerjs/ui';
|
||||
|
||||
export const SHEETS_SORT_UI_PLUGIN_CONFIG_KEY = 'sheets-sort-ui.config';
|
||||
|
||||
export const configSymbol = Symbol(SHEETS_SORT_UI_PLUGIN_CONFIG_KEY);
|
||||
|
||||
export interface IUniverSheetsSortUIConfig {
|
||||
menu?: MenuConfig;
|
||||
}
|
||||
|
||||
export const defaultPluginConfig: IUniverSheetsSortUIConfig = {};
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { Disposable, Inject } from '@univerjs/core';
|
||||
import { AscendingIcon, CustomSortIcon, DescendingIcon, ExpandAscendingIcon, ExpandDescendingIcon } from '@univerjs/icons';
|
||||
import { ComponentManager, IconManager } from '@univerjs/ui';
|
||||
import { CustomSortPanel } from '../views/CustomSortPanel';
|
||||
|
||||
export class ComponentsController extends Disposable {
|
||||
constructor(
|
||||
@Inject(ComponentManager) private readonly _componentManager: ComponentManager,
|
||||
@Inject(IconManager) private readonly _iconManager: IconManager
|
||||
) {
|
||||
super();
|
||||
|
||||
this._registerIcons();
|
||||
this._registerComponents();
|
||||
}
|
||||
|
||||
private _registerIcons(): void {
|
||||
this.disposeWithMe(this._iconManager.register({
|
||||
AscendingIcon,
|
||||
ExpandAscendingIcon,
|
||||
DescendingIcon,
|
||||
ExpandDescendingIcon,
|
||||
CustomSortIcon,
|
||||
}));
|
||||
}
|
||||
|
||||
private _registerComponents(): void {
|
||||
([
|
||||
['CustomSortPanel', CustomSortPanel],
|
||||
] as const).forEach(([key, comp]) => {
|
||||
this.disposeWithMe(
|
||||
this._componentManager.register(key, comp)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 { Disposable, Inject } from '@univerjs/core';
|
||||
import { AscendingIcon, CustomSortIcon, DescendingIcon, ExpandAscendingIcon, ExpandDescendingIcon } from '@univerjs/icons';
|
||||
import { ComponentManager, IconManager } from '@univerjs/ui';
|
||||
import { MobileCustomSortPanel } from '../../views/mobile/MobileCustomSortPanel';
|
||||
|
||||
export const MOBILE_CUSTOM_SORT_PANEL = 'MobileCustomSortPanel';
|
||||
|
||||
export class MobileComponentsController extends Disposable {
|
||||
constructor(
|
||||
@Inject(ComponentManager) componentManager: ComponentManager,
|
||||
@Inject(IconManager) iconManager: IconManager
|
||||
) {
|
||||
super();
|
||||
|
||||
this.disposeWithMe(iconManager.register({
|
||||
AscendingIcon,
|
||||
ExpandAscendingIcon,
|
||||
DescendingIcon,
|
||||
ExpandDescendingIcon,
|
||||
CustomSortIcon,
|
||||
}));
|
||||
this.disposeWithMe(componentManager.register(MOBILE_CUSTOM_SORT_PANEL, MobileCustomSortPanel));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { LocaleKey } from '../../locale/types';
|
||||
import { ICommandService, Inject, LocaleService, RxDisposable } from '@univerjs/core';
|
||||
import { serializeRange } from '@univerjs/engine-formula';
|
||||
import { SortRangeCommand } from '@univerjs/sheets-sort';
|
||||
import { SheetsRenderService } from '@univerjs/sheets-ui';
|
||||
import { IDialogService, IMenuManagerService } from '@univerjs/ui';
|
||||
import { takeUntil } from 'rxjs';
|
||||
import {
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
} from '../../commands/commands/sheets-sort.command';
|
||||
import { mobileMenuSchema } from '../../menu/mobile-schema';
|
||||
import { SheetsSortUIService } from '../../services/sheets-sort-ui.service';
|
||||
import { MOBILE_CUSTOM_SORT_PANEL } from './components.controller';
|
||||
|
||||
const MOBILE_CUSTOM_SORT_DIALOG_ID = 'mobile-custom-sort-dialog';
|
||||
|
||||
export class SheetsSortMobileUIController extends RxDisposable {
|
||||
constructor(
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IMenuManagerService private readonly _menuManagerService: IMenuManagerService,
|
||||
@IDialogService private readonly _dialogService: IDialogService,
|
||||
@Inject(SheetsRenderService) private readonly _sheetRenderService: SheetsRenderService,
|
||||
@Inject(LocaleService) private readonly _localeService: LocaleService,
|
||||
@Inject(SheetsSortUIService) private readonly _sheetsSortUIService: SheetsSortUIService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._initCommands();
|
||||
this._menuManagerService.mergeMenu(mobileMenuSchema);
|
||||
this._initCustomSortPanel();
|
||||
}
|
||||
|
||||
private _initCommands(): void {
|
||||
[
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
].forEach((command) => this.disposeWithMe(this._commandService.registerCommand(command)));
|
||||
|
||||
this.disposeWithMe(this._sheetRenderService.registerSkeletonChangingMutations(SortRangeCommand.id));
|
||||
}
|
||||
|
||||
private _initCustomSortPanel(): void {
|
||||
this._sheetsSortUIService.customSortState$.pipe(takeUntil(this.dispose$)).subscribe((state) => {
|
||||
if (state?.show && state.location) {
|
||||
this._dialogService.open({
|
||||
id: MOBILE_CUSTOM_SORT_DIALOG_ID,
|
||||
title: {
|
||||
title: `${this._localeService.t<LocaleKey>('sheets-sort-ui.general.sort-custom')}: ${serializeRange(state.location.range)}`,
|
||||
},
|
||||
children: { label: MOBILE_CUSTOM_SORT_PANEL },
|
||||
mask: true,
|
||||
onClose: () => this._sheetsSortUIService.closeCustomSortPanel(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (state && !state.show) {
|
||||
this._dialogService.close(MOBILE_CUSTOM_SORT_DIALOG_ID);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// TODO:@yuhongz maybe we should add shortcut for sorting
|
||||
133
packages/sheets-sort-ui/src/controllers/ui.controller.ts
Normal file
133
packages/sheets-sort-ui/src/controllers/ui.controller.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { UIPartsService } from '@univerjs/ui';
|
||||
import type { LocaleKey } from '../locale/types';
|
||||
import type { ISheetSortLocation } from '../services/sheets-sort-ui.service';
|
||||
import { ICommandService, Inject, Injector, LocaleService, RxDisposable } from '@univerjs/core';
|
||||
import { serializeRange } from '@univerjs/engine-formula';
|
||||
import { SortRangeCommand } from '@univerjs/sheets-sort';
|
||||
import { SheetsRenderService, SheetsUIPart } from '@univerjs/sheets-ui';
|
||||
import {
|
||||
connectInjector,
|
||||
IDialogService,
|
||||
ILayoutService,
|
||||
IMenuManagerService,
|
||||
IUIPartsService,
|
||||
} from '@univerjs/ui';
|
||||
import { takeUntil } from 'rxjs';
|
||||
import {
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
} from '../commands/commands/sheets-sort.command';
|
||||
import { menuSchema } from '../menu/schema';
|
||||
import { SheetsSortUIService } from '../services/sheets-sort-ui.service';
|
||||
import EmbedSortBtn from '../views/EmbedSortBtn';
|
||||
|
||||
const CUSTOM_SORT_DIALOG_ID = 'custom-sort-dialog';
|
||||
const CUSTOM_SORT_PANEL_WIDTH = 560;
|
||||
const CUSTOM_SORT_PANEL_TOP = 128;
|
||||
|
||||
export class SheetsSortUIController extends RxDisposable {
|
||||
constructor(
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IMenuManagerService private readonly _menuManagerService: IMenuManagerService,
|
||||
@IDialogService private readonly _dialogService: IDialogService,
|
||||
@ILayoutService private readonly _layoutService: ILayoutService,
|
||||
@IUIPartsService private readonly _uiPartsService: UIPartsService,
|
||||
@Inject(SheetsRenderService) private _sheetRenderService: SheetsRenderService,
|
||||
@Inject(LocaleService) private readonly _localeService: LocaleService,
|
||||
@Inject(SheetsSortUIService) private readonly _sheetsSortUIService: SheetsSortUIService,
|
||||
@Inject(Injector) private _injector: Injector
|
||||
) {
|
||||
super();
|
||||
this._initCommands();
|
||||
this._initMenu();
|
||||
this._initUI();
|
||||
}
|
||||
|
||||
private _initMenu() {
|
||||
this._menuManagerService.mergeMenu(menuSchema);
|
||||
}
|
||||
|
||||
private _initCommands(): void {
|
||||
[
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
|
||||
].forEach((command) => this.disposeWithMe(this._commandService.registerCommand(command)));
|
||||
|
||||
this.disposeWithMe(this._sheetRenderService.registerSkeletonChangingMutations(SortRangeCommand.id));
|
||||
}
|
||||
|
||||
private _initUI(): void {
|
||||
this.disposeWithMe(
|
||||
this._uiPartsService.registerComponent(SheetsUIPart.FILTER_PANEL_EMBED_POINT, () => connectInjector(EmbedSortBtn, this._injector))
|
||||
);
|
||||
|
||||
// this controller is also responsible for toggling the CustomSortDialog
|
||||
this._sheetsSortUIService.customSortState$.pipe(takeUntil(this.dispose$)).subscribe((newState) => {
|
||||
if (newState && newState.show && newState.location) {
|
||||
this._openCustomSortPanel(newState.location);
|
||||
} else if (newState && !newState?.show) {
|
||||
this._closePanel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _openCustomSortPanel(location: ISheetSortLocation): void {
|
||||
this._dialogService.open({
|
||||
id: CUSTOM_SORT_DIALOG_ID,
|
||||
draggable: true,
|
||||
width: CUSTOM_SORT_PANEL_WIDTH,
|
||||
title: { title: `${this._localeService.t<LocaleKey>('sheets-sort-ui.general.sort-custom')}: ${serializeRange(location.range)}` },
|
||||
children: { label: 'CustomSortPanel' },
|
||||
defaultPosition: getCustomSortDialogDefaultPosition(),
|
||||
preservePositionOnDestroy: false,
|
||||
onClose: () => this._closePanel(),
|
||||
mask: true,
|
||||
});
|
||||
}
|
||||
|
||||
private _closePanel(): void {
|
||||
this._dialogService.close(CUSTOM_SORT_DIALOG_ID);
|
||||
|
||||
queueMicrotask(() => this._layoutService.focus());
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomSortDialogDefaultPosition(): { x: number; y: number } {
|
||||
const x = (window.innerWidth - CUSTOM_SORT_PANEL_WIDTH) / 2;
|
||||
const y = CUSTOM_SORT_PANEL_TOP;
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
2
packages/sheets-sort-ui/src/global.css
Normal file
2
packages/sheets-sort-ui/src/global.css
Normal file
@@ -0,0 +1,2 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
34
packages/sheets-sort-ui/src/index.ts
Normal file
34
packages/sheets-sort-ui/src/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 './global.css';
|
||||
|
||||
export {
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
} from './commands/commands/sheets-sort.command';
|
||||
export type { IUniverSheetsSortUIConfig } from './config/config';
|
||||
export { menuSchema as SheetsSortUIMenuSchema } from './menu/schema';
|
||||
export { UniverSheetsSortMobileUIPlugin } from './mobile-plugin';
|
||||
export { UniverSheetsSortUIPlugin } from './plugin';
|
||||
54
packages/sheets-sort-ui/src/locale/ar-SA.ts
Normal file
54
packages/sheets-sort-ui/src/locale/ar-SA.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'فرز',
|
||||
'sort-asc': 'تصاعدي',
|
||||
'sort-desc': 'تنازلي',
|
||||
'sort-custom': 'فرز مخصص',
|
||||
'sort-asc-ext': 'فرز تصاعدي ممتد',
|
||||
'sort-desc-ext': 'فرز تنازلي ممتد',
|
||||
'sort-asc-cur': 'تصاعدي',
|
||||
'sort-desc-cur': 'تنازلي',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'يحتوي النطاق المحدد على خلايا مدمجة بأحجام مختلفة ولا يمكن فرزها.',
|
||||
empty: 'النطاق المحدد فارغ ولا يمكن فرزه.',
|
||||
single: 'يحتوي النطاق المحدد على صف واحد فقط ولا يمكن فرزه.',
|
||||
'formula-array': 'يحتوي النطاق المحدد على صيغ مصفوفة ولا يمكن فرزه.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'تذكير الفرز',
|
||||
'sort-reminder-desc': 'توسيع نطاق الفرز أم الاحتفاظ بنطاق الفرز الحالي؟',
|
||||
'sort-reminder-ext': 'توسيع نطاق الفرز',
|
||||
'sort-reminder-no': 'الاحتفاظ بنطاق الفرز',
|
||||
'first-row-check': 'الصف الأول لا يشارك في الفرز',
|
||||
'add-condition': 'إضافة شرط',
|
||||
'delete-condition': 'حذف الشرط',
|
||||
cancel: 'إلغاء',
|
||||
confirm: 'تأكيد',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'تلميح',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/ca-ES.ts
Normal file
54
packages/sheets-sort-ui/src/locale/ca-ES.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Ordena',
|
||||
'sort-asc': 'Ascendent',
|
||||
'sort-desc': 'Descendent',
|
||||
'sort-custom': 'Ordre personalitzat',
|
||||
'sort-asc-ext': 'Expandeix ascendent',
|
||||
'sort-desc-ext': 'Expandeix descendent',
|
||||
'sort-asc-cur': 'Ascendent',
|
||||
'sort-desc-cur': 'Descendent',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'L\'interval seleccionat conté cel·les combinades de diferents mides, que no es poden ordenar.',
|
||||
empty: 'L\'interval seleccionat no té contingut i no es pot ordenar.',
|
||||
single: 'L\'interval seleccionat només té una fila i no es pot ordenar.',
|
||||
'formula-array': 'L\'interval seleccionat té fórmules de matriu i no es pot ordenar.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Recordatori d\'ordenació',
|
||||
'sort-reminder-desc': 'Estendre l\'interval d\'ordenació o mantenir-lo?',
|
||||
'sort-reminder-ext': 'Estendre l\'interval d\'ordenació',
|
||||
'sort-reminder-no': 'Mantenir l\'interval d\'ordenació',
|
||||
'first-row-check': 'La primera fila no participa en l\'ordenació',
|
||||
'add-condition': 'Afegeix condició',
|
||||
'delete-condition': 'Suprimeix la condició',
|
||||
cancel: 'Cancel·lar',
|
||||
confirm: 'Confirmar',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Informació sobre l\'eina',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/de-DE.ts
Normal file
54
packages/sheets-sort-ui/src/locale/de-DE.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Sortieren',
|
||||
'sort-asc': 'Aufsteigend',
|
||||
'sort-desc': 'Absteigend',
|
||||
'sort-custom': 'Benutzerdefiniertes Sortieren',
|
||||
'sort-asc-ext': 'Aufsteigend erweitern',
|
||||
'sort-desc-ext': 'Absteigend erweitern',
|
||||
'sort-asc-cur': 'Aufsteigend',
|
||||
'sort-desc-cur': 'Absteigend',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'Der ausgewählte Bereich enthält verbundene Zellen unterschiedlicher Größe und kann nicht sortiert werden.',
|
||||
empty: 'Der ausgewählte Bereich hat keinen Inhalt und kann nicht sortiert werden.',
|
||||
single: 'Der ausgewählte Bereich hat nur eine Zeile und kann nicht sortiert werden.',
|
||||
'formula-array': 'Der ausgewählte Bereich enthält Matrixformeln und kann nicht sortiert werden.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Sortierhinweis',
|
||||
'sort-reminder-desc': 'Bereichssortierung erweitern oder beibehalten?',
|
||||
'sort-reminder-ext': 'Bereichssortierung erweitern',
|
||||
'sort-reminder-no': 'Bereichssortierung beibehalten',
|
||||
'first-row-check': 'Erste Zeile nimmt nicht am Sortieren teil',
|
||||
'add-condition': 'Bedingung hinzufügen',
|
||||
'delete-condition': 'Bedingung löschen',
|
||||
cancel: 'Abbrechen',
|
||||
confirm: 'Bestätigen',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Tooltip',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
52
packages/sheets-sort-ui/src/locale/en-US.ts
Normal file
52
packages/sheets-sort-ui/src/locale/en-US.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const locale = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Sort',
|
||||
'sort-asc': 'Ascending',
|
||||
'sort-desc': 'Descending',
|
||||
'sort-custom': 'Custom Sort',
|
||||
'sort-asc-ext': 'Expand Ascending',
|
||||
'sort-desc-ext': 'Expand Descending',
|
||||
'sort-asc-cur': 'Ascending',
|
||||
'sort-desc-cur': 'Descending',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'The selected range contains merged cells of different sizes, which cannot be sorted.',
|
||||
empty: 'The selected range has no content and cannot be sorted.',
|
||||
single: 'The selected range has only one row and cannot be sorted.',
|
||||
'formula-array': 'The selected range has array formulas and cannot be sorted.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Sort Reminder',
|
||||
'sort-reminder-desc': 'Extend range sorting or keep range sorting?',
|
||||
'sort-reminder-ext': 'Extend range sorting',
|
||||
'sort-reminder-no': 'Keep range sorting',
|
||||
'first-row-check': 'First row does not participate in sorting',
|
||||
'add-condition': 'Add condition',
|
||||
'delete-condition': 'Delete condition',
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Confirm',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Tooltip',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/es-ES.ts
Normal file
54
packages/sheets-sort-ui/src/locale/es-ES.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Ordenar',
|
||||
'sort-asc': 'Ascendente',
|
||||
'sort-desc': 'Descendente',
|
||||
'sort-custom': 'Orden personalizado',
|
||||
'sort-asc-ext': 'Expandir ascendente',
|
||||
'sort-desc-ext': 'Expandir descendente',
|
||||
'sort-asc-cur': 'Ascendente',
|
||||
'sort-desc-cur': 'Descendente',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'El rango seleccionado contiene celdas combinadas de diferentes tamaños, que no se pueden ordenar.',
|
||||
empty: 'El rango seleccionado no tiene contenido y no se puede ordenar.',
|
||||
single: 'El rango seleccionado solo tiene una fila y no se puede ordenar.',
|
||||
'formula-array': 'El rango seleccionado tiene fórmulas de matriz y no se puede ordenar.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Recordatorio de ordenación',
|
||||
'sort-reminder-desc': '¿Extender el rango de ordenación o mantener el rango?',
|
||||
'sort-reminder-ext': 'Extender el rango de ordenación',
|
||||
'sort-reminder-no': 'Mantener el rango de ordenación',
|
||||
'first-row-check': 'La primera fila no participa en la ordenación',
|
||||
'add-condition': 'Agregar condición',
|
||||
'delete-condition': 'Eliminar condición',
|
||||
cancel: 'Cancelar',
|
||||
confirm: 'Confirmar',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Información sobre la herramienta',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/fa-IR.ts
Normal file
54
packages/sheets-sort-ui/src/locale/fa-IR.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'مرتبسازی',
|
||||
'sort-asc': 'صعودی',
|
||||
'sort-desc': 'نزولی',
|
||||
'sort-custom': 'مرتبسازی سفارشی',
|
||||
'sort-asc-ext': 'گسترش صعودی',
|
||||
'sort-desc-ext': 'گسترش نزولی',
|
||||
'sort-asc-cur': 'صعودی',
|
||||
'sort-desc-cur': 'نزولی',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'محدوده انتخاب شده حاوی سلولهای ادغام شده با اندازههای مختلف است که نمیتوان مرتب کرد.',
|
||||
empty: 'محدوده انتخاب شده هیچ محتوایی ندارد و نمیتوان مرتب کرد.',
|
||||
single: 'محدوده انتخاب شده فقط یک ردیف دارد و نمیتوان مرتب کرد.',
|
||||
'formula-array': 'محدوده انتخاب شده دارای فرمولهای آرایهای است و نمیتوان مرتب کرد.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'یادآوری مرتبسازی',
|
||||
'sort-reminder-desc': 'گسترش مرتبسازی محدوده یا حفظ مرتبسازی محدوده؟',
|
||||
'sort-reminder-ext': 'گسترش مرتبسازی محدوده',
|
||||
'sort-reminder-no': 'حفظ مرتبسازی محدوده',
|
||||
'first-row-check': 'ردیف اول در مرتبسازی شرکت نمیکند',
|
||||
'add-condition': 'افزودن شرط',
|
||||
'delete-condition': 'حذف شرط',
|
||||
cancel: 'انصراف',
|
||||
confirm: 'تایید',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'راهنما',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/fr-FR.ts
Normal file
54
packages/sheets-sort-ui/src/locale/fr-FR.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Trier',
|
||||
'sort-asc': 'Croissant',
|
||||
'sort-desc': 'Décroissant',
|
||||
'sort-custom': 'Tri personnalisé',
|
||||
'sort-asc-ext': 'Étendre croissant',
|
||||
'sort-desc-ext': 'Étendre décroissant',
|
||||
'sort-asc-cur': 'Croissant',
|
||||
'sort-desc-cur': 'Décroissant',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'La plage sélectionnée contient des cellules fusionnées de tailles différentes, qui ne peuvent pas être triées.',
|
||||
empty: 'La plage sélectionnée ne contient aucun contenu et ne peut pas être triée.',
|
||||
single: 'La plage sélectionnée ne contient qu\'une seule ligne et ne peut pas être triée.',
|
||||
'formula-array': 'La plage sélectionnée contient des formules matricielles et ne peut pas être triée.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Rappel de tri',
|
||||
'sort-reminder-desc': 'Étendre le tri de la plage ou conserver le tri de la plage?',
|
||||
'sort-reminder-ext': 'Étendre le tri de la plage',
|
||||
'sort-reminder-no': 'Conserver le tri de la plage',
|
||||
'first-row-check': 'La première ligne ne participe pas au tri',
|
||||
'add-condition': 'Ajouter une condition',
|
||||
'delete-condition': 'Supprimer la condition',
|
||||
cancel: 'Annuler',
|
||||
confirm: 'Confirmer',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Info-bulle',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/id-ID.ts
Normal file
54
packages/sheets-sort-ui/src/locale/id-ID.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Urutkan',
|
||||
'sort-asc': 'Menaik',
|
||||
'sort-desc': 'Menurun',
|
||||
'sort-custom': 'Urutan Kustom',
|
||||
'sort-asc-ext': 'Perluas Menaik',
|
||||
'sort-desc-ext': 'Perluas Menurun',
|
||||
'sort-asc-cur': 'Menaik',
|
||||
'sort-desc-cur': 'Menurun',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'Rentang yang dipilih mengandung sel yang digabungkan dengan ukuran berbeda, yang tidak dapat diurutkan.',
|
||||
empty: 'Rentang yang dipilih tidak memiliki konten dan tidak dapat diurutkan.',
|
||||
single: 'Rentang yang dipilih hanya memiliki satu baris dan tidak dapat diurutkan.',
|
||||
'formula-array': 'Rentang yang dipilih memiliki rumus array dan tidak dapat diurutkan.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Pengingat Urutan',
|
||||
'sort-reminder-desc': 'Perluas pengurutan rentang atau pertahankan pengurutan rentang?',
|
||||
'sort-reminder-ext': 'Perluas pengurutan rentang',
|
||||
'sort-reminder-no': 'Pertahankan pengurutan rentang',
|
||||
'first-row-check': 'Baris pertama tidak ikut dalam pengurutan',
|
||||
'add-condition': 'Tambah kondisi',
|
||||
'delete-condition': 'Hapus kondisi',
|
||||
cancel: 'Batal',
|
||||
confirm: 'Konfirmasi',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Tooltip',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/it-IT.ts
Normal file
54
packages/sheets-sort-ui/src/locale/it-IT.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Ordinamento',
|
||||
'sort-asc': 'Crescente',
|
||||
'sort-desc': 'Decrescente',
|
||||
'sort-custom': 'Ordinamento personalizzato',
|
||||
'sort-asc-ext': 'Espandi crescente',
|
||||
'sort-desc-ext': 'Espandi decrescente',
|
||||
'sort-asc-cur': 'Crescente',
|
||||
'sort-desc-cur': 'Decrescente',
|
||||
},
|
||||
error: {
|
||||
'merge-size': "L'intervallo selezionato contiene celle unite di dimensioni diverse e non può essere ordinato.",
|
||||
empty: "L'intervallo selezionato non ha contenuto e non può essere ordinato.",
|
||||
single: "L'intervallo selezionato ha una sola riga e non può essere ordinato.",
|
||||
'formula-array': "L'intervallo selezionato contiene formule matriciali e non può essere ordinato.",
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Promemoria ordinamento',
|
||||
'sort-reminder-desc': 'Estendere l\'ordinamento all\'intervallo o mantenerlo?',
|
||||
'sort-reminder-ext': 'Estendi ordinamento intervallo',
|
||||
'sort-reminder-no': 'Mantieni ordinamento intervallo',
|
||||
'first-row-check': 'La prima riga non partecipa all\'ordinamento',
|
||||
'add-condition': 'Aggiungi condizione',
|
||||
'delete-condition': 'Elimina condizione',
|
||||
cancel: 'Annulla',
|
||||
confirm: 'Conferma',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Suggerimento',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/ja-JP.ts
Normal file
54
packages/sheets-sort-ui/src/locale/ja-JP.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: '並べ替え',
|
||||
'sort-asc': '昇順',
|
||||
'sort-desc': '降順',
|
||||
'sort-custom': 'カスタム順序',
|
||||
'sort-asc-ext': '昇順(拡張)',
|
||||
'sort-desc-ext': '降順(拡張)',
|
||||
'sort-asc-cur': '昇順',
|
||||
'sort-desc-cur': '降順',
|
||||
},
|
||||
error: {
|
||||
'merge-size': '選択範囲に異なるサイズの結合セルが含まれているため、並べ替えができません。',
|
||||
empty: '選択範囲にデータがないため、並べ替えができません。',
|
||||
single: '選択範囲に1行しか含まれていないため、並べ替えができません。',
|
||||
'formula-array': '選択範囲に配列数式が含まれているため、並べ替えができません。',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': '並べ替えの確認',
|
||||
'sort-reminder-desc': '範囲を拡張して並べ替えますか、それとも範囲を維持して並べ替えますか?',
|
||||
'sort-reminder-ext': '範囲を拡張して並べ替え',
|
||||
'sort-reminder-no': '範囲を維持して並べ替え',
|
||||
'first-row-check': '先頭行を並べ替えの対象に含めない',
|
||||
'add-condition': '条件を追加',
|
||||
'delete-condition': '条件を削除',
|
||||
cancel: 'キャンセル',
|
||||
confirm: '確認',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'ツールチップ',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/ko-KR.ts
Normal file
54
packages/sheets-sort-ui/src/locale/ko-KR.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: '정렬',
|
||||
'sort-asc': '오름차순',
|
||||
'sort-desc': '내림차순',
|
||||
'sort-custom': '사용자 정의 순서',
|
||||
'sort-asc-ext': '오름차순 확장',
|
||||
'sort-desc-ext': '내림차순 확장',
|
||||
'sort-asc-cur': '오름차순',
|
||||
'sort-desc-cur': '내림차순',
|
||||
},
|
||||
error: {
|
||||
'merge-size': '선택한 범위에 서로 다른 크기의 병합 셀이 있어 정렬할 수 없습니다.',
|
||||
empty: '선택한 범위에 내용이 없어 정렬할 수 없습니다.',
|
||||
single: '선택한 범위에 한 행만 있어 정렬할 수 없습니다.',
|
||||
'formula-array': '선택한 범위에 배열 수식이 있어 정렬할 수 없습니다.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': '정렬 알림',
|
||||
'sort-reminder-desc': '범위 정렬 확장 또는 범위 정렬 유지?',
|
||||
'sort-reminder-ext': '범위 정렬 확장',
|
||||
'sort-reminder-no': '범위 정렬 유지',
|
||||
'first-row-check': '첫 번째 행은 정렬에 참여하지 않습니다',
|
||||
'add-condition': '조건 추가',
|
||||
'delete-condition': '조건 삭제',
|
||||
cancel: '취소',
|
||||
confirm: '확인',
|
||||
},
|
||||
info: {
|
||||
tooltip: '툴팁',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/pl-PL.ts
Normal file
54
packages/sheets-sort-ui/src/locale/pl-PL.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Sortuj',
|
||||
'sort-asc': 'Rosnąco',
|
||||
'sort-desc': 'Malejąco',
|
||||
'sort-custom': 'Sortowanie niestandardowe',
|
||||
'sort-asc-ext': 'Rozszerz rosnąco',
|
||||
'sort-desc-ext': 'Rozszerz malejąco',
|
||||
'sort-asc-cur': 'Rosnąco',
|
||||
'sort-desc-cur': 'Malejąco',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'Wybrany zakres zawiera scalone komórki o różnych rozmiarach, których nie można sortować.',
|
||||
empty: 'Wybrany zakres nie zawiera treści i nie można go sortować.',
|
||||
single: 'Wybrany zakres ma tylko jeden wiersz i nie można go sortować.',
|
||||
'formula-array': 'Wybrany zakres zawiera formuły tablicowe i nie można go sortować.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Przypomnienie o sortowaniu',
|
||||
'sort-reminder-desc': 'Rozszerzyć sortowanie zakresu czy zachować sortowanie zakresu?',
|
||||
'sort-reminder-ext': 'Rozszerz sortowanie zakresu',
|
||||
'sort-reminder-no': 'Zachowaj sortowanie zakresu',
|
||||
'first-row-check': 'Pierwszy wiersz nie bierze udziału w sortowaniu',
|
||||
'add-condition': 'Dodaj warunek',
|
||||
'delete-condition': 'Usuń warunek',
|
||||
cancel: 'Anuluj',
|
||||
confirm: 'Potwierdź',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Podpowiedź',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/pt-BR.ts
Normal file
54
packages/sheets-sort-ui/src/locale/pt-BR.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Ordenar',
|
||||
'sort-asc': 'Crescente',
|
||||
'sort-desc': 'Decrescente',
|
||||
'sort-custom': 'Ordenação Personalizada',
|
||||
'sort-asc-ext': 'Expandir Crescente',
|
||||
'sort-desc-ext': 'Expandir Decrescente',
|
||||
'sort-asc-cur': 'Crescente',
|
||||
'sort-desc-cur': 'Decrescente',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'O intervalo selecionado contém células mescladas de tamanhos diferentes e não pode ser ordenado.',
|
||||
empty: 'O intervalo selecionado não tem conteúdo e não pode ser ordenado.',
|
||||
single: 'O intervalo selecionado tem apenas uma linha e não pode ser ordenado.',
|
||||
'formula-array': 'O intervalo selecionado possui fórmulas de matriz e não pode ser ordenado.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Lembrete de Ordenação',
|
||||
'sort-reminder-desc': 'Expandir a ordenação do intervalo ou manter a ordenação do intervalo?',
|
||||
'sort-reminder-ext': 'Expandir ordenação do intervalo',
|
||||
'sort-reminder-no': 'Manter ordenação do intervalo',
|
||||
'first-row-check': 'A primeira linha não participa da ordenação',
|
||||
'add-condition': 'Adicionar condição',
|
||||
'delete-condition': 'Excluir condição',
|
||||
cancel: 'Cancelar',
|
||||
confirm: 'Confirmar',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Dica',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/ru-RU.ts
Normal file
54
packages/sheets-sort-ui/src/locale/ru-RU.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Сортировка',
|
||||
'sort-asc': 'По возрастанию',
|
||||
'sort-desc': 'По убыванию',
|
||||
'sort-custom': 'Пользовательская сортировка',
|
||||
'sort-asc-ext': 'Расширенная сортировка по возрастанию',
|
||||
'sort-desc-ext': 'Расширенная сортировка по убыванию',
|
||||
'sort-asc-cur': 'По возрастанию',
|
||||
'sort-desc-cur': 'По убыванию',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'Выбранный диапазон содержит объединенные ячейки разного размера, которые нельзя отсортировать.',
|
||||
empty: 'Выбранный диапазон не содержит данных и не может быть отсортирован.',
|
||||
single: 'Выбранный диапазон содержит только одну строку и не может быть отсортирован.',
|
||||
'formula-array': 'Выбранный диапазон содержит массив формул и не может быть отсортирован.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Напоминание о сортировке',
|
||||
'sort-reminder-desc': 'Расширить сортировку диапазона или оставить сортировку диапазона?',
|
||||
'sort-reminder-ext': 'Расширить сортировку диапазона',
|
||||
'sort-reminder-no': 'Оставить сортировку диапазона',
|
||||
'first-row-check': 'Первая строка контента не участвует в сортировке',
|
||||
'add-condition': 'Добавить условие',
|
||||
'delete-condition': 'Удалить условие',
|
||||
cancel: 'Отмена',
|
||||
confirm: 'Подтвердить',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Подсказка',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/sk-SK.ts
Normal file
54
packages/sheets-sort-ui/src/locale/sk-SK.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Triediť',
|
||||
'sort-asc': 'Vzostupne',
|
||||
'sort-desc': 'Zostupne',
|
||||
'sort-custom': 'Vlastné triedenie',
|
||||
'sort-asc-ext': 'Rozšíriť vzostupne',
|
||||
'sort-desc-ext': 'Rozšíriť zostupne',
|
||||
'sort-asc-cur': 'Vzostupne',
|
||||
'sort-desc-cur': 'Zostupne',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'Vybraný rozsah obsahuje zlúčené bunky rôznych veľkostí, ktoré nie je možné triediť.',
|
||||
empty: 'Vybraný rozsah nemá žiadny obsah a nedá sa triediť.',
|
||||
single: 'Vybraný rozsah má iba jeden riadok a nedá sa triediť.',
|
||||
'formula-array': 'Vybraný rozsah obsahuje maticové vzorce a nedá sa triediť.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Upozornenie na triedenie',
|
||||
'sort-reminder-desc': 'Rozšíriť triedenie rozsahu alebo zachovať triedenie rozsahu?',
|
||||
'sort-reminder-ext': 'Rozšíriť triedenie rozsahu',
|
||||
'sort-reminder-no': 'Zachovať triedenie rozsahu',
|
||||
'first-row-check': 'Prvý riadok sa nezúčastňuje triedenia',
|
||||
'add-condition': 'Pridať podmienku',
|
||||
'delete-condition': 'Odstrániť podmienku',
|
||||
cancel: 'Zrušiť',
|
||||
confirm: 'Potvrdiť',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Bublinový tip',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
20
packages/sheets-sort-ui/src/locale/types.ts
Normal file
20
packages/sheets-sort-ui/src/locale/types.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { LocaleLeafKeys } from '@univerjs/core';
|
||||
import type enUS from './en-US';
|
||||
|
||||
export type LocaleKey = LocaleLeafKeys<typeof enUS>;
|
||||
55
packages/sheets-sort-ui/src/locale/vi-VN.ts
Normal file
55
packages/sheets-sort-ui/src/locale/vi-VN.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: 'Sắp xếp',
|
||||
'sort-asc': 'Tăng dần',
|
||||
'sort-desc': 'Giảm dần',
|
||||
'sort-custom': 'Tùy chỉnh sắp xếp',
|
||||
'sort-asc-ext': 'Tăng dần khu vực mở rộng',
|
||||
'sort-desc-ext': 'Giảm dần khu vực mở rộng',
|
||||
'sort-asc-cur': 'Tăng dần khu vực hiện tại',
|
||||
'sort-desc-cur': 'Giảm dần khu vực hiện tại',
|
||||
},
|
||||
error: {
|
||||
'merge-size': 'Kích thước của các ô đã hợp nhất trong vùng được chọn không đồng nhất, không thể sắp xếp.',
|
||||
empty: 'Vùng được chọn không có nội dung, không thể sắp xếp.',
|
||||
single: 'Vùng được chọn chỉ có một hàng, không thể sắp xếp.',
|
||||
'formula-array': 'Vùng được chọn chứa công thức mảng, không thể sắp xếp.',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': 'Nhắc nhở sắp xếp',
|
||||
'sort-reminder-desc': 'Hiện tại chỉ sắp xếp vùng được chọn, có muốn mở rộng phạm vi sắp xếp không?',
|
||||
'sort-reminder-ext': 'Mở rộng phạm vi sắp xếp',
|
||||
'sort-reminder-no': 'Giữ nguyên phạm vi sắp xếp được chọn',
|
||||
'first-row-check': 'Hàng đầu tiên không tham gia sắp xếp',
|
||||
'add-condition': 'Thêm điều kiện sắp xếp',
|
||||
'delete-condition': 'Xóa điều kiện',
|
||||
cancel: 'Hủy bỏ',
|
||||
confirm: 'Xác nhận',
|
||||
},
|
||||
info: {
|
||||
tooltip: 'Tooltip',
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/zh-CN.ts
Normal file
54
packages/sheets-sort-ui/src/locale/zh-CN.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: '排序',
|
||||
'sort-asc': '升序',
|
||||
'sort-desc': '降序',
|
||||
'sort-custom': '自定义排序',
|
||||
'sort-asc-ext': '拓展区域升序',
|
||||
'sort-desc-ext': '拓展区域降序',
|
||||
'sort-asc-cur': '当前区域升序',
|
||||
'sort-desc-cur': '当前区域降序',
|
||||
},
|
||||
error: {
|
||||
'merge-size': '所选区域的合并单元格的大小不一致,无法排序。',
|
||||
empty: '所选区域无内容,无法排序。',
|
||||
single: '所选区域仅有一行,无法排序。',
|
||||
'formula-array': '所选区域含数组公式,无法排序。',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': '排序提醒',
|
||||
'sort-reminder-desc': '当前仅会对选中区域进行排序,是否拓展排序范围?',
|
||||
'sort-reminder-ext': '拓展排序范围',
|
||||
'sort-reminder-no': '保持所选排序范围',
|
||||
'first-row-check': '标题不参与排序',
|
||||
'add-condition': '添加排序条件',
|
||||
'delete-condition': '删除排序条件',
|
||||
cancel: '取消',
|
||||
confirm: '确认',
|
||||
},
|
||||
info: {
|
||||
tooltip: '提示',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/zh-HK.ts
Normal file
54
packages/sheets-sort-ui/src/locale/zh-HK.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: '排序',
|
||||
'sort-asc': '升序',
|
||||
'sort-desc': '降序',
|
||||
'sort-custom': '自訂排序',
|
||||
'sort-asc-ext': '拓展區域升序',
|
||||
'sort-desc-ext': '拓展區域降序',
|
||||
'sort-asc-cur': '目前區域升序',
|
||||
'sort-desc-cur': '當前區域降序',
|
||||
},
|
||||
error: {
|
||||
'merge-size': '所選區域的合併儲存格的大小不一致,無法排序。 ',
|
||||
empty: '所選區域無內容,無法排序。 ',
|
||||
single: '所選區域只有一列,無法排序。 ',
|
||||
'formula-array': '所選區域含陣列公式,無法排序。 ',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': '排序提醒',
|
||||
'sort-reminder-desc': '目前只會對選取區域進行排序,是否拓展排序範圍? ',
|
||||
'sort-reminder-ext': '拓展排序範圍',
|
||||
'sort-reminder-no': '保持所選排序範圍',
|
||||
'first-row-check': '標題不參與排序',
|
||||
'add-condition': '新增排序條件',
|
||||
'delete-condition': '刪除排序條件',
|
||||
cancel: '取消',
|
||||
confirm: '確認',
|
||||
},
|
||||
info: {
|
||||
tooltip: '提示',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
54
packages/sheets-sort-ui/src/locale/zh-TW.ts
Normal file
54
packages/sheets-sort-ui/src/locale/zh-TW.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type enUS from './en-US';
|
||||
|
||||
const locale: typeof enUS = {
|
||||
'sheets-sort-ui': {
|
||||
general: {
|
||||
sort: '排序',
|
||||
'sort-asc': '升序',
|
||||
'sort-desc': '降序',
|
||||
'sort-custom': '自訂排序',
|
||||
'sort-asc-ext': '拓展區域升序',
|
||||
'sort-desc-ext': '拓展區域降序',
|
||||
'sort-asc-cur': '目前區域升序',
|
||||
'sort-desc-cur': '當前區域降序',
|
||||
},
|
||||
error: {
|
||||
'merge-size': '所選區域的合併儲存格的大小不一致,無法排序。 ',
|
||||
empty: '所選區域無內容,無法排序。 ',
|
||||
single: '所選區域只有一列,無法排序。 ',
|
||||
'formula-array': '所選區域含陣列公式,無法排序。 ',
|
||||
},
|
||||
dialog: {
|
||||
'sort-reminder': '排序提醒',
|
||||
'sort-reminder-desc': '目前只會對選取區域進行排序,是否拓展排序範圍? ',
|
||||
'sort-reminder-ext': '拓展排序範圍',
|
||||
'sort-reminder-no': '保持所選排序範圍',
|
||||
'first-row-check': '標題不參與排序',
|
||||
'add-condition': '新增排序條件',
|
||||
'delete-condition': '刪除排序條件',
|
||||
cancel: '取消',
|
||||
confirm: '確認',
|
||||
},
|
||||
info: {
|
||||
tooltip: '提示',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
103
packages/sheets-sort-ui/src/menu/mobile-schema.ts
Normal file
103
packages/sheets-sort-ui/src/menu/mobile-schema.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { MenuSchemaType } from '@univerjs/ui';
|
||||
import { ContextMenuGroup, ContextMenuPosition, RibbonDataGroup } from '@univerjs/ui';
|
||||
import {
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
} from '../commands/commands/sheets-sort.command';
|
||||
import {
|
||||
SHEETS_SORT_CTX_MENU_ID,
|
||||
SHEETS_SORT_MENU_ID,
|
||||
sortRangeAscCtxMenuFactory,
|
||||
sortRangeAscExtCtxMenuFactory,
|
||||
sortRangeAscExtMenuFactory,
|
||||
sortRangeAscMenuFactory,
|
||||
sortRangeCtxMenuFactory,
|
||||
sortRangeCustomCtxMenuFactory,
|
||||
sortRangeCustomMenuFactory,
|
||||
sortRangeDescCtxMenuFactory,
|
||||
sortRangeDescExtCtxMenuFactory,
|
||||
sortRangeDescExtMenuFactory,
|
||||
sortRangeDescMenuFactory,
|
||||
sortRangeMenuFactory,
|
||||
} from './sheets-sort.menu';
|
||||
|
||||
export const mobileMenuSchema: MenuSchemaType = {
|
||||
[RibbonDataGroup.ORGANIZATION]: {
|
||||
[SHEETS_SORT_MENU_ID]: {
|
||||
order: 3,
|
||||
menuItemFactory: sortRangeMenuFactory,
|
||||
[SortRangeAscCommand.id]: {
|
||||
order: 0,
|
||||
menuItemFactory: sortRangeAscMenuFactory,
|
||||
},
|
||||
[SortRangeAscExtCommand.id]: {
|
||||
order: 1,
|
||||
menuItemFactory: sortRangeAscExtMenuFactory,
|
||||
},
|
||||
[SortRangeDescCommand.id]: {
|
||||
order: 2,
|
||||
menuItemFactory: sortRangeDescMenuFactory,
|
||||
},
|
||||
[SortRangeDescExtCommand.id]: {
|
||||
order: 3,
|
||||
menuItemFactory: sortRangeDescExtMenuFactory,
|
||||
},
|
||||
[SortRangeCustomCommand.id]: {
|
||||
order: 4,
|
||||
menuItemFactory: sortRangeCustomMenuFactory,
|
||||
},
|
||||
},
|
||||
},
|
||||
[ContextMenuPosition.MAIN_AREA]: {
|
||||
[ContextMenuGroup.DATA]: {
|
||||
[SHEETS_SORT_CTX_MENU_ID]: {
|
||||
order: 0,
|
||||
menuItemFactory: sortRangeCtxMenuFactory,
|
||||
[SortRangeAscInCtxMenuCommand.id]: {
|
||||
order: 0,
|
||||
menuItemFactory: sortRangeAscCtxMenuFactory,
|
||||
},
|
||||
[SortRangeAscExtInCtxMenuCommand.id]: {
|
||||
order: 1,
|
||||
menuItemFactory: sortRangeAscExtCtxMenuFactory,
|
||||
},
|
||||
[SortRangeDescInCtxMenuCommand.id]: {
|
||||
order: 2,
|
||||
menuItemFactory: sortRangeDescCtxMenuFactory,
|
||||
},
|
||||
[SortRangeDescExtInCtxMenuCommand.id]: {
|
||||
order: 3,
|
||||
menuItemFactory: sortRangeDescExtCtxMenuFactory,
|
||||
},
|
||||
[SortRangeCustomInCtxMenuCommand.id]: {
|
||||
order: 4,
|
||||
menuItemFactory: sortRangeCustomCtxMenuFactory,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
104
packages/sheets-sort-ui/src/menu/schema.ts
Normal file
104
packages/sheets-sort-ui/src/menu/schema.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { MenuSchemaType } from '@univerjs/ui';
|
||||
import { ContextMenuGroup, ContextMenuPosition, RibbonDataGroup } from '@univerjs/ui';
|
||||
import {
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
} from '../commands/commands/sheets-sort.command';
|
||||
import {
|
||||
SHEETS_SORT_CTX_MENU_ID,
|
||||
SHEETS_SORT_MENU_ID,
|
||||
sortRangeAscCtxMenuFactory,
|
||||
sortRangeAscExtCtxMenuFactory,
|
||||
sortRangeAscExtMenuFactory,
|
||||
sortRangeAscMenuFactory,
|
||||
sortRangeCtxMenuFactory,
|
||||
sortRangeCustomCtxMenuFactory,
|
||||
sortRangeCustomMenuFactory,
|
||||
sortRangeDescCtxMenuFactory,
|
||||
sortRangeDescExtCtxMenuFactory,
|
||||
sortRangeDescExtMenuFactory,
|
||||
sortRangeDescMenuFactory,
|
||||
sortRangeMenuFactory,
|
||||
} from './sheets-sort.menu';
|
||||
|
||||
export const menuSchema: MenuSchemaType = {
|
||||
[RibbonDataGroup.ORGANIZATION]: {
|
||||
[SHEETS_SORT_MENU_ID]: {
|
||||
order: 3,
|
||||
gridLayout: { row: 2, column: 2 },
|
||||
menuItemFactory: sortRangeMenuFactory,
|
||||
[SortRangeAscCommand.id]: {
|
||||
order: 0,
|
||||
menuItemFactory: sortRangeAscMenuFactory,
|
||||
},
|
||||
[SortRangeAscExtCommand.id]: {
|
||||
order: 1,
|
||||
menuItemFactory: sortRangeAscExtMenuFactory,
|
||||
},
|
||||
[SortRangeDescCommand.id]: {
|
||||
order: 2,
|
||||
menuItemFactory: sortRangeDescMenuFactory,
|
||||
},
|
||||
[SortRangeDescExtCommand.id]: {
|
||||
order: 3,
|
||||
menuItemFactory: sortRangeDescExtMenuFactory,
|
||||
},
|
||||
[SortRangeCustomCommand.id]: {
|
||||
order: 4,
|
||||
menuItemFactory: sortRangeCustomMenuFactory,
|
||||
},
|
||||
},
|
||||
},
|
||||
[ContextMenuPosition.MAIN_AREA]: {
|
||||
[ContextMenuGroup.DATA]: {
|
||||
[SHEETS_SORT_CTX_MENU_ID]: {
|
||||
order: 0,
|
||||
menuItemFactory: sortRangeCtxMenuFactory,
|
||||
[SortRangeAscInCtxMenuCommand.id]: {
|
||||
order: 0,
|
||||
menuItemFactory: sortRangeAscCtxMenuFactory,
|
||||
},
|
||||
[SortRangeAscExtInCtxMenuCommand.id]: {
|
||||
order: 1,
|
||||
menuItemFactory: sortRangeAscExtCtxMenuFactory,
|
||||
},
|
||||
[SortRangeDescInCtxMenuCommand.id]: {
|
||||
order: 2,
|
||||
menuItemFactory: sortRangeDescCtxMenuFactory,
|
||||
},
|
||||
[SortRangeDescExtInCtxMenuCommand.id]: {
|
||||
order: 3,
|
||||
menuItemFactory: sortRangeDescExtCtxMenuFactory,
|
||||
},
|
||||
[SortRangeCustomInCtxMenuCommand.id]: {
|
||||
order: 4,
|
||||
menuItemFactory: sortRangeCustomCtxMenuFactory,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
189
packages/sheets-sort-ui/src/menu/sheets-sort.menu.ts
Normal file
189
packages/sheets-sort-ui/src/menu/sheets-sort.menu.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { IAccessor } from '@univerjs/core';
|
||||
import type { IMenuItem } from '@univerjs/ui';
|
||||
import type { LocaleKey } from '../locale/types';
|
||||
import { UniverInstanceType } from '@univerjs/core';
|
||||
import {
|
||||
RangeProtectionPermissionEditPoint,
|
||||
WorkbookEditablePermission,
|
||||
WorksheetEditPermission,
|
||||
WorksheetSortPermission,
|
||||
} from '@univerjs/sheets';
|
||||
import { getCurrentExclusiveRangeInterest$, getCurrentRangeDisable$ } from '@univerjs/sheets-ui';
|
||||
import { getMenuHiddenObservable, MenuItemType } from '@univerjs/ui';
|
||||
import {
|
||||
SortRangeAscCommand,
|
||||
SortRangeAscExtCommand,
|
||||
SortRangeAscExtInCtxMenuCommand,
|
||||
SortRangeAscInCtxMenuCommand,
|
||||
SortRangeCustomCommand,
|
||||
SortRangeCustomInCtxMenuCommand,
|
||||
SortRangeDescCommand,
|
||||
SortRangeDescExtCommand,
|
||||
SortRangeDescExtInCtxMenuCommand,
|
||||
SortRangeDescInCtxMenuCommand,
|
||||
} from '../commands/commands/sheets-sort.command';
|
||||
|
||||
export const SHEETS_SORT_MENU_ID = 'sheet.menu.sheets-sort';
|
||||
export const SHEETS_SORT_CTX_MENU_ID = 'sheet.menu.sheets-sort-ctx';
|
||||
|
||||
export function sortRangeMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SHEETS_SORT_MENU_ID,
|
||||
type: MenuItemType.SUBITEMS,
|
||||
icon: 'AscendingIcon',
|
||||
tooltip: 'sheets-sort-ui.general.sort',
|
||||
hidden$: getMenuHiddenObservable(accessor, UniverInstanceType.UNIVER_SHEET),
|
||||
disabled$: getCurrentRangeDisable$(accessor, {
|
||||
workbookTypes: [WorkbookEditablePermission],
|
||||
worksheetTypes: [WorksheetSortPermission, WorksheetEditPermission],
|
||||
rangeTypes: [RangeProtectionPermissionEditPoint],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeAscMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeAscCommand.id,
|
||||
icon: 'AscendingIcon',
|
||||
title: 'sheets-sort-ui.general.sort-asc-cur',
|
||||
type: MenuItemType.BUTTON,
|
||||
hidden$: getCurrentExclusiveRangeInterest$(accessor),
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeAscExtMenuFactory(): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeAscExtCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-asc-ext',
|
||||
icon: 'ExpandAscendingIcon',
|
||||
type: MenuItemType.BUTTON,
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeDescMenuFactory(): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeDescCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-desc-cur',
|
||||
icon: 'DescendingIcon',
|
||||
type: MenuItemType.BUTTON,
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeDescExtMenuFactory(): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeDescExtCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-desc-ext',
|
||||
icon: 'ExpandDescendingIcon',
|
||||
type: MenuItemType.BUTTON,
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeCustomMenuFactory(): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeCustomCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-custom',
|
||||
type: MenuItemType.BUTTON,
|
||||
icon: 'CustomSortIcon',
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeCtxMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SHEETS_SORT_CTX_MENU_ID,
|
||||
title: 'sheets-sort-ui.general.sort',
|
||||
type: MenuItemType.SUBITEMS,
|
||||
icon: 'AscendingIcon',
|
||||
hidden$: getMenuHiddenObservable(accessor, UniverInstanceType.UNIVER_SHEET),
|
||||
disabled$: getCurrentRangeDisable$(accessor, {
|
||||
workbookTypes: [WorkbookEditablePermission],
|
||||
worksheetTypes: [WorksheetSortPermission, WorksheetEditPermission],
|
||||
rangeTypes: [RangeProtectionPermissionEditPoint],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeAscCtxMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeAscInCtxMenuCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-asc-cur',
|
||||
type: MenuItemType.BUTTON,
|
||||
icon: 'AscendingIcon',
|
||||
disabled$: getCurrentRangeDisable$(accessor, {
|
||||
workbookTypes: [WorkbookEditablePermission],
|
||||
worksheetTypes: [WorksheetSortPermission, WorksheetEditPermission],
|
||||
rangeTypes: [RangeProtectionPermissionEditPoint],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeAscExtCtxMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeAscExtInCtxMenuCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-asc-ext',
|
||||
type: MenuItemType.BUTTON,
|
||||
icon: 'ExpandAscendingIcon',
|
||||
disabled$: getCurrentRangeDisable$(accessor, {
|
||||
workbookTypes: [WorkbookEditablePermission],
|
||||
worksheetTypes: [WorksheetSortPermission, WorksheetEditPermission],
|
||||
rangeTypes: [RangeProtectionPermissionEditPoint],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeDescCtxMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeDescInCtxMenuCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-desc-cur',
|
||||
type: MenuItemType.BUTTON,
|
||||
icon: 'DescendingIcon',
|
||||
disabled$: getCurrentRangeDisable$(accessor, {
|
||||
workbookTypes: [WorkbookEditablePermission],
|
||||
worksheetTypes: [WorksheetSortPermission, WorksheetEditPermission],
|
||||
rangeTypes: [RangeProtectionPermissionEditPoint],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeDescExtCtxMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeDescExtInCtxMenuCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-desc-ext',
|
||||
type: MenuItemType.BUTTON,
|
||||
icon: 'ExpandDescendingIcon',
|
||||
disabled$: getCurrentRangeDisable$(accessor, {
|
||||
workbookTypes: [WorkbookEditablePermission],
|
||||
worksheetTypes: [WorksheetSortPermission, WorksheetEditPermission],
|
||||
rangeTypes: [RangeProtectionPermissionEditPoint],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function sortRangeCustomCtxMenuFactory(accessor: IAccessor): IMenuItem<LocaleKey> {
|
||||
return {
|
||||
id: SortRangeCustomInCtxMenuCommand.id,
|
||||
title: 'sheets-sort-ui.general.sort-custom',
|
||||
type: MenuItemType.BUTTON,
|
||||
icon: 'CustomSortIcon',
|
||||
disabled$: getCurrentRangeDisable$(accessor, {
|
||||
workbookTypes: [WorkbookEditablePermission],
|
||||
worksheetTypes: [WorksheetSortPermission, WorksheetEditPermission],
|
||||
rangeTypes: [RangeProtectionPermissionEditPoint],
|
||||
}),
|
||||
};
|
||||
}
|
||||
69
packages/sheets-sort-ui/src/mobile-plugin.ts
Normal file
69
packages/sheets-sort-ui/src/mobile-plugin.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { IUniverSheetsSortUIConfig } from './config/config';
|
||||
import {
|
||||
DependentOn,
|
||||
IConfigService,
|
||||
Inject,
|
||||
Injector,
|
||||
merge,
|
||||
Plugin,
|
||||
registerDependencies,
|
||||
touchDependencies,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { UniverSheetsPlugin } from '@univerjs/sheets';
|
||||
import { UniverSheetsSortPlugin } from '@univerjs/sheets-sort';
|
||||
import { UniverSheetsMobileUIPlugin } from '@univerjs/sheets-ui';
|
||||
import { UniverMobileUIPlugin } from '@univerjs/ui';
|
||||
import pkg from '../package.json';
|
||||
import { defaultPluginConfig, SHEETS_SORT_UI_PLUGIN_CONFIG_KEY } from './config/config';
|
||||
import { MobileComponentsController } from './controllers/mobile/components.controller';
|
||||
import { SheetsSortMobileUIController } from './controllers/mobile/ui.controller';
|
||||
import { SheetsSortUIService } from './services/sheets-sort-ui.service';
|
||||
|
||||
@DependentOn(UniverSheetsPlugin, UniverSheetsSortPlugin, UniverSheetsMobileUIPlugin, UniverMobileUIPlugin)
|
||||
export class UniverSheetsSortMobileUIPlugin extends Plugin {
|
||||
static override type = UniverInstanceType.UNIVER_SHEET;
|
||||
static override pluginName = 'SHEET_SORT_UI_PLUGIN';
|
||||
static override packageName = pkg.name;
|
||||
static override version = pkg.version;
|
||||
|
||||
constructor(
|
||||
private readonly _config: Partial<IUniverSheetsSortUIConfig> = defaultPluginConfig,
|
||||
@Inject(Injector) protected override readonly _injector: Injector,
|
||||
@IConfigService private readonly _configService: IConfigService
|
||||
) {
|
||||
super();
|
||||
|
||||
const { ...rest } = merge({}, defaultPluginConfig, this._config);
|
||||
this._configService.setConfig(SHEETS_SORT_UI_PLUGIN_CONFIG_KEY, rest);
|
||||
}
|
||||
|
||||
override onStarting(): void {
|
||||
registerDependencies(this._injector, [
|
||||
[MobileComponentsController],
|
||||
[SheetsSortUIService],
|
||||
[SheetsSortMobileUIController],
|
||||
]);
|
||||
touchDependencies(this._injector, [[MobileComponentsController]]);
|
||||
}
|
||||
|
||||
override onRendered(): void {
|
||||
touchDependencies(this._injector, [[SheetsSortMobileUIController]]);
|
||||
}
|
||||
}
|
||||
72
packages/sheets-sort-ui/src/plugin.ts
Normal file
72
packages/sheets-sort-ui/src/plugin.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { Dependency } from '@univerjs/core';
|
||||
import type { IUniverSheetsSortUIConfig } from './config/config';
|
||||
import {
|
||||
DependentOn,
|
||||
IConfigService,
|
||||
Inject,
|
||||
Injector,
|
||||
merge,
|
||||
Plugin,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { UniverSheetsPlugin } from '@univerjs/sheets';
|
||||
import { UniverSheetsSortPlugin } from '@univerjs/sheets-sort';
|
||||
import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
|
||||
import pkg from '../package.json';
|
||||
import { defaultPluginConfig, SHEETS_SORT_UI_PLUGIN_CONFIG_KEY } from './config/config';
|
||||
import { ComponentsController } from './controllers/components.controller';
|
||||
import { SheetsSortUIController } from './controllers/ui.controller';
|
||||
import { SheetsSortUIService } from './services/sheets-sort-ui.service';
|
||||
|
||||
@DependentOn(UniverSheetsPlugin, UniverSheetsSortPlugin, UniverSheetsUIPlugin)
|
||||
export class UniverSheetsSortUIPlugin extends Plugin {
|
||||
static override type = UniverInstanceType.UNIVER_SHEET;
|
||||
static override pluginName = 'SHEET_SORT_UI_PLUGIN';
|
||||
static override packageName = pkg.name;
|
||||
static override version = pkg.version;
|
||||
|
||||
constructor(
|
||||
private readonly _config: Partial<IUniverSheetsSortUIConfig> = defaultPluginConfig,
|
||||
@Inject(Injector) protected readonly _injector: Injector,
|
||||
@IConfigService private readonly _configService: IConfigService
|
||||
) {
|
||||
super();
|
||||
|
||||
// Manage the plugin configuration.
|
||||
const { ...rest } = merge(
|
||||
{},
|
||||
defaultPluginConfig,
|
||||
this._config
|
||||
);
|
||||
this._configService.setConfig(SHEETS_SORT_UI_PLUGIN_CONFIG_KEY, rest);
|
||||
}
|
||||
|
||||
override onStarting(): void {
|
||||
this._injector.add([ComponentsController]);
|
||||
this._injector.get(ComponentsController);
|
||||
([
|
||||
[SheetsSortUIService],
|
||||
[SheetsSortUIController],
|
||||
] as Dependency[]).forEach((d) => this._injector.add(d));
|
||||
}
|
||||
|
||||
override onRendered(): void {
|
||||
this._injector.get(SheetsSortUIController);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type {
|
||||
ICommand,
|
||||
} from '@univerjs/core';
|
||||
import {
|
||||
CommandService,
|
||||
ConfigService,
|
||||
ContextService,
|
||||
DesktopLogService,
|
||||
Disposable,
|
||||
ICommandService,
|
||||
IConfigService,
|
||||
IConfirmService,
|
||||
IContextService,
|
||||
ILogService,
|
||||
Injector,
|
||||
IUniverInstanceService,
|
||||
LocaleService,
|
||||
RANGE_TYPE,
|
||||
TestConfirmService,
|
||||
UniverInstanceService,
|
||||
Workbook,
|
||||
} from '@univerjs/core';
|
||||
import { FormulaDataModel, LexerTreeBuilder } from '@univerjs/engine-formula';
|
||||
import { SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { SheetsSortService, SortType } from '@univerjs/sheets-sort';
|
||||
import { Subject } from 'rxjs';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import zhCN from '../../locale/zh-CN';
|
||||
import { EXTEND_TYPE, SheetsSortUIService } from '../sheets-sort-ui.service';
|
||||
|
||||
class RecordingCommandService {
|
||||
readonly commands: Array<{ id: string; params?: unknown }> = [];
|
||||
|
||||
executeCommand(id: string, params?: unknown): Promise<boolean> {
|
||||
this.commands.push({ id, params });
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
registerCommand(_command: ICommand): Disposable {
|
||||
return new Disposable();
|
||||
}
|
||||
}
|
||||
|
||||
class RecordingConfirmService {
|
||||
readonly confirmOptions$ = new Subject<unknown[]>();
|
||||
readonly confirmations: unknown[] = [];
|
||||
result = true;
|
||||
extendType = EXTEND_TYPE.KEEP;
|
||||
|
||||
open(params: unknown): Disposable {
|
||||
this.confirmations.push(params);
|
||||
return new Disposable();
|
||||
}
|
||||
|
||||
confirm(params: { children?: { title?: { props?: { onChange?: (value: string) => void } } } }): Promise<boolean> {
|
||||
this.confirmations.push(params);
|
||||
if (this.extendType === EXTEND_TYPE.EXTEND) {
|
||||
params.children?.title?.props?.onChange?.('1');
|
||||
}
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
close(_id: string): void {}
|
||||
}
|
||||
|
||||
class RecordingSheetsSortService {
|
||||
readonly appliedSorts: Array<{ option: unknown; unitId?: string; subUnitId?: string }> = [];
|
||||
failedCheck: 'single' | 'merge' | 'formula' | 'empty' | null = null;
|
||||
|
||||
singleCheck(): boolean {
|
||||
return this.failedCheck !== 'single';
|
||||
}
|
||||
|
||||
mergeCheck(): boolean {
|
||||
return this.failedCheck !== 'merge';
|
||||
}
|
||||
|
||||
formulaCheck(): boolean {
|
||||
return this.failedCheck !== 'formula';
|
||||
}
|
||||
|
||||
emptyCheck(): boolean {
|
||||
return this.failedCheck !== 'empty';
|
||||
}
|
||||
|
||||
applySort(option: unknown, unitId?: string, subUnitId?: string): void {
|
||||
this.appliedSorts.push({ option, unitId, subUnitId });
|
||||
}
|
||||
}
|
||||
|
||||
function createService(useRecordingServices = false) {
|
||||
const injector = new Injector();
|
||||
injector.add([ILogService, { useClass: DesktopLogService }]);
|
||||
injector.add([IConfigService, { useClass: ConfigService }]);
|
||||
injector.add([IContextService, { useClass: ContextService }]);
|
||||
injector.add([IUniverInstanceService, { useClass: UniverInstanceService }]);
|
||||
injector.add([ICommandService, { useClass: (useRecordingServices ? RecordingCommandService : CommandService) as never }]);
|
||||
injector.add([IConfirmService, { useClass: (useRecordingServices ? RecordingConfirmService : TestConfirmService) as never }]);
|
||||
injector.add([LexerTreeBuilder]);
|
||||
injector.add([FormulaDataModel]);
|
||||
injector.add([SheetsSelectionsService]);
|
||||
if (useRecordingServices) {
|
||||
injector.add([SheetsSortService, { useClass: RecordingSheetsSortService as never }]);
|
||||
} else {
|
||||
injector.add([SheetsSortService]);
|
||||
}
|
||||
injector.add([LocaleService]);
|
||||
injector.add([SheetsSortUIService]);
|
||||
injector.get(LocaleService).load({ zhCN });
|
||||
const univerInstanceService = injector.get(IUniverInstanceService) as UniverInstanceService;
|
||||
const workbook = injector.createInstance(Workbook, {
|
||||
id: 'unit-1',
|
||||
sheets: {
|
||||
'sheet-1': {
|
||||
id: 'sheet-1',
|
||||
cellData: {
|
||||
0: {
|
||||
1: { v: '0:1' },
|
||||
2: { v: '0:2' },
|
||||
3: { v: '0:3' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sheetOrder: ['sheet-1'],
|
||||
});
|
||||
univerInstanceService.__addUnit(workbook);
|
||||
univerInstanceService.focusUnit('unit-1');
|
||||
return {
|
||||
injector,
|
||||
service: injector.get(SheetsSortUIService),
|
||||
sortService: injector.get(SheetsSortService) as unknown as RecordingSheetsSortService,
|
||||
commandService: injector.get(ICommandService) as unknown as RecordingCommandService,
|
||||
confirmService: injector.get(IConfirmService) as unknown as RecordingConfirmService,
|
||||
selectionsService: injector.get(SheetsSelectionsService),
|
||||
};
|
||||
}
|
||||
|
||||
describe('SheetsSortUIService', () => {
|
||||
it('tracks the custom sort panel location and derives column titles from the selected range', () => {
|
||||
const { service } = createService();
|
||||
const location = {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
colIndex: 1,
|
||||
range: { startRow: 0, endRow: 4, startColumn: 1, endColumn: 3 },
|
||||
};
|
||||
|
||||
service.showCustomSortPanel(location);
|
||||
expect(service.customSortState()).toEqual({ location, show: true });
|
||||
expect(service.getTitles(true)).toEqual([
|
||||
{ index: 1, label: '0:1' },
|
||||
{ index: 2, label: '0:2' },
|
||||
{ index: 3, label: '0:3' },
|
||||
]);
|
||||
|
||||
service.closeCustomSortPanel();
|
||||
expect(service.customSortState()).toEqual({ show: false });
|
||||
});
|
||||
|
||||
it('sorts the current selection directly with the requested direction', async () => {
|
||||
const { service, sortService, selectionsService } = createService(true);
|
||||
|
||||
selectionsService.addSelections([{
|
||||
range: { startRow: 0, endRow: 2, startColumn: 1, endColumn: 3, rangeType: RANGE_TYPE.NORMAL },
|
||||
primary: {
|
||||
actualRow: 0,
|
||||
actualColumn: 2,
|
||||
startRow: 0,
|
||||
endRow: 2,
|
||||
startColumn: 1,
|
||||
endColumn: 3,
|
||||
isMerged: false,
|
||||
isMergedMainCell: false,
|
||||
},
|
||||
style: null,
|
||||
}]);
|
||||
|
||||
expect(await service.triggerSortDirectly(false, false)).toBe(true);
|
||||
expect(sortService.appliedSorts).toEqual([{
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
option: {
|
||||
orderRules: [{ type: SortType.DESC, colIndex: 2 }],
|
||||
range: { startRow: 0, endRow: 2, startColumn: 1, endColumn: 3, rangeType: RANGE_TYPE.NORMAL },
|
||||
},
|
||||
}]);
|
||||
});
|
||||
|
||||
it('extends a partial selection before opening custom sort when the user chooses extend', async () => {
|
||||
const { service, sortService, commandService, confirmService, selectionsService } = createService(true);
|
||||
confirmService.extendType = EXTEND_TYPE.EXTEND;
|
||||
|
||||
selectionsService.addSelections([{
|
||||
range: { startRow: 0, endRow: 0, startColumn: 1, endColumn: 1, rangeType: RANGE_TYPE.NORMAL },
|
||||
primary: {
|
||||
actualRow: 0,
|
||||
actualColumn: 1,
|
||||
startRow: 0,
|
||||
endRow: 0,
|
||||
startColumn: 1,
|
||||
endColumn: 1,
|
||||
isMerged: false,
|
||||
isMergedMainCell: false,
|
||||
},
|
||||
style: null,
|
||||
}]);
|
||||
|
||||
expect(await service.triggerSortCustomize()).toBe(true);
|
||||
expect(commandService.commands[0]).toMatchObject({
|
||||
id: 'sheet.operation.set-selections',
|
||||
params: {
|
||||
selections: [{
|
||||
range: { startRow: 0, endRow: 0, startColumn: 1, endColumn: 3 },
|
||||
}],
|
||||
},
|
||||
});
|
||||
expect(service.customSortState()?.location?.range).toMatchObject({
|
||||
startRow: 0,
|
||||
endRow: 0,
|
||||
startColumn: 1,
|
||||
endColumn: 3,
|
||||
});
|
||||
expect(sortService.appliedSorts).toEqual([]);
|
||||
});
|
||||
|
||||
it('cancels custom sort when the extend prompt is rejected', async () => {
|
||||
const { service, confirmService, selectionsService } = createService(true);
|
||||
confirmService.result = false;
|
||||
|
||||
selectionsService.addSelections([{
|
||||
range: { startRow: 0, endRow: 0, startColumn: 1, endColumn: 1, rangeType: RANGE_TYPE.NORMAL },
|
||||
primary: {
|
||||
actualRow: 0,
|
||||
actualColumn: 1,
|
||||
startRow: 0,
|
||||
endRow: 0,
|
||||
startColumn: 1,
|
||||
endColumn: 1,
|
||||
isMerged: false,
|
||||
isMergedMainCell: false,
|
||||
},
|
||||
style: null,
|
||||
}]);
|
||||
|
||||
expect(await service.triggerSortCustomize()).toBe(false);
|
||||
expect(service.customSortState()).toBeNull();
|
||||
});
|
||||
|
||||
it('blocks sorting and shows a check error when the selected range is invalid', async () => {
|
||||
const { service, sortService, confirmService } = createService(true);
|
||||
const location = {
|
||||
unitId: 'unit-1',
|
||||
subUnitId: 'sheet-1',
|
||||
colIndex: 1,
|
||||
range: { startRow: 0, endRow: 0, startColumn: 1, endColumn: 3 },
|
||||
};
|
||||
sortService.failedCheck = 'single';
|
||||
|
||||
expect(await service.triggerSortDirectly(true, false, location)).toBe(false);
|
||||
expect(sortService.appliedSorts).toEqual([]);
|
||||
expect(confirmService.confirmations).toMatchObject([{ id: 'sort-range-check-error' }]);
|
||||
});
|
||||
});
|
||||
278
packages/sheets-sort-ui/src/services/sheets-sort-ui.service.ts
Normal file
278
packages/sheets-sort-ui/src/services/sheets-sort-ui.service.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { IRange, Nullable, Workbook, Worksheet } from '@univerjs/core';
|
||||
import type { ISheetRangeLocation } from '@univerjs/sheets';
|
||||
import type { ISortOption } from '@univerjs/sheets-sort';
|
||||
import type { LocaleKey } from '../locale/types';
|
||||
import {
|
||||
Disposable,
|
||||
ICommandService,
|
||||
IConfirmService,
|
||||
Inject,
|
||||
IUniverInstanceService,
|
||||
LocaleService,
|
||||
LocaleType,
|
||||
Tools,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { expandToContinuousRange, getPrimaryForRange, SetSelectionsOperation, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { SheetsSortService, SortType } from '@univerjs/sheets-sort';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { createExtendConfirm } from '../views/ExtendConfirm';
|
||||
import { createSortCheckError } from '../views/SortCheckError';
|
||||
|
||||
export enum EXTEND_TYPE {
|
||||
KEEP = 'keep',
|
||||
EXTEND = 'extend',
|
||||
CANCEL = 'cancel',
|
||||
}
|
||||
|
||||
export interface ICustomSortState {
|
||||
location?: ISheetSortLocation;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
export interface ISheetSortLocation extends ISheetRangeLocation {
|
||||
colIndex: number;
|
||||
}
|
||||
|
||||
const SORT_ERROR_MESSAGE = {
|
||||
MERGE_ERROR: 'sheets-sort-ui.error.merge-size',
|
||||
EMPTY_ERROR: 'sheets-sort-ui.error.empty',
|
||||
SINGLE_ERROR: 'sheets-sort-ui.error.single',
|
||||
FORMULA_ARRAY: 'sheets-sort-ui.error.formula-array',
|
||||
};
|
||||
|
||||
export class SheetsSortUIService extends Disposable {
|
||||
private readonly _customSortState$ = new BehaviorSubject<Nullable<ICustomSortState>>(null);
|
||||
readonly customSortState$ = this._customSortState$.asObservable();
|
||||
|
||||
constructor(
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
|
||||
@IConfirmService private readonly _confirmService: IConfirmService,
|
||||
@Inject(SheetsSelectionsService) private readonly _selectionManagerService: SheetsSelectionsService,
|
||||
@Inject(SheetsSortService) private readonly _sheetsSortService: SheetsSortService,
|
||||
@Inject(LocaleService) private readonly _localeService: LocaleService,
|
||||
@ICommandService private readonly _commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async triggerSortDirectly(asc: boolean, extend: boolean, sheetRangeLocation?: ISheetSortLocation): Promise<boolean> {
|
||||
const location = sheetRangeLocation || await this._detectSortLocation(extend);
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const check = this._check(location);
|
||||
if (!check) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sortOption: ISortOption = {
|
||||
orderRules: [{
|
||||
type: asc ? SortType.ASC : SortType.DESC,
|
||||
colIndex: location.colIndex,
|
||||
}],
|
||||
range: location.range,
|
||||
};
|
||||
this._sheetsSortService.applySort(sortOption, location.unitId, location.subUnitId);
|
||||
return true;
|
||||
}
|
||||
|
||||
async triggerSortCustomize() {
|
||||
const location = await this._detectSortLocation();
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const check = this._check(location);
|
||||
if (!check) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// open customize dialog
|
||||
this.showCustomSortPanel(location);
|
||||
return true;
|
||||
}
|
||||
|
||||
customSortState() {
|
||||
return this._customSortState$.getValue();
|
||||
}
|
||||
|
||||
getTitles(hasTitle: boolean) {
|
||||
const location = this.customSortState()?.location;
|
||||
if (!location) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { unitId, subUnitId, range } = location;
|
||||
const worksheet = (this._univerInstanceService.getUnit(unitId) as Workbook)?.getSheetBySheetId(subUnitId);
|
||||
if (!worksheet) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const colTranslator = colIndexTranslator(this._localeService);
|
||||
|
||||
return Array.from({ length: range.endColumn - range.startColumn + 1 }, (_, i) => {
|
||||
const cellValue = worksheet.getCell(range.startRow, i + range.startColumn)?.v;
|
||||
return {
|
||||
index: i + range.startColumn,
|
||||
label: hasTitle ?
|
||||
`${cellValue ?? colTranslator(i + range.startColumn)}` :
|
||||
colTranslator(i + range.startColumn),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
setSelection(unitId: string, subUnitId: string, range: IRange) {
|
||||
const worksheet = (this._univerInstanceService.getUnit(unitId) as Workbook)?.getSheetBySheetId(subUnitId);
|
||||
if (!worksheet) {
|
||||
return;
|
||||
}
|
||||
const setSelectionsOperationParams = {
|
||||
unitId,
|
||||
subUnitId,
|
||||
|
||||
selections: [{ range, primary: getPrimaryForRange(range, worksheet), style: null }],
|
||||
|
||||
};
|
||||
this._commandService.executeCommand(SetSelectionsOperation.id, setSelectionsOperationParams);
|
||||
}
|
||||
|
||||
async showCheckError(content: string): Promise<boolean> {
|
||||
return await this._confirmService.confirm({
|
||||
id: 'sort-range-check-error',
|
||||
title: {
|
||||
title: this._localeService.t<LocaleKey>('sheets-sort-ui.info.tooltip'),
|
||||
},
|
||||
children: {
|
||||
title: createSortCheckError(this._localeService.t(content)),
|
||||
},
|
||||
cancelText: this._localeService.t<LocaleKey>('sheets-sort-ui.dialog.cancel'),
|
||||
confirmText: this._localeService.t<LocaleKey>('sheets-sort-ui.dialog.confirm'),
|
||||
});
|
||||
}
|
||||
|
||||
async showExtendConfirm(): Promise<EXTEND_TYPE> {
|
||||
let shouldExtend = false;
|
||||
const confirm = await this._confirmService.confirm({
|
||||
id: 'extend-sort-range-dialog',
|
||||
title: {
|
||||
title: this._localeService.t<LocaleKey>('sheets-sort-ui.dialog.sort-reminder'),
|
||||
},
|
||||
children: {
|
||||
title: createExtendConfirm((value: string) => {
|
||||
shouldExtend = value === '1';
|
||||
}),
|
||||
},
|
||||
width: 400,
|
||||
cancelText: this._localeService.t<LocaleKey>('sheets-sort-ui.dialog.cancel'),
|
||||
confirmText: this._localeService.t<LocaleKey>('sheets-sort-ui.dialog.confirm'),
|
||||
});
|
||||
if (confirm) {
|
||||
return shouldExtend ? EXTEND_TYPE.EXTEND : EXTEND_TYPE.KEEP;
|
||||
}
|
||||
return EXTEND_TYPE.CANCEL;
|
||||
}
|
||||
|
||||
showCustomSortPanel(location: ISheetSortLocation) {
|
||||
this._customSortState$.next({ location, show: true });
|
||||
}
|
||||
|
||||
closeCustomSortPanel() {
|
||||
this._customSortState$.next({ show: false });
|
||||
}
|
||||
|
||||
private _check(location: ISheetSortLocation) {
|
||||
const singleCheck = this._sheetsSortService.singleCheck(location);
|
||||
if (!singleCheck) {
|
||||
this.showCheckError(SORT_ERROR_MESSAGE.SINGLE_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
const mergeCheck = this._sheetsSortService.mergeCheck(location);
|
||||
if (!mergeCheck) {
|
||||
this.showCheckError(SORT_ERROR_MESSAGE.MERGE_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
const formulaCheck = this._sheetsSortService.formulaCheck(location);
|
||||
if (!formulaCheck) {
|
||||
this.showCheckError(SORT_ERROR_MESSAGE.FORMULA_ARRAY);
|
||||
return false;
|
||||
}
|
||||
|
||||
const emptyCheck = this._sheetsSortService.emptyCheck(location);
|
||||
if (!emptyCheck) {
|
||||
this.showCheckError(SORT_ERROR_MESSAGE.EMPTY_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async _detectSortLocation(extend?: boolean): Promise<Nullable<ISheetSortLocation>> {
|
||||
const workbook = this._univerInstanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_SHEET) as Workbook;
|
||||
const worksheet = workbook.getActiveSheet() as Worksheet;
|
||||
const unitId = workbook.getUnitId();
|
||||
const subUnitId = worksheet.getSheetId();
|
||||
const selection = this._selectionManagerService.getCurrentLastSelection();
|
||||
if (!selection) {
|
||||
return null;
|
||||
}
|
||||
let range: IRange;
|
||||
if (extend === true) {
|
||||
range = expandToContinuousRange(selection.range, { up: true, down: true, left: true, right: true }, worksheet);
|
||||
this.setSelection(unitId, subUnitId, range);
|
||||
} else if (extend === false) {
|
||||
range = selection.range;
|
||||
} else {
|
||||
const confirmRes = await this.showExtendConfirm();
|
||||
if (confirmRes === EXTEND_TYPE.CANCEL) {
|
||||
return null;
|
||||
}
|
||||
if (confirmRes === EXTEND_TYPE.KEEP) {
|
||||
range = selection.range;
|
||||
} else {
|
||||
range = expandToContinuousRange(selection.range, { up: true, down: true, left: true, right: true }, worksheet);
|
||||
this.setSelection(unitId, subUnitId, range);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
range,
|
||||
unitId,
|
||||
subUnitId,
|
||||
colIndex: selection.primary.actualColumn,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function colIndexTranslator(localeService: LocaleService) {
|
||||
return (colIndex: number) => {
|
||||
const colName = Tools.chatAtABC(colIndex);
|
||||
const currentLocale = localeService.getCurrentLocale();
|
||||
switch (currentLocale) {
|
||||
case LocaleType.ZH_CN:
|
||||
return `"${colName}"列`;
|
||||
case LocaleType.EN_US:
|
||||
return `Column "${colName}"`;
|
||||
default:
|
||||
return `Column "${colName}"`;
|
||||
}
|
||||
};
|
||||
}
|
||||
335
packages/sheets-sort-ui/src/views/CustomSortPanel.tsx
Normal file
335
packages/sheets-sort-ui/src/views/CustomSortPanel.tsx
Normal file
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { IRange, Nullable } from '@univerjs/core';
|
||||
import type { IOrderRule } from '@univerjs/sheets-sort';
|
||||
import type { LocaleKey } from '../locale/types';
|
||||
import type { ICustomSortState } from '../services/sheets-sort-ui.service';
|
||||
import { LocaleService, throttle } from '@univerjs/core';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
clsx,
|
||||
DraggableList,
|
||||
Dropdown,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
scrollbarClassName,
|
||||
} from '@univerjs/design';
|
||||
import { CheckMarkIcon, DeleteEmptyIcon, GripVerticalIcon, IncreaseIcon, MoreDownIcon } from '@univerjs/icons';
|
||||
import { SheetsSortService, SortType } from '@univerjs/sheets-sort';
|
||||
import { useDependency, useObservable } from '@univerjs/ui';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { SheetsSortUIService } from '../services/sheets-sort-ui.service';
|
||||
|
||||
export interface ICustomSortPanelProps {
|
||||
range: IRange;
|
||||
onListChange: (value: IOrderRule[]) => void;
|
||||
}
|
||||
|
||||
export function CustomSortPanel() {
|
||||
const sheetsSortUIService = useDependency(SheetsSortUIService);
|
||||
const state = useObservable(sheetsSortUIService.customSortState$);
|
||||
|
||||
if (!state || !state.location) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <CustomSortPanelImpl state={state} />;
|
||||
}
|
||||
|
||||
function CustomSortPanelImpl({ state }: { state: ICustomSortState }) {
|
||||
const sheetsSortService = useDependency(SheetsSortService);
|
||||
const localeService = useDependency(LocaleService);
|
||||
const sheetsSortUIService = useDependency(SheetsSortUIService);
|
||||
|
||||
const [hasTitle, setHasTitle] = useState(false);
|
||||
const [scrollPosition, setScrollPosition] = useState(0);
|
||||
const listEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { range, unitId, subUnitId } = state.location!;
|
||||
|
||||
const titles = sheetsSortUIService.getTitles(hasTitle);
|
||||
|
||||
const [list, setList] = useState<IOrderRule[]>([
|
||||
{ type: SortType.ASC, colIndex: range.startColumn },
|
||||
]);
|
||||
|
||||
const onItemChange = useCallback((index: number, value: Nullable<IOrderRule>) => {
|
||||
const newList = [...list];
|
||||
if (value === null) {
|
||||
newList.splice(index, 1);
|
||||
} else {
|
||||
newList[index] = value as IOrderRule;
|
||||
}
|
||||
|
||||
setList(newList as IOrderRule[]);
|
||||
}, [list]);
|
||||
|
||||
const newItem = useCallback(
|
||||
throttle(() => {
|
||||
const newList = [...list];
|
||||
const nextColIndex = findNextColIndex(range, list);
|
||||
if (nextColIndex !== null) {
|
||||
newList.push({ type: SortType.ASC, colIndex: nextColIndex });
|
||||
setList(newList);
|
||||
}
|
||||
}, 200),
|
||||
[list, range]
|
||||
);
|
||||
|
||||
const apply = useCallback((orderRules: IOrderRule[], hasTitle: boolean) => {
|
||||
sheetsSortService.applySort({ range, orderRules, hasTitle });
|
||||
sheetsSortUIService.closeCustomSortPanel();
|
||||
}, [sheetsSortService, sheetsSortUIService, range]);
|
||||
|
||||
const cancel = useCallback(() => {
|
||||
sheetsSortUIService.closeCustomSortPanel();
|
||||
}, [sheetsSortUIService]);
|
||||
|
||||
const setTitle = useCallback((value: boolean) => {
|
||||
setHasTitle(value);
|
||||
if (value) {
|
||||
sheetsSortUIService.setSelection(unitId, subUnitId, { ...range, startRow: range.startRow + 1 });
|
||||
} else {
|
||||
sheetsSortUIService.setSelection(unitId, subUnitId, range);
|
||||
}
|
||||
}, [sheetsSortUIService, range, subUnitId, unitId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (listEndRef.current && list.length > 5) {
|
||||
listEndRef.current.scrollTop = listEndRef.current.scrollHeight;
|
||||
}
|
||||
}, [list]);
|
||||
|
||||
const canNew = list.length < titles.length;
|
||||
|
||||
const dragList = list.map((item) => ({ ...item, id: `${item.colIndex}` }));
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div onMouseDown={(e) => { e.stopPropagation(); }}>
|
||||
<div className="univer-mb-2 univer-flex univer-items-center univer-justify-between">
|
||||
<Checkbox checked={hasTitle} onChange={(value) => setTitle(!!value)}>
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.first-row-check')}
|
||||
</Checkbox>
|
||||
{canNew
|
||||
? (
|
||||
<div
|
||||
className={`
|
||||
univer-flex univer-cursor-pointer univer-select-none univer-items-center
|
||||
univer-text-base
|
||||
`}
|
||||
onClick={newItem}
|
||||
>
|
||||
<IncreaseIcon />
|
||||
<span className="univer-ml-1.5">{localeService.t<LocaleKey>('sheets-sort-ui.dialog.add-condition')}</span>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div
|
||||
className={`
|
||||
univer-flex univer-cursor-pointer univer-select-none univer-items-center
|
||||
univer-text-base univer-text-primary-500
|
||||
disabled:univer-cursor-not-allowed disabled:univer-divide-opacity-30
|
||||
disabled:univer-text-gray-800
|
||||
`}
|
||||
>
|
||||
<IncreaseIcon />
|
||||
<span className="univer-ml-1.5 univer-text-xs">{localeService.t<LocaleKey>('sheets-sort-ui.dialog.add-condition')}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
<div
|
||||
ref={listEndRef}
|
||||
className={clsx('univer-max-h-[310px] univer-overflow-y-auto univer-overflow-x-hidden', scrollbarClassName)}
|
||||
onScroll={(e) => {
|
||||
const position = e.currentTarget.scrollTop;
|
||||
setScrollPosition(position);
|
||||
}}
|
||||
>
|
||||
<DraggableList
|
||||
list={dragList}
|
||||
onListChange={setList}
|
||||
idKey="id"
|
||||
draggableHandle="[data-u-comp=sort-panel-item-handler]"
|
||||
itemRender={(item) => (
|
||||
<SortOptionItem
|
||||
titles={titles}
|
||||
list={dragList}
|
||||
item={item}
|
||||
onChange={(value, index) => onItemChange(index, value)}
|
||||
scrollPosition={scrollPosition}
|
||||
/>
|
||||
)}
|
||||
rowHeight={32}
|
||||
margin={[0, 12]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="univer-mt-5 univer-flex univer-justify-end">
|
||||
<Button
|
||||
className="univer-ml-3"
|
||||
onClick={() => cancel()}
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
className="univer-ml-3"
|
||||
variant="primary"
|
||||
onClick={() => apply(list, hasTitle)}
|
||||
>
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.confirm')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ISortOptionItemProps {
|
||||
titles: { index: number; label: string }[];
|
||||
list: IOrderRule[];
|
||||
item: IOrderRule;
|
||||
scrollPosition: number;
|
||||
onChange: (value: Nullable<IOrderRule>, index: number) => void;
|
||||
}
|
||||
|
||||
export function SortOptionItem(props: ISortOptionItemProps) {
|
||||
const { list, item, titles, onChange, scrollPosition } = props;
|
||||
const localeService = useDependency(LocaleService);
|
||||
|
||||
const availableMenu = titles.filter((title) => (!list.some((item) => item.colIndex === title.index)) || title.index === item.colIndex);
|
||||
const currentIndex = list.findIndex((listItem) => listItem.colIndex === item.colIndex);
|
||||
|
||||
const handleChangeColIndex = useCallback((menuItem: { index: number; label: string }) => {
|
||||
onChange({ ...item, colIndex: menuItem.index }, currentIndex);
|
||||
setVisible(false);
|
||||
}, [currentIndex, item, onChange]);
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
setVisible(visible);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setVisible(false);
|
||||
}, [scrollPosition]);
|
||||
|
||||
const showDelete = list.length > 1;
|
||||
const itemLabel = titles.find((title) => title.index === item.colIndex)?.label;
|
||||
|
||||
return (
|
||||
<div className="univer-grid univer-grid-flow-col univer-grid-cols-2 univer-items-center univer-gap-2">
|
||||
<div className="univer-flex univer-items-center">
|
||||
<div
|
||||
data-u-comp="sort-panel-item-handler"
|
||||
className={`
|
||||
univer-flex univer-cursor-pointer univer-items-center univer-justify-center univer-text-base
|
||||
univer-text-gray-700
|
||||
`}
|
||||
>
|
||||
<GripVerticalIcon />
|
||||
</div>
|
||||
{/* TODO@wzhudev: change it to the Select component later. */}
|
||||
<Dropdown
|
||||
overlay={(
|
||||
<ul
|
||||
className={clsx(`
|
||||
univer-my-0 univer-box-border univer-grid univer-max-h-[310px]
|
||||
univer-w-[--radix-popper-anchor-width] univer-items-center univer-gap-1
|
||||
univer-overflow-y-auto univer-overflow-x-hidden univer-rounded-lg univer-border
|
||||
univer-bg-gray-0 univer-p-1 univer-text-base univer-shadow-lg
|
||||
`, scrollbarClassName)}
|
||||
>
|
||||
{availableMenu.map((menuItem) => (
|
||||
<li
|
||||
key={menuItem.index}
|
||||
onClick={() => handleChangeColIndex(menuItem)}
|
||||
className={`
|
||||
univer-relative univer-box-border univer-flex univer-h-7 univer-cursor-pointer
|
||||
univer-list-none univer-items-center univer-justify-between univer-rounded
|
||||
univer-px-2 univer-text-sm univer-transition-all
|
||||
hover:univer-bg-gray-100
|
||||
dark:hover:!univer-bg-gray-700
|
||||
`}
|
||||
>
|
||||
<span className="univer-max-w-[220px] univer-truncate">
|
||||
{menuItem.label}
|
||||
</span>
|
||||
<span>
|
||||
{menuItem.index === item.colIndex && (
|
||||
<CheckMarkIcon />
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
open={visible}
|
||||
onOpenChange={onVisibleChange}
|
||||
>
|
||||
<div
|
||||
className={clsx(`
|
||||
univer-ml-2 univer-flex univer-w-full univer-items-center univer-justify-between
|
||||
univer-overflow-hidden univer-rounded-md univer-py-1.5 univer-text-sm univer-text-gray-900
|
||||
dark:!univer-text-gray-0
|
||||
`)}
|
||||
>
|
||||
<span
|
||||
className="univer-max-w-[220px] univer-truncate"
|
||||
>
|
||||
{itemLabel}
|
||||
</span>
|
||||
<MoreDownIcon />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div className="univer-flex univer-items-center univer-justify-end univer-gap-2">
|
||||
<RadioGroup
|
||||
value={item.type}
|
||||
onChange={(value) => {
|
||||
onChange({ ...item, type: value as SortType }, currentIndex);
|
||||
}}
|
||||
>
|
||||
<Radio value={SortType.ASC}>{localeService.t<LocaleKey>('sheets-sort-ui.general.sort-asc')}</Radio>
|
||||
<Radio value={SortType.DESC}>{localeService.t<LocaleKey>('sheets-sort-ui.general.sort-desc')}</Radio>
|
||||
</RadioGroup>
|
||||
<a
|
||||
className={`
|
||||
univer-flex univer-cursor-pointer univer-items-center univer-text-sm univer-transition-colors
|
||||
hover:univer-text-red-500
|
||||
`}
|
||||
onClick={() => onChange(null, currentIndex)}
|
||||
>
|
||||
{!showDelete && <DeleteEmptyIcon />}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function findNextColIndex(range: IRange, list: Nullable<IOrderRule>[]): number | null {
|
||||
const { startColumn, endColumn } = range;
|
||||
const used = new Set(list.map((item) => item?.colIndex));
|
||||
for (let i = startColumn; i <= endColumn; i++) {
|
||||
if (!used.has(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
64
packages/sheets-sort-ui/src/views/EmbedSortBtn.tsx
Normal file
64
packages/sheets-sort-ui/src/views/EmbedSortBtn.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { IRange } from '@univerjs/core';
|
||||
import type { LocaleKey } from '../locale/types';
|
||||
import { IUniverInstanceService, LocaleService } from '@univerjs/core';
|
||||
import { Button, ButtonGroup } from '@univerjs/design';
|
||||
import { AscendingIcon, DescendingIcon } from '@univerjs/icons';
|
||||
import { getSheetCommandTarget } from '@univerjs/sheets';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useCallback } from 'react';
|
||||
import { SheetsSortUIService } from '../services/sheets-sort-ui.service';
|
||||
|
||||
export interface IEmbedSortBtnProps {
|
||||
range: IRange;
|
||||
colIndex: number;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function EmbedSortBtn(props: IEmbedSortBtnProps) {
|
||||
const { range, colIndex, onClose } = props;
|
||||
|
||||
const sheetsSortUIService = useDependency(SheetsSortUIService);
|
||||
const univerInstanceService = useDependency(IUniverInstanceService);
|
||||
const localeService = useDependency(LocaleService);
|
||||
|
||||
const apply = useCallback((asc: boolean) => {
|
||||
const { unitId, subUnitId } = getSheetCommandTarget(univerInstanceService) || {};
|
||||
if (range && unitId && subUnitId) {
|
||||
const noTitleRange = { ...range, startRow: range.startRow + 1 };
|
||||
sheetsSortUIService.triggerSortDirectly(asc, false, { unitId, subUnitId, range: noTitleRange, colIndex });
|
||||
} else {
|
||||
throw new Error(`Cannot find the target to sort. unitId: ${unitId}, subUnitId: ${subUnitId}, range: ${range}, colIndex: ${colIndex}`);
|
||||
}
|
||||
|
||||
onClose();
|
||||
}, [range, colIndex, sheetsSortUIService, univerInstanceService, onClose]);
|
||||
|
||||
return (
|
||||
<ButtonGroup className="univer-mb-3 univer-w-full univer-grid-cols-2">
|
||||
<Button onClick={() => apply(true)}>
|
||||
<AscendingIcon />
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.general.sort-asc')}
|
||||
</Button>
|
||||
<Button onClick={() => apply(false)}>
|
||||
<DescendingIcon />
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.general.sort-desc')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
58
packages/sheets-sort-ui/src/views/ExtendConfirm.tsx
Normal file
58
packages/sheets-sort-ui/src/views/ExtendConfirm.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { LocaleKey } from '../locale/types';
|
||||
import { LocaleService } from '@univerjs/core';
|
||||
import { Radio, RadioGroup } from '@univerjs/design';
|
||||
import { useDependency } from '@univerjs/ui';
|
||||
import { useState } from 'react';
|
||||
|
||||
export interface IExtendConfirmProps {
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
|
||||
export const ExtendConfirm = (props: IExtendConfirmProps) => {
|
||||
const [extend, setExtend] = useState<string>('0');
|
||||
const localeService = useDependency(LocaleService);
|
||||
return (
|
||||
<div className="univer-text-sm">
|
||||
<div className="extend-confirm-desc">
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.sort-reminder-desc')}
|
||||
</div>
|
||||
|
||||
<RadioGroup
|
||||
className="univer-mt-4"
|
||||
value={extend}
|
||||
direction="vertical"
|
||||
onChange={(value) => {
|
||||
setExtend(value as string);
|
||||
props.onChange(value as string);
|
||||
}}
|
||||
>
|
||||
<Radio value="0">
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.sort-reminder-no')}
|
||||
</Radio>
|
||||
<Radio value="1">
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.sort-reminder-ext')}
|
||||
</Radio>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export function createExtendConfirm(onChange: IExtendConfirmProps['onChange']) {
|
||||
return <ExtendConfirm onChange={onChange} />;
|
||||
}
|
||||
27
packages/sheets-sort-ui/src/views/SortCheckError.tsx
Normal file
27
packages/sheets-sort-ui/src/views/SortCheckError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export interface ISortCheckErrorProps {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export const SortCheckError = (props: ISortCheckErrorProps) => {
|
||||
return <div>{props.message}</div>;
|
||||
};
|
||||
|
||||
export function createSortCheckError(message: string) {
|
||||
return <SortCheckError message={message} />;
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { Dependency, IDisposable, IWorkbookData, Workbook, Worksheet } from '@univerjs/core';
|
||||
import type { Root } from 'react-dom/client';
|
||||
import {
|
||||
awaitTime,
|
||||
ICommandService,
|
||||
IConfirmService,
|
||||
ILogService,
|
||||
Inject,
|
||||
Injector,
|
||||
IUniverInstanceService,
|
||||
LocaleService,
|
||||
LocaleType,
|
||||
LogLevel,
|
||||
Plugin,
|
||||
toDisposable,
|
||||
Univer,
|
||||
UniverInstanceType,
|
||||
} from '@univerjs/core';
|
||||
import { FormulaDataModel, LexerTreeBuilder } from '@univerjs/engine-formula';
|
||||
import {
|
||||
ReorderRangeCommand,
|
||||
ReorderRangeMutation,
|
||||
SetSelectionsOperation,
|
||||
SheetInterceptorService,
|
||||
SheetSkeletonService,
|
||||
SheetsSelectionsService,
|
||||
} from '@univerjs/sheets';
|
||||
import { SheetsSortController, SheetsSortService } from '@univerjs/sheets-sort';
|
||||
import { RediContext } from '@univerjs/ui';
|
||||
import { act } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Subject } from 'rxjs';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import enUS from '../../locale/en-US';
|
||||
import { SheetsSortUIService } from '../../services/sheets-sort-ui.service';
|
||||
import { CustomSortPanel } from '../CustomSortPanel';
|
||||
import EmbedSortBtn from '../EmbedSortBtn';
|
||||
import { ExtendConfirm } from '../ExtendConfirm';
|
||||
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
const UNIT_ID = 'test';
|
||||
const SUB_UNIT_ID = 'sheet1';
|
||||
|
||||
class TestConfirmService implements IConfirmService {
|
||||
readonly confirmOptions$ = new Subject<unknown[]>();
|
||||
|
||||
open(): IDisposable {
|
||||
return toDisposable(() => undefined);
|
||||
}
|
||||
|
||||
confirm(): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
close() {
|
||||
// The tested sort paths close panels through SheetsSortUIService.
|
||||
}
|
||||
}
|
||||
|
||||
function createWorkbookData(): IWorkbookData {
|
||||
return {
|
||||
id: UNIT_ID,
|
||||
appVersion: '3.0.0-alpha',
|
||||
locale: LocaleType.EN_US,
|
||||
name: 'sort view test',
|
||||
sheetOrder: [SUB_UNIT_ID],
|
||||
styles: {},
|
||||
sheets: {
|
||||
[SUB_UNIT_ID]: {
|
||||
id: SUB_UNIT_ID,
|
||||
name: 'Sheet1',
|
||||
rowCount: 20,
|
||||
columnCount: 20,
|
||||
cellData: {
|
||||
0: {
|
||||
0: { v: 'Name' },
|
||||
1: { v: 'Score' },
|
||||
2: { v: 'Region' },
|
||||
},
|
||||
1: {
|
||||
0: { v: 'Cat' },
|
||||
1: { v: 20, t: 2 },
|
||||
2: { v: 'North' },
|
||||
},
|
||||
2: {
|
||||
0: { v: 'Amy' },
|
||||
1: { v: 10, t: 2 },
|
||||
2: { v: 'East' },
|
||||
},
|
||||
3: {
|
||||
0: { v: 'Ben' },
|
||||
1: { v: 30, t: 2 },
|
||||
2: { v: 'West' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createSortViewTestBed() {
|
||||
const univer = new Univer();
|
||||
const injector = univer.__getInjector();
|
||||
|
||||
class TestPlugin extends Plugin {
|
||||
static override pluginName = 'sheets-sort-ui-view-test-plugin';
|
||||
static override type = UniverInstanceType.UNIVER_SHEET;
|
||||
|
||||
constructor(
|
||||
_config: undefined,
|
||||
@Inject(Injector) protected readonly _injector: Injector
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override onStarting(): void {
|
||||
([
|
||||
[SheetsSelectionsService],
|
||||
[SheetInterceptorService],
|
||||
[SheetSkeletonService],
|
||||
[FormulaDataModel],
|
||||
[LexerTreeBuilder],
|
||||
[SheetsSortController],
|
||||
[SheetsSortService],
|
||||
[SheetsSortUIService],
|
||||
[IConfirmService, { useClass: TestConfirmService }],
|
||||
] as Dependency[]).forEach((dependency) => this._injector.add(dependency));
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
this._injector.get(SheetsSortController);
|
||||
}
|
||||
}
|
||||
|
||||
injector.get(LocaleService).load({ [LocaleType.EN_US]: enUS });
|
||||
injector.get(LocaleService).setLocale(LocaleType.EN_US);
|
||||
univer.registerPlugin(TestPlugin);
|
||||
|
||||
const workbook = univer.createUnit<IWorkbookData, Workbook>(UniverInstanceType.UNIVER_SHEET, createWorkbookData());
|
||||
injector.get(IUniverInstanceService).focusUnit(UNIT_ID);
|
||||
injector.get(ILogService).setLogLevel(LogLevel.SILENT);
|
||||
|
||||
const commandService = injector.get(ICommandService);
|
||||
[SetSelectionsOperation, ReorderRangeMutation, ReorderRangeCommand].forEach((command) => commandService.registerCommand(command));
|
||||
|
||||
return {
|
||||
univer,
|
||||
injector,
|
||||
workbook,
|
||||
worksheet: workbook.getSheetBySheetId(SUB_UNIT_ID)!,
|
||||
sortUIService: injector.get(SheetsSortUIService),
|
||||
};
|
||||
}
|
||||
|
||||
async function renderWithInjector(root: Root, testBed: ReturnType<typeof createSortViewTestBed>, node: React.ReactNode) {
|
||||
await act(async () => {
|
||||
root.render(
|
||||
<RediContext.Provider value={{ injector: testBed.injector }}>
|
||||
{node}
|
||||
</RediContext.Provider>
|
||||
);
|
||||
await awaitTime(20);
|
||||
});
|
||||
}
|
||||
|
||||
function getColumnValues(worksheet: Worksheet, col: number, rows: number[]) {
|
||||
return rows.map((row) => worksheet.getCell(row, col)?.v);
|
||||
}
|
||||
|
||||
function getButton(container: HTMLElement, text: string) {
|
||||
const button = Array.from(container.querySelectorAll('[data-u-comp="button"]'))
|
||||
.find((item) => item.textContent === text);
|
||||
expect(button).toBeTruthy();
|
||||
return button as HTMLElement;
|
||||
}
|
||||
|
||||
function getRadio(container: HTMLElement, text: string, position = 0) {
|
||||
const radios = Array.from(container.querySelectorAll('[data-u-comp="radio"]'))
|
||||
.filter((item) => item.textContent === text);
|
||||
expect(radios[position]).toBeTruthy();
|
||||
return radios[position].querySelector('input') as HTMLInputElement;
|
||||
}
|
||||
|
||||
function getAddCondition(container: HTMLElement) {
|
||||
const trigger = Array.from(container.querySelectorAll('div'))
|
||||
.find((item) => item.textContent === 'Add condition');
|
||||
expect(trigger).toBeTruthy();
|
||||
return trigger as HTMLElement;
|
||||
}
|
||||
|
||||
function setCellValue(worksheet: Worksheet, row: number, col: number, value: string | number) {
|
||||
worksheet.getCellMatrix().setValue(row, col, { v: value, t: typeof value === 'number' ? 2 : 1 });
|
||||
}
|
||||
|
||||
function setDuplicateNamesForConditionTests(worksheet: Worksheet) {
|
||||
setCellValue(worksheet, 1, 0, 'Sam');
|
||||
setCellValue(worksheet, 1, 1, 20);
|
||||
setCellValue(worksheet, 2, 0, 'Amy');
|
||||
setCellValue(worksheet, 2, 1, 10);
|
||||
setCellValue(worksheet, 3, 0, 'Sam');
|
||||
setCellValue(worksheet, 3, 1, 30);
|
||||
}
|
||||
|
||||
describe('Sheets sort views', () => {
|
||||
let root: Root | undefined;
|
||||
let container: HTMLDivElement | undefined;
|
||||
let currentTestBed: ReturnType<typeof createSortViewTestBed> | undefined;
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
root?.unmount();
|
||||
});
|
||||
container?.remove();
|
||||
currentTestBed?.univer.dispose();
|
||||
root = undefined;
|
||||
container = undefined;
|
||||
currentTestBed = undefined;
|
||||
});
|
||||
|
||||
it('sorts the selected data rows from the embedded descending action', async () => {
|
||||
currentTestBed = createSortViewTestBed();
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
let closeCount = 0;
|
||||
|
||||
await renderWithInjector(
|
||||
root,
|
||||
currentTestBed, (
|
||||
<EmbedSortBtn
|
||||
range={{ startRow: 0, endRow: 3, startColumn: 0, endColumn: 2 }}
|
||||
colIndex={1}
|
||||
onClose={() => {
|
||||
closeCount += 1;
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
await act(async () => {
|
||||
getButton(container!, 'Descending').dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
expect(getColumnValues(currentTestBed.worksheet, 0, [0, 1, 2, 3])).toEqual(['Name', 'Ben', 'Cat', 'Amy']);
|
||||
expect(getColumnValues(currentTestBed.worksheet, 1, [0, 1, 2, 3])).toEqual(['Score', 30, 20, 10]);
|
||||
expect(closeCount).toBe(1);
|
||||
});
|
||||
|
||||
it('applies custom sort with the first row excluded from sorting', async () => {
|
||||
currentTestBed = createSortViewTestBed();
|
||||
currentTestBed.sortUIService.showCustomSortPanel({
|
||||
unitId: UNIT_ID,
|
||||
subUnitId: SUB_UNIT_ID,
|
||||
range: { startRow: 0, endRow: 3, startColumn: 0, endColumn: 2 },
|
||||
colIndex: 1,
|
||||
});
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
|
||||
await renderWithInjector(root, currentTestBed, <CustomSortPanel />);
|
||||
|
||||
const titleCheckbox = container.querySelector('[data-u-comp="checkbox"] input') as HTMLInputElement;
|
||||
expect(titleCheckbox).toBeTruthy();
|
||||
|
||||
await act(async () => {
|
||||
titleCheckbox.click();
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
getButton(container!, 'Confirm').dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
expect(getColumnValues(currentTestBed.worksheet, 0, [0, 1, 2, 3])).toEqual(['Name', 'Amy', 'Ben', 'Cat']);
|
||||
expect(getColumnValues(currentTestBed.worksheet, 1, [0, 1, 2, 3])).toEqual(['Score', 10, 30, 20]);
|
||||
expect(currentTestBed.sortUIService.customSortState()).toEqual({ show: false });
|
||||
});
|
||||
|
||||
it('reports the selected extend range option through the rendered confirmation body', async () => {
|
||||
currentTestBed = createSortViewTestBed();
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
const selectedOptions: string[] = [];
|
||||
|
||||
await renderWithInjector(
|
||||
root,
|
||||
currentTestBed,
|
||||
<ExtendConfirm onChange={(value) => selectedOptions.push(value)} />
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
getRadio(container!, 'Extend range sorting').click();
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
getRadio(container!, 'Keep range sorting').click();
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
expect(selectedOptions).toEqual(['1', '0']);
|
||||
});
|
||||
|
||||
it('applies an added custom sort condition as a secondary rule', async () => {
|
||||
currentTestBed = createSortViewTestBed();
|
||||
setDuplicateNamesForConditionTests(currentTestBed.worksheet);
|
||||
currentTestBed.sortUIService.showCustomSortPanel({
|
||||
unitId: UNIT_ID,
|
||||
subUnitId: SUB_UNIT_ID,
|
||||
range: { startRow: 1, endRow: 3, startColumn: 0, endColumn: 1 },
|
||||
colIndex: 0,
|
||||
});
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
|
||||
await renderWithInjector(root, currentTestBed, <CustomSortPanel />);
|
||||
|
||||
await act(async () => {
|
||||
getAddCondition(container!).click();
|
||||
await awaitTime(260);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
getRadio(container!, 'Descending', 1).click();
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
getButton(container!, 'Confirm').dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
expect(getColumnValues(currentTestBed.worksheet, 0, [1, 2, 3])).toEqual(['Amy', 'Sam', 'Sam']);
|
||||
expect(getColumnValues(currentTestBed.worksheet, 1, [1, 2, 3])).toEqual([10, 30, 20]);
|
||||
expect(currentTestBed.sortUIService.customSortState()).toEqual({ show: false });
|
||||
});
|
||||
|
||||
it('removes an added custom sort condition before confirming', async () => {
|
||||
currentTestBed = createSortViewTestBed();
|
||||
setDuplicateNamesForConditionTests(currentTestBed.worksheet);
|
||||
currentTestBed.sortUIService.showCustomSortPanel({
|
||||
unitId: UNIT_ID,
|
||||
subUnitId: SUB_UNIT_ID,
|
||||
range: { startRow: 1, endRow: 3, startColumn: 0, endColumn: 1 },
|
||||
colIndex: 0,
|
||||
});
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
|
||||
await renderWithInjector(root, currentTestBed, <CustomSortPanel />);
|
||||
|
||||
await act(async () => {
|
||||
getAddCondition(container!).click();
|
||||
await awaitTime(260);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
getRadio(container!, 'Descending', 1).click();
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
const removeLinks = Array.from(container.querySelectorAll('a'));
|
||||
expect(removeLinks[1]).toBeTruthy();
|
||||
|
||||
await act(async () => {
|
||||
removeLinks[1].dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
expect(Array.from(container.querySelectorAll('[data-u-comp="radio"]'))
|
||||
.filter((item) => item.textContent === 'Descending')).toHaveLength(1);
|
||||
|
||||
await act(async () => {
|
||||
getButton(container!, 'Confirm').dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
await awaitTime(20);
|
||||
});
|
||||
|
||||
expect(getColumnValues(currentTestBed.worksheet, 0, [1, 2, 3])).toEqual(['Amy', 'Sam', 'Sam']);
|
||||
expect(getColumnValues(currentTestBed.worksheet, 1, [1, 2, 3])).toEqual([10, 20, 30]);
|
||||
expect(currentTestBed.sortUIService.customSortState()).toEqual({ show: false });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { IOrderRule } from '@univerjs/sheets-sort';
|
||||
import type { LocaleKey } from '../../locale/types';
|
||||
import type { ISheetSortLocation } from '../../services/sheets-sort-ui.service';
|
||||
import { LocaleService } from '@univerjs/core';
|
||||
import { Button, Checkbox, MobileSelect } from '@univerjs/design';
|
||||
import { DeleteEmptyIcon } from '@univerjs/icons';
|
||||
import { SheetsSortService, SortType } from '@univerjs/sheets-sort';
|
||||
import { useDependency, useObservable } from '@univerjs/ui';
|
||||
import { useState } from 'react';
|
||||
import { SheetsSortUIService } from '../../services/sheets-sort-ui.service';
|
||||
|
||||
export function MobileCustomSortPanel() {
|
||||
const sheetsSortUIService = useDependency(SheetsSortUIService);
|
||||
const state = useObservable(sheetsSortUIService.customSortState$);
|
||||
|
||||
if (!state?.location) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <MobileCustomSortPanelContent location={state.location} />;
|
||||
}
|
||||
|
||||
function MobileCustomSortPanelContent({ location }: { location: ISheetSortLocation }) {
|
||||
const sheetsSortService = useDependency(SheetsSortService);
|
||||
const sheetsSortUIService = useDependency(SheetsSortUIService);
|
||||
const localeService = useDependency(LocaleService);
|
||||
const { range, unitId, subUnitId } = location;
|
||||
const [hasTitle, setHasTitle] = useState(false);
|
||||
const [orderRules, setOrderRules] = useState<IOrderRule[]>([
|
||||
{ type: SortType.ASC, colIndex: range.startColumn },
|
||||
]);
|
||||
const titles = sheetsSortUIService.getTitles(hasTitle);
|
||||
const canAddCondition = orderRules.length < titles.length;
|
||||
|
||||
function updateRule(index: number, update: Partial<IOrderRule>) {
|
||||
setOrderRules((current) => current.map((rule, ruleIndex) =>
|
||||
ruleIndex === index ? { ...rule, ...update } : rule));
|
||||
}
|
||||
|
||||
function addCondition() {
|
||||
setOrderRules((current) => {
|
||||
const nextTitle = titles.find((title) => !current.some((rule) => rule.colIndex === title.index));
|
||||
return nextTitle
|
||||
? [...current, { type: SortType.ASC, colIndex: nextTitle.index }]
|
||||
: current;
|
||||
});
|
||||
}
|
||||
|
||||
function setTitle(value: boolean) {
|
||||
setHasTitle(value);
|
||||
sheetsSortUIService.setSelection(
|
||||
unitId,
|
||||
subUnitId,
|
||||
value ? { ...range, startRow: range.startRow + 1 } : range
|
||||
);
|
||||
}
|
||||
|
||||
function apply() {
|
||||
sheetsSortService.applySort({ range, orderRules, hasTitle }, unitId, subUnitId);
|
||||
sheetsSortUIService.closeCustomSortPanel();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="univer-grid univer-gap-4">
|
||||
<div className="univer-flex univer-items-center univer-justify-between univer-gap-3">
|
||||
<Checkbox checked={hasTitle} onChange={(value) => setTitle(Boolean(value))}>
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.first-row-check')}
|
||||
</Checkbox>
|
||||
<Button variant="text" disabled={!canAddCondition} onClick={addCondition}>
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.add-condition')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="univer-grid univer-gap-3">
|
||||
{orderRules.map((rule, index) => {
|
||||
const availableTitles = titles.filter((title) =>
|
||||
title.index === rule.colIndex || !orderRules.some((item) => item.colIndex === title.index));
|
||||
|
||||
return (
|
||||
<div
|
||||
key={rule.colIndex}
|
||||
className="
|
||||
univer-grid univer-gap-2 univer-rounded-xl univer-bg-gray-50 univer-p-3
|
||||
dark:!univer-bg-gray-800
|
||||
"
|
||||
>
|
||||
<div className="univer-flex univer-items-center univer-gap-2">
|
||||
<MobileSelect
|
||||
className="!univer-min-w-0 univer-flex-1"
|
||||
value={`${rule.colIndex}`}
|
||||
options={availableTitles.map((title) => ({
|
||||
label: title.label,
|
||||
value: `${title.index}`,
|
||||
}))}
|
||||
onChange={(value) => updateRule(index, { colIndex: Number(value) })}
|
||||
/>
|
||||
{orderRules.length > 1 && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="text"
|
||||
aria-label={localeService.t<LocaleKey>('sheets-sort-ui.dialog.delete-condition')}
|
||||
onClick={() => setOrderRules((current) => current.filter((_, ruleIndex) => ruleIndex !== index))}
|
||||
>
|
||||
<DeleteEmptyIcon />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<MobileSelect
|
||||
className="univer-w-full"
|
||||
value={rule.type}
|
||||
options={[
|
||||
{
|
||||
label: localeService.t<LocaleKey>('sheets-sort-ui.general.sort-asc'),
|
||||
value: SortType.ASC,
|
||||
},
|
||||
{
|
||||
label: localeService.t<LocaleKey>('sheets-sort-ui.general.sort-desc'),
|
||||
value: SortType.DESC,
|
||||
},
|
||||
]}
|
||||
onChange={(value) => updateRule(index, { type: value as SortType })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className="
|
||||
univer-sticky univer-bottom-0 univer-grid univer-grid-cols-2 univer-gap-3 univer-bg-gray-0 univer-py-3
|
||||
dark:!univer-bg-gray-900
|
||||
"
|
||||
>
|
||||
<Button size="large" onClick={() => sheetsSortUIService.closeCustomSortPanel()}>
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.cancel')}
|
||||
</Button>
|
||||
<Button size="large" variant="primary" onClick={apply}>
|
||||
{localeService.t<LocaleKey>('sheets-sort-ui.dialog.confirm')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
27
packages/sheets-sort-ui/tailwind.config.ts
Normal file
27
packages/sheets-sort-ui/tailwind.config.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Co., Ltd.
|
||||
*
|
||||
* 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 type { Config } from 'tailwindcss';
|
||||
import preset from '@univerjs-infra/shared/tailwind';
|
||||
|
||||
const config: Config = {
|
||||
presets: [preset],
|
||||
content: [
|
||||
'./src/**/*.{js,ts,jsx,tsx}',
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
8
packages/sheets-sort-ui/tsconfig.json
Normal file
8
packages/sheets-sort-ui/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
18
packages/sheets-sort-ui/tsconfig.node.json
Normal file
18
packages/sheets-sort-ui/tsconfig.node.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/node",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"noEmit": false,
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/__tests__/**",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts"
|
||||
]
|
||||
}
|
||||
3
packages/sheets-sort-ui/vitest.config.ts
Normal file
3
packages/sheets-sort-ui/vitest.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import createConfig from '@univerjs-infra/shared/vitest';
|
||||
|
||||
export default createConfig();
|
||||
Reference in New Issue
Block a user