chore: migrate to eslint 8+ (#1235)

* chore: migrate to eslint 8+

* chore: remove unused deps

* fix: fix type check

* chore(eslint): add missing rules
This commit is contained in:
白熱
2024-01-23 20:57:03 -06:00
committed by GitHub
parent 758a80bcef
commit ee85ccbef9
228 changed files with 3024 additions and 2419 deletions
-39
View File
@@ -1,39 +0,0 @@
# Build
dist
lib
local
.turbo
# Coverage
coverage-report/
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Dependency directories
node_modules
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# dotenv environment variables file
.env
.env.test
.cache/
# yarn v2
.yarn
# Vite
vite-env.d.ts
-193
View File
@@ -1,193 +0,0 @@
const { resolve } = require('node:path');
const tsConfig = require('./tsconfig.json');
/**
* @type {import('eslint').Linter.Config}
*/
const config = {
root: true,
env: {
browser: true,
es2021: true,
},
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
'prettier',
'import',
'import-newlines',
'unused-imports',
'simple-import-sort',
'react',
'header',
],
extends: [
'airbnb-base', // https://www.npmjs.com/package/eslint-config-airbnb-base
'airbnb-typescript/base', // https://www.npmjs.com/package/eslint-config-airbnb-typescript
// "plugin:@typescript-eslint/recommended",// no need https://typescript-eslint.io/
'plugin:prettier/recommended',
'prettier',
],
rules: {
'header/header': [
2,
'block',
[
'*',
' * Copyright 2023-present DreamNum Inc.',
' *',
' * Licensed under the Apache License, Version 2.0 (the "License");',
' * you may not use this file except in compliance with the License.',
' * You may obtain a copy of the License at',
' *',
' * http://www.apache.org/licenses/LICENSE-2.0',
' *',
' * Unless required by applicable law or agreed to in writing, software',
' * distributed under the License is distributed on an "AS IS" BASIS,',
' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
' * See the License for the specific language governing permissions and',
' * limitations under the License.',
' ',
],
2,
],
'no-cond-assign': 'off',
'no-restricted-globals': 'off',
'import/prefer-default-export': 'off',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: true,
},
],
// turn on errors for missing imports
'import/no-unresolved': [
2,
{
ignore: ['\\.less$', '^@'],
},
],
'import/no-cycle': 'warn',
'no-param-reassign': 'off',
'no-bitwise': 'off',
'default-case': 'off',
'class-methods-use-this': 'off',
'consistent-return': 'off',
'no-underscore-dangle': 'off',
'no-restricted-syntax': 'off',
'max-classes-per-file': 'off',
'prefer-destructuring': 'off',
'no-plusplus': 'off',
'no-return-assign': 'off',
'no-continue': 'off',
'no-loop-func': 'off',
'@typescript-eslint/naming-convention': [
'warn',
// Interfaces' names should start with a capital 'I'.
{
selector: 'interface',
format: ['PascalCase'],
custom: {
regex: '^I[A-Z0-9]',
match: true,
},
},
// Private fields of a class should start with an underscore '_'.
{
selector: ['classMethod', 'classProperty'],
modifiers: ['private'],
format: ['camelCase'],
leadingUnderscore: 'require',
},
],
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-shadow': 'off',
'@typescript-eslint/no-loop-func': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'guard-for-in': 'off',
'no-prototype-builtins': 'off',
'no-lonely-if': 'off',
radix: 'off',
'no-nested-ternary': 'off',
'no-new': 'off',
'no-unused-expressions': 'off',
'no-console': ['error', { allow: ["warn", "error"] }],
'no-multi-assign': 'off',
'no-restricted-properties': 'off',
'no-control-regex': 'off',
'no-await-in-loop': 'off',
'@typescript-eslint/array-type': [
'error',
{
default: 'array-simple',
},
],
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
accessibility: 'no-public',
},
],
'spaced-comment': 'off',
eqeqeq: [
'error',
'always',
{
null: 'ignore',
},
],
// eslint-plugin-unused-imports
'@typescript-eslint/consistent-type-assertions': 'off',
'@typescript-eslint/default-param-last': 'off',
'@typescript-eslint/lines-between-class-members': [
'warn',
{ enforce: [{ blankLine: 'always', prev: '*', next: 'method' }] },
{ exceptAfterSingleLine: true },
],
// '@typescript-eslint/member-ordering': 'error',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-redeclare': 'off', // dependency interface and dependency token share the same name
'@typescript-eslint/no-unused-vars': 'warn',
'grouped-accessor-pairs': 'off',
'no-magic-numbers': ['warn', { ignore: [0, 1, -1, 2] }],
'no-unsafe-optional-chaining': 'off',
'prefer-regex-literals': 'off',
'simple-import-sort/exports': 'error',
'simple-import-sort/imports': 'error',
'unused-imports/no-unused-imports': 'error',
'@typescript-eslint/consistent-type-definitions': ['warn'],
'@typescript-eslint/consistent-type-imports': ['warn'],
'react/self-closing-comp': [
'error',
{
component: true,
},
],
},
// https://www.npmjs.com/package/eslint-import-resolver-typescript
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
node: {
extensions: ['.ts', '.tsx'],
},
},
},
overrides: [
...tsConfig.references.map(({ path }) => ({
files: [`${path}/src/**/*.ts`, `${path}/src/**/*.tsx`],
parserOptions: {
project: resolve(__dirname, './tsconfig.eslint.json'),
},
})),
],
};
module.exports = config;
+1 -1
View File
@@ -13,4 +13,4 @@ close #
<!-- A description of the proposed changes. -->
<!-- How to test them. -->
<!-- How to test them. -->
+16 -15
View File
@@ -1,22 +1,23 @@
name: 📌 Check PR
on:
pull_request:
types:
- opened
- edited
- synchronize
pull_request:
types:
- opened
- edited
- synchronize
permissions:
pull-requests: read
pull-requests: read
jobs:
validate:
name: validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
subjectPattern: ^(?![A-Z]).+$
validate:
name: validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
subjectPattern: ^(?![A-Z]).+$
+10 -8
View File
@@ -1,12 +1,14 @@
name: 🌍 Issues Translator
on:
issue_comment:
types: [created]
issues:
types: [opened]
issue_comment:
types: [created]
issues:
types: [opened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: usthe/issues-translate-action@v2.7
build:
runs-on: ubuntu-latest
steps:
- uses: usthe/issues-translate-action@v2.7
+2 -2
View File
@@ -2,7 +2,7 @@ name: 📤 Preview Deploy
on:
workflow_run:
workflows: ['📦 Build']
workflows: [📦 Build]
types: [completed]
permissions:
@@ -133,7 +133,7 @@ jobs:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID}}
vercel-project-id: ${{ secrets.PROJECT_ID}}
vercel-args: '--prod'
vercel-args: --prod
- name: 🚀 Deploy to Vercel (demo)
uses: amondnet/vercel-action@v25
-8
View File
@@ -1,8 +0,0 @@
{
"printWidth": 120,
"semi": true,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "es5",
"endOfLine": "auto"
}
+25 -9
View File
@@ -1,5 +1,21 @@
import { type StorybookConfig } from 'storybook';
import { join, dirname } from 'path';
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { dirname, join } from 'node:path';
import type { StorybookConfig } from '@storybook/react-vite';
/**
* This function is used to resolve the absolute path of a package.
@@ -14,22 +30,22 @@ const config: StorybookConfig = {
{
directory: '../packages/design/src/**',
files: '*.stories.@(js|jsx|mjs|ts|tsx)',
titlePrefix: 'Design'
titlePrefix: 'Design',
},
{
directory: '../packages/ui/src/**',
files: '*.stories.@(js|jsx|mjs|ts|tsx)',
titlePrefix: 'Base UI'
titlePrefix: 'Base UI',
},
{
directory: '../packages/sheets-numfmt/src/**',
files: '*.stories.@(js|jsx|mjs|ts|tsx)',
titlePrefix: 'Numfmt'
titlePrefix: 'Numfmt',
},
{
directory: '../packages/find-replace/src/**',
files: '*.stories.@(js|jsx|mjs|ts|tsx)',
titlePrefix: 'Find & Replace'
titlePrefix: 'Find & Replace',
},
],
addons: [
@@ -39,18 +55,18 @@ const config: StorybookConfig = {
getAbsolutePath('@storybook/addon-docs'),
],
framework: {
name: getAbsolutePath('@storybook/react-vite'),
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: true,
},
async viteFinal(config, options) {
async viteFinal(config) {
config.css = {
modules: {
localsConvention: 'camelCaseOnly',
generateScopedName: 'univer-[local]',
}
},
};
return config;
+21 -5
View File
@@ -1,11 +1,27 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { type Preview } from '@storybook/react';
import type { Preview } from '@storybook/react';
import { defaultTheme, greenTheme, themeInstance } from '@univerjs/design';
export const themes: Record<string, Record<string, string>> = {
default: defaultTheme,
green: greenTheme
}
green: greenTheme,
};
const preview: Preview = {
parameters: {
@@ -36,8 +52,8 @@ const preview: Preview = {
return (
<Story />
)
}]
);
}],
};
export default preview;
+43 -8
View File
@@ -1,12 +1,4 @@
{
"editor.codeActionsOnSave": {
"mode": "explicit",
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
},
"editor.formatOnSave": false,
"javascript.preferences.importModuleSpecifier": "relative",
"typescript.preferences.importModuleSpecifier": "relative",
"commentTranslate.targetLanguage": "zh-CN",
@@ -90,4 +82,47 @@
"Xinwei"
],
"vsicons.presets.angular": false,
// Enable the ESlint flat config support
"eslint.experimental.useFlatConfig": true,
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never",
"source.fixAll.stylelint": "explicit"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off" },
{ "rule": "format/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml",
"toml"
]
}
+1 -1
View File
@@ -171,4 +171,4 @@ npm create @univerjs/cli init <project-name>
## Links
* [How to Contribute to Facade API](./packages/facade/docs/CONTRIBUTING.md)
* [How to Contribute to Facade API](./packages/facade/docs/CONTRIBUTING.md)
-2
View File
@@ -1,5 +1,3 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
-1
View File
@@ -57,7 +57,6 @@ Highlights of Univer:
| [Multi Instances](https://univer.work/playground/?title=Multi%20Instance)<br>Run multi Univer instances on the same page | ![](./docs/img/multi-instances.png) |
| [Uniscript](https://univer.work/playground/?title=Uniscript)<br>Use Uniscript to automate your workflow | ![](./docs/img/uniscript.png) |
## Usage
We recommend to import Univer as a npm package. Please checkout the [Quick Start](https://univer.work/en-us/guides/quick-start/) section on the documentation website. We also have an [online playground](https://univer.work/playground/) which can help you preview Univer without setting up the development environment.
+12 -12
View File
@@ -1,13 +1,13 @@
coverage:
patch: false
status:
project:
default:
# basic
target: auto
threshold: 0
base: auto
# advanced settings
if_ci_failed: error
informational: false
only_pulls: false
patch: false
status:
project:
default:
# basic
target: auto
threshold: 0
base: auto
# advanced settings
if_ci_failed: error
informational: false
only_pulls: false
+19
View File
@@ -0,0 +1,19 @@
{
"name": "@univerjs/infra",
"version": "0.1.0-beta.2",
"private": true,
"description": "Some infrastructures for univerjs",
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"homepage": "https://github.com/dream-num/univer",
"repository": {
"type": "git",
"url": "https://github.com/dream-num/univer.git"
},
"keywords": [],
"exports": {
"./tsconfigs/*.json": "./tsconfigs/*",
"./tsconfigs/*": "./tsconfigs/*.json"
},
"dependencies": {}
}
+24
View File
@@ -0,0 +1,24 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ESNext",
"jsx": "react",
"lib": ["ESNext", "DOM", "DOM.Iterable", "WebWorker"],
"useDefineForClassFields": true,
"experimentalDecorators": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"strict": true,
"strictPropertyInitialization": false,
"noFallthroughCasesInSwitch": true,
"noEmit": true,
"isolatedModules": true,
"skipLibCheck": true
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"skipLibCheck": true
}
}
+338 -323
View File
@@ -1,437 +1,452 @@
import { test, expect, type Page } from '@playwright/test';
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, type Page, test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
await page.goto('https://demo.playwright.dev/todomvc');
});
const TODO_ITEMS = [
'buy some cheese',
'feed the cat',
'book a doctors appointment'
'buy some cheese',
'feed the cat',
'book a doctors appointment',
];
test.describe('New Todo', () => {
test('should allow me to add todo items', async ({ page }) => {
test('should allow me to add todo items', async ({ page }) => {
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
const newTodo = page.getByPlaceholder('What needs to be done?');
// Create 1st todo.
await newTodo.fill(TODO_ITEMS[0]);
await newTodo.press('Enter');
// Create 1st todo.
await newTodo.fill(TODO_ITEMS[0]);
await newTodo.press('Enter');
// Make sure the list only has one todo item.
await expect(page.getByTestId('todo-title')).toHaveText([
TODO_ITEMS[0]
]);
// Make sure the list only has one todo item.
await expect(page.getByTestId('todo-title')).toHaveText([
TODO_ITEMS[0],
]);
// Create 2nd todo.
await newTodo.fill(TODO_ITEMS[1]);
await newTodo.press('Enter');
// Create 2nd todo.
await newTodo.fill(TODO_ITEMS[1]);
await newTodo.press('Enter');
// Make sure the list now has two todo items.
await expect(page.getByTestId('todo-title')).toHaveText([
TODO_ITEMS[0],
TODO_ITEMS[1]
]);
// Make sure the list now has two todo items.
await expect(page.getByTestId('todo-title')).toHaveText([
TODO_ITEMS[0],
TODO_ITEMS[1],
]);
await checkNumberOfTodosInLocalStorage(page, 2);
});
await checkNumberOfTodosInLocalStorage(page, 2);
});
test('should clear text input field when an item is added', async ({ page }) => {
test('should clear text input field when an item is added', async ({ page }) => {
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
const newTodo = page.getByPlaceholder('What needs to be done?');
// Create one todo item.
await newTodo.fill(TODO_ITEMS[0]);
await newTodo.press('Enter');
// Create one todo item.
await newTodo.fill(TODO_ITEMS[0]);
await newTodo.press('Enter');
// Check that input is empty.
await expect(newTodo).toBeEmpty();
await checkNumberOfTodosInLocalStorage(page, 1);
});
// Check that input is empty.
await expect(newTodo).toBeEmpty();
await checkNumberOfTodosInLocalStorage(page, 1);
});
test('should append new items to the bottom of the list', async ({ page }) => {
test('should append new items to the bottom of the list', async ({ page }) => {
// Create 3 items.
await createDefaultTodos(page);
await createDefaultTodos(page);
// create a todo count locator
const todoCount = page.getByTestId('todo-count')
// Check test using different methods.
await expect(page.getByText('3 items left')).toBeVisible();
await expect(todoCount).toHaveText('3 items left');
await expect(todoCount).toContainText('3');
await expect(todoCount).toHaveText(/3/);
// create a todo count locator
const todoCount = page.getByTestId('todo-count');
// Check all items in one call.
await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
await checkNumberOfTodosInLocalStorage(page, 3);
});
// Check test using different methods.
await expect(page.getByText('3 items left')).toBeVisible();
await expect(todoCount).toHaveText('3 items left');
await expect(todoCount).toContainText('3');
await expect(todoCount).toHaveText(/3/);
// Check all items in one call.
await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
await checkNumberOfTodosInLocalStorage(page, 3);
});
});
test.describe('Mark all as completed', () => {
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
await checkNumberOfTodosInLocalStorage(page, 3);
});
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
await checkNumberOfTodosInLocalStorage(page, 3);
});
test.afterEach(async ({ page }) => {
await checkNumberOfTodosInLocalStorage(page, 3);
});
test.afterEach(async ({ page }) => {
await checkNumberOfTodosInLocalStorage(page, 3);
});
test('should allow me to mark all items as completed', async ({ page }) => {
test('should allow me to mark all items as completed', async ({ page }) => {
// Complete all todos.
await page.getByLabel('Mark all as complete').check();
await page.getByLabel('Mark all as complete').check();
// Ensure all todos have 'completed' class.
await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
});
// Ensure all todos have 'completed' class.
await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
});
test('should allow me to clear the complete state of all items', async ({ page }) => {
const toggleAll = page.getByLabel('Mark all as complete');
// Check and then immediately uncheck.
await toggleAll.check();
await toggleAll.uncheck();
test('should allow me to clear the complete state of all items', async ({ page }) => {
const toggleAll = page.getByLabel('Mark all as complete');
// Check and then immediately uncheck.
await toggleAll.check();
await toggleAll.uncheck();
// Should be no completed classes.
await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
});
// Should be no completed classes.
await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
});
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
const toggleAll = page.getByLabel('Mark all as complete');
await toggleAll.check();
await expect(toggleAll).toBeChecked();
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
const toggleAll = page.getByLabel('Mark all as complete');
await toggleAll.check();
await expect(toggleAll).toBeChecked();
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
// Uncheck first todo.
const firstTodo = page.getByTestId('todo-item').nth(0);
await firstTodo.getByRole('checkbox').uncheck();
// Uncheck first todo.
const firstTodo = page.getByTestId('todo-item').nth(0);
await firstTodo.getByRole('checkbox').uncheck();
// Reuse toggleAll locator and make sure its not checked.
await expect(toggleAll).not.toBeChecked();
// Reuse toggleAll locator and make sure its not checked.
await expect(toggleAll).not.toBeChecked();
await firstTodo.getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
await firstTodo.getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
// Assert the toggle all is checked again.
await expect(toggleAll).toBeChecked();
});
// Assert the toggle all is checked again.
await expect(toggleAll).toBeChecked();
});
});
test.describe('Item', () => {
test('should allow me to mark items as complete', async ({ page }) => {
test('should allow me to mark items as complete', async ({ page }) => {
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
const newTodo = page.getByPlaceholder('What needs to be done?');
// Create two items.
for (const item of TODO_ITEMS.slice(0, 2)) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
// Create two items.
for (const item of TODO_ITEMS.slice(0, 2)) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
// Check first item.
const firstTodo = page.getByTestId('todo-item').nth(0);
await firstTodo.getByRole('checkbox').check();
await expect(firstTodo).toHaveClass('completed');
// Check first item.
const firstTodo = page.getByTestId('todo-item').nth(0);
await firstTodo.getByRole('checkbox').check();
await expect(firstTodo).toHaveClass('completed');
// Check second item.
const secondTodo = page.getByTestId('todo-item').nth(1);
await expect(secondTodo).not.toHaveClass('completed');
await secondTodo.getByRole('checkbox').check();
// Check second item.
const secondTodo = page.getByTestId('todo-item').nth(1);
await expect(secondTodo).not.toHaveClass('completed');
await secondTodo.getByRole('checkbox').check();
// Assert completed class.
await expect(firstTodo).toHaveClass('completed');
await expect(secondTodo).toHaveClass('completed');
});
// Assert completed class.
await expect(firstTodo).toHaveClass('completed');
await expect(secondTodo).toHaveClass('completed');
});
test('should allow me to un-mark items as complete', async ({ page }) => {
test('should allow me to un-mark items as complete', async ({ page }) => {
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
const newTodo = page.getByPlaceholder('What needs to be done?');
// Create two items.
for (const item of TODO_ITEMS.slice(0, 2)) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
// Create two items.
for (const item of TODO_ITEMS.slice(0, 2)) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
const firstTodo = page.getByTestId('todo-item').nth(0);
const secondTodo = page.getByTestId('todo-item').nth(1);
const firstTodoCheckbox = firstTodo.getByRole('checkbox');
const firstTodo = page.getByTestId('todo-item').nth(0);
const secondTodo = page.getByTestId('todo-item').nth(1);
const firstTodoCheckbox = firstTodo.getByRole('checkbox');
await firstTodoCheckbox.check();
await expect(firstTodo).toHaveClass('completed');
await expect(secondTodo).not.toHaveClass('completed');
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await firstTodoCheckbox.check();
await expect(firstTodo).toHaveClass('completed');
await expect(secondTodo).not.toHaveClass('completed');
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await firstTodoCheckbox.uncheck();
await expect(firstTodo).not.toHaveClass('completed');
await expect(secondTodo).not.toHaveClass('completed');
await checkNumberOfCompletedTodosInLocalStorage(page, 0);
});
await firstTodoCheckbox.uncheck();
await expect(firstTodo).not.toHaveClass('completed');
await expect(secondTodo).not.toHaveClass('completed');
await checkNumberOfCompletedTodosInLocalStorage(page, 0);
});
test('should allow me to edit an item', async ({ page }) => {
await createDefaultTodos(page);
test('should allow me to edit an item', async ({ page }) => {
await createDefaultTodos(page);
const todoItems = page.getByTestId('todo-item');
const secondTodo = todoItems.nth(1);
await secondTodo.dblclick();
await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
const todoItems = page.getByTestId('todo-item');
const secondTodo = todoItems.nth(1);
await secondTodo.dblclick();
await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
// Explicitly assert the new text value.
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
'buy some sausages',
TODO_ITEMS[2]
]);
await checkTodosInLocalStorage(page, 'buy some sausages');
});
// Explicitly assert the new text value.
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
'buy some sausages',
TODO_ITEMS[2],
]);
await checkTodosInLocalStorage(page, 'buy some sausages');
});
});
test.describe('Editing', () => {
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
await checkNumberOfTodosInLocalStorage(page, 3);
});
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
await checkNumberOfTodosInLocalStorage(page, 3);
});
test('should hide other controls when editing', async ({ page }) => {
const todoItem = page.getByTestId('todo-item').nth(1);
await todoItem.dblclick();
await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
await expect(todoItem.locator('label', {
hasText: TODO_ITEMS[1],
})).not.toBeVisible();
await checkNumberOfTodosInLocalStorage(page, 3);
});
test('should hide other controls when editing', async ({ page }) => {
const todoItem = page.getByTestId('todo-item').nth(1);
await todoItem.dblclick();
await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
await expect(todoItem.locator('label', {
hasText: TODO_ITEMS[1],
})).not.toBeVisible();
await checkNumberOfTodosInLocalStorage(page, 3);
});
test('should save edits on blur', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
test('should save edits on blur', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
'buy some sausages',
TODO_ITEMS[2],
]);
await checkTodosInLocalStorage(page, 'buy some sausages');
});
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
'buy some sausages',
TODO_ITEMS[2],
]);
await checkTodosInLocalStorage(page, 'buy some sausages');
});
test('should trim entered text', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
test('should trim entered text', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
'buy some sausages',
TODO_ITEMS[2],
]);
await checkTodosInLocalStorage(page, 'buy some sausages');
});
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
'buy some sausages',
TODO_ITEMS[2],
]);
await checkTodosInLocalStorage(page, 'buy some sausages');
});
test('should remove the item if an empty text string was entered', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
test('should remove the item if an empty text string was entered', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
TODO_ITEMS[2],
]);
});
await expect(todoItems).toHaveText([
TODO_ITEMS[0],
TODO_ITEMS[2],
]);
});
test('should cancel edits on escape', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
await expect(todoItems).toHaveText(TODO_ITEMS);
});
test('should cancel edits on escape', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
await expect(todoItems).toHaveText(TODO_ITEMS);
});
});
test.describe('Counter', () => {
test('should display the current number of todo items', async ({ page }) => {
test('should display the current number of todo items', async ({ page }) => {
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
// create a todo count locator
const todoCount = page.getByTestId('todo-count')
const newTodo = page.getByPlaceholder('What needs to be done?');
await newTodo.fill(TODO_ITEMS[0]);
await newTodo.press('Enter');
// create a todo count locator
const todoCount = page.getByTestId('todo-count');
await expect(todoCount).toContainText('1');
await newTodo.fill(TODO_ITEMS[0]);
await newTodo.press('Enter');
await newTodo.fill(TODO_ITEMS[1]);
await newTodo.press('Enter');
await expect(todoCount).toContainText('2');
await expect(todoCount).toContainText('1');
await checkNumberOfTodosInLocalStorage(page, 2);
});
await newTodo.fill(TODO_ITEMS[1]);
await newTodo.press('Enter');
await expect(todoCount).toContainText('2');
await checkNumberOfTodosInLocalStorage(page, 2);
});
});
test.describe('Clear completed button', () => {
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
});
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
});
test('should display the correct text', async ({ page }) => {
await page.locator('.todo-list li .toggle').first().check();
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
});
test('should display the correct text', async ({ page }) => {
await page.locator('.todo-list li .toggle').first().check();
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
});
test('should remove completed items when clicked', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).getByRole('checkbox').check();
await page.getByRole('button', { name: 'Clear completed' }).click();
await expect(todoItems).toHaveCount(2);
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
});
test('should remove completed items when clicked', async ({ page }) => {
const todoItems = page.getByTestId('todo-item');
await todoItems.nth(1).getByRole('checkbox').check();
await page.getByRole('button', { name: 'Clear completed' }).click();
await expect(todoItems).toHaveCount(2);
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
});
test('should be hidden when there are no items that are completed', async ({ page }) => {
await page.locator('.todo-list li .toggle').first().check();
await page.getByRole('button', { name: 'Clear completed' }).click();
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
});
test('should be hidden when there are no items that are completed', async ({ page }) => {
await page.locator('.todo-list li .toggle').first().check();
await page.getByRole('button', { name: 'Clear completed' }).click();
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
});
});
test.describe('Persistence', () => {
test('should persist its data', async ({ page }) => {
test('should persist its data', async ({ page }) => {
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
const newTodo = page.getByPlaceholder('What needs to be done?');
for (const item of TODO_ITEMS.slice(0, 2)) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
for (const item of TODO_ITEMS.slice(0, 2)) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
const todoItems = page.getByTestId('todo-item');
const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
await firstTodoCheck.check();
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
await expect(firstTodoCheck).toBeChecked();
await expect(todoItems).toHaveClass(['completed', '']);
const todoItems = page.getByTestId('todo-item');
const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
await firstTodoCheck.check();
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
await expect(firstTodoCheck).toBeChecked();
await expect(todoItems).toHaveClass(['completed', '']);
// Ensure there is 1 completed item.
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
// Ensure there is 1 completed item.
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
// Now reload.
await page.reload();
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
await expect(firstTodoCheck).toBeChecked();
await expect(todoItems).toHaveClass(['completed', '']);
});
// Now reload.
await page.reload();
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
await expect(firstTodoCheck).toBeChecked();
await expect(todoItems).toHaveClass(['completed', '']);
});
});
test.describe('Routing', () => {
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
// make sure the app had a chance to save updated todos in storage
// before navigating to a new view, otherwise the items can get lost :(
// in some frameworks like Durandal
await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
});
test('should allow me to display active items', async ({ page }) => {
const todoItem = page.getByTestId('todo-item');
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.getByRole('link', { name: 'Active' }).click();
await expect(todoItem).toHaveCount(2);
await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
});
test('should respect the back button', async ({ page }) => {
const todoItem = page.getByTestId('todo-item');
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await test.step('Showing all items', async () => {
await page.getByRole('link', { name: 'All' }).click();
await expect(todoItem).toHaveCount(3);
test.beforeEach(async ({ page }) => {
await createDefaultTodos(page);
// make sure the app had a chance to save updated todos in storage
// before navigating to a new view, otherwise the items can get lost :(
// in some frameworks like Durandal
await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
});
await test.step('Showing active items', async () => {
await page.getByRole('link', { name: 'Active' }).click();
test('should allow me to display active items', async ({ page }) => {
const todoItem = page.getByTestId('todo-item');
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.getByRole('link', { name: 'Active' }).click();
await expect(todoItem).toHaveCount(2);
await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
});
await test.step('Showing completed items', async () => {
await page.getByRole('link', { name: 'Completed' }).click();
test('should respect the back button', async ({ page }) => {
const todoItem = page.getByTestId('todo-item');
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await test.step('Showing all items', async () => {
await page.getByRole('link', { name: 'All' }).click();
await expect(todoItem).toHaveCount(3);
});
await test.step('Showing active items', async () => {
await page.getByRole('link', { name: 'Active' }).click();
});
await test.step('Showing completed items', async () => {
await page.getByRole('link', { name: 'Completed' }).click();
});
await expect(todoItem).toHaveCount(1);
await page.goBack();
await expect(todoItem).toHaveCount(2);
await page.goBack();
await expect(todoItem).toHaveCount(3);
});
await expect(todoItem).toHaveCount(1);
await page.goBack();
await expect(todoItem).toHaveCount(2);
await page.goBack();
await expect(todoItem).toHaveCount(3);
});
test('should allow me to display completed items', async ({ page }) => {
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.getByRole('link', { name: 'Completed' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(1);
});
test('should allow me to display completed items', async ({ page }) => {
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.getByRole('link', { name: 'Completed' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(1);
});
test('should allow me to display all items', async ({ page }) => {
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.getByRole('link', { name: 'Active' }).click();
await page.getByRole('link', { name: 'Completed' }).click();
await page.getByRole('link', { name: 'All' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(3);
});
test('should allow me to display all items', async ({ page }) => {
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.getByRole('link', { name: 'Active' }).click();
await page.getByRole('link', { name: 'Completed' }).click();
await page.getByRole('link', { name: 'All' }).click();
await expect(page.getByTestId('todo-item')).toHaveCount(3);
});
test('should highlight the currently applied filter', async ({ page }) => {
await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
test('should highlight the currently applied filter', async ({ page }) => {
await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
//create locators for active and completed links
const activeLink = page.getByRole('link', { name: 'Active' });
const completedLink = page.getByRole('link', { name: 'Completed' });
await activeLink.click();
//create locators for active and completed links
const activeLink = page.getByRole('link', { name: 'Active' });
const completedLink = page.getByRole('link', { name: 'Completed' });
await activeLink.click();
// Page change - active items.
await expect(activeLink).toHaveClass('selected');
await completedLink.click();
// Page change - active items.
await expect(activeLink).toHaveClass('selected');
await completedLink.click();
// Page change - completed items.
await expect(completedLink).toHaveClass('selected');
});
// Page change - completed items.
await expect(completedLink).toHaveClass('selected');
});
});
async function createDefaultTodos(page: Page) {
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
// create a new todo locator
const newTodo = page.getByPlaceholder('What needs to be done?');
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press('Enter');
}
}
async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
return await page.waitForFunction(e => {
return JSON.parse(localStorage['react-todos']).length === e;
}, expected);
return await page.waitForFunction((e) => {
return JSON.parse(localStorage['react-todos']).length === e;
}, expected);
}
async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
return await page.waitForFunction(e => {
return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
}, expected);
return await page.waitForFunction((e) => {
return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
}, expected);
}
async function checkTodosInLocalStorage(page: Page, title: string) {
return await page.waitForFunction(t => {
return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
}, title);
return await page.waitForFunction((t) => {
return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
}, title);
}
+25 -9
View File
@@ -1,18 +1,34 @@
import { test, expect } from '@playwright/test';
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, test } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.goto('https://playwright.dev/');
// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();
// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();
// Expects page to have a heading with the name of Installation.
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
// Expects page to have a heading with the name of Installation.
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
+161
View File
@@ -0,0 +1,161 @@
import antfu from '@antfu/eslint-config';
import header from 'eslint-plugin-header';
import tsParser from '@typescript-eslint/parser';
export default antfu({
stylistic: {
indent: 4,
semi: true,
},
react: true,
yaml: {
overrides: {
'yaml/indent': ['error', 4, { indicatorValueIndent: 2 }],
},
},
markdown: false,
rules: {
'import/no-cycle': 'error',
'ts/no-explicit-any': 'warn',
'style/brace-style': ['warn', '1tbs', { allowSingleLine: true }],
'style/comma-dangle': ['error', {
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
exports: 'always-multiline',
enums: 'always-multiline',
functions: 'never',
}],
'style/arrow-parens': ['error', 'always'],
curly: ['error', 'multi-line'],
'antfu/if-newline': 'off',
'style/spaced-comment': 'off',
'tunicorn/number-literal-case': 'off',
'style/indent-binary-ops': 'off',
'style/indent': ['error', 4, {
ObjectExpression: 'first',
SwitchCase: 1,
ignoreComments: true,
}],
'sort-imports': [
'error',
{
allowSeparatedGroups: false,
// ignoreCase: false,
ignoreCase: true,
ignoreDeclarationSort: true,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
},
],
// TODO: debatable rules
'test/prefer-lowercase-title': 'off',
'antfu/top-level-function': 'off',
'style/operator-linebreak': 'off',
'unicorn/no-new-array': 'off',
'unicorn/prefer-includes': 'off',
'prefer-arrow-callback': 'off',
'no-restricted-globals': 'off',
'unicorn/prefer-string-starts-ends-with': 'warn',
// TODO: just for compatibility with old code
'unused-imports/no-unused-vars': 'warn',
'style/jsx-closing-tag-location': 'warn',
'ts/ban-types': 'warn',
'unicorn/prefer-dom-node-text-content': 'warn',
'unicorn/prefer-number-properties': 'warn',
'no-prototype-builtins': 'warn',
'style/no-tabs': 'warn',
'style/quotes': ['warn', 'single', { avoidEscape: true }],
'react/display-name': 'off',
'react-hooks/rules-of-hooks': 'off',
'eslint-comments/no-unlimited-disable': 'off',
'ts/prefer-ts-expect-error': 'off',
'ts/ban-ts-comment': 'off',
'ts/no-duplicate-enum-values': 'off',
'no-cond-assign': 'warn',
'antfu/consistent-list-newline': 'off',
'ts/no-use-before-define': 'warn',
'intunicorn/number-literal-case': 'off',
'ts/no-redeclare': 'warn',
'test/no-identical-title': 'warn',
'ts/no-non-null-asserted-optional-chain': 'warn',
'no-restricted-syntax': 'warn',
'prefer-regex-literals': 'warn',
'ts/no-this-alias': 'warn',
'prefer-promise-reject-errors': 'warn',
'no-new': 'warn',
'unicorn/error-message': 'warn',
'ts/prefer-literal-enum-member': 'warn',
'style/jsx-curly-newline': ['warn', { multiline: 'forbid', singleline: 'forbid' }],
'no-control-regex': 'warn',
'style/jsx-wrap-multilines': 'warn',
'ts/no-import-type-side-effects': 'warn',
'style/quote-props': ['warn', 'as-needed'],
'unicorn/number-literal-case': 'warn',
'react/no-direct-mutation-state': 'warn',
'style/jsx-curly-brace-presence': 'warn',
'style/multiline-ternary': 'warn',
'unicorn/prefer-type-error': 'warn',
'accessor-pairs': 'warn',
},
}, {
files: ['**/*.ts', '**/*.tsx'],
ignores: ['**/*.d.ts', '**/vite.config.ts', 'playwright.config.ts'],
plugins: {
header,
},
rules: {
'header/header': [
2,
'block',
[
'*',
' * Copyright 2023-present DreamNum Inc.',
' *',
' * Licensed under the Apache License, Version 2.0 (the "License");',
' * you may not use this file except in compliance with the License.',
' * You may obtain a copy of the License at',
' *',
' * http://www.apache.org/licenses/LICENSE-2.0',
' *',
' * Unless required by applicable law or agreed to in writing, software',
' * distributed under the License is distributed on an "AS IS" BASIS,',
' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
' * See the License for the specific language governing permissions and',
' * limitations under the License.',
' ',
],
2,
],
},
}, {
files: ['**/*.ts', '**/*.tsx'],
rules: {
'ts/naming-convention': [
'warn',
// Interfaces' names should start with a capital 'I'.
{
selector: 'interface',
format: ['PascalCase'],
custom: {
regex: '^I[A-Z0-9]',
match: true,
},
},
// Private fields of a class should start with an underscore '_'.
{
selector: ['classMethod', 'classProperty'],
modifiers: ['private'],
format: ['camelCase'],
leadingUnderscore: 'require',
},
],
},
languageOptions: {
parser: tsParser,
},
});
+8 -4
View File
@@ -1,11 +1,12 @@
import path from 'node:path';
import process from 'node:process';
import { execSync } from 'node:child_process';
import esbuild from 'esbuild';
import cleanPlugin from 'esbuild-plugin-clean';
import copyPlugin from 'esbuild-plugin-copy';
import stylePlugin from 'esbuild-style-plugin';
import minimist from 'minimist';
import { execSync } from 'node:child_process';
const nodeModules = path.resolve(process.cwd(), './node_modules');
@@ -18,8 +19,8 @@ const monacoEditorEntryPoints = ['vs/language/typescript/ts.worker.js', 'vs/edit
const gitCommitHash = execSync('git rev-parse --short HEAD').toString().trim();
const gitRefName = execSync('git symbolic-ref -q --short HEAD || git describe --tags --exact-match').toString().trim();
const monacoBuildTask = () =>
esbuild.build({
function monacoBuildTask() {
return esbuild.build({
entryPoints: monacoEditorEntryPoints.map((entry) => `./node_modules/monaco-editor/esm/${entry}`),
bundle: true,
color: true,
@@ -32,6 +33,7 @@ const monacoBuildTask = () =>
}),
],
});
}
const ctx = await esbuild[args.watch ? 'context' : 'build']({
bundle: true,
@@ -93,11 +95,13 @@ if (args.watch) {
await monacoBuildTask();
await ctx.watch();
const { host, port } = await ctx.serve({
const { port } = await ctx.serve({
servedir: './local',
port: 3002,
});
const url = `http://localhost:${port}`;
// eslint-disable-next-line no-console
console.log(`Local server: ${url}`);
}
+5 -3
View File
@@ -1,12 +1,13 @@
{
"name": "univer-examples",
"private": true,
"description": "Univer vanilla ts demo project",
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"private": true,
"scripts": {
"build:demo": "node ./esbuild.config.mjs",
"dev:demo": "node ./esbuild.config.mjs --watch"
"dev:demo": "node ./esbuild.config.mjs --watch",
"lint:types": "tsc --noEmit"
},
"dependencies": {
"@univerjs/core": "workspace:*",
@@ -14,8 +15,8 @@
"@univerjs/docs": "workspace:*",
"@univerjs/docs-ui": "workspace:*",
"@univerjs/engine-formula": "workspace:*",
"@univerjs/find-replace": "workspace:*",
"@univerjs/engine-render": "workspace:*",
"@univerjs/find-replace": "workspace:*",
"@univerjs/icons": "^0.1.26",
"@univerjs/rpc": "workspace:*",
"@univerjs/sheets": "workspace:*",
@@ -39,6 +40,7 @@
"devDependencies": {
"@types/react": "^18.2.46",
"@types/react-dom": "^18.2.18",
"@univerjs/infra": "workspace:*",
"esbuild": "^0.19.10",
"esbuild-plugin-clean": "^1.0.1",
"esbuild-plugin-copy": "^2.1.1",
+1 -1
View File
@@ -14,6 +14,7 @@
* limitations under the License.
*/
/* eslint-disable node/prefer-global/process */
import { LocaleType, Univer } from '@univerjs/core';
import { defaultTheme } from '@univerjs/design';
import { UniverDocsPlugin } from '@univerjs/docs';
@@ -65,7 +66,6 @@ univer.createUniverDoc(DEFAULT_DOCUMENT_DATA_CN);
// use for console test
declare global {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Window {
univer?: Univer;
}
+14 -5
View File
@@ -22,9 +22,13 @@ import styles from './styles.module.less';
// package info
// eslint-disable-next-line no-console
console.table({
// eslint-disable-next-line node/prefer-global/process
NODE_ENV: process.env.NODE_ENV,
// eslint-disable-next-line node/prefer-global/process
GIT_COMMIT_HASH: process.env.GIT_COMMIT_HASH,
// eslint-disable-next-line node/prefer-global/process
GIT_REF_NAME: process.env.GIT_REF_NAME,
// eslint-disable-next-line node/prefer-global/process
BUILD_TIME: process.env.BUILD_TIME,
});
@@ -33,23 +37,28 @@ function Examples() {
<section className={styles.examples}>
<a className={styles.btn} href="./sheets/">
<span> Univer Sheets</span>
<div className={styles.btnBg}></div>{' '}
<div className={styles.btnBg}></div>
{' '}
</a>
<a className={styles.btn} href="./docs/">
<span> Univer Docs</span>
<div className={styles.btnBg}></div>{' '}
<div className={styles.btnBg}></div>
{' '}
</a>
<a className={styles.btn} href="./slides/">
<span> Univer Slides</span>
<div className={styles.btnBg}></div>{' '}
<div className={styles.btnBg}></div>
{' '}
</a>
<a className={styles.btn} href="./sheets-multi/">
<span> Univer Multi Instance</span>
<div className={styles.btnBg}></div>{' '}
<div className={styles.btnBg}></div>
{' '}
</a>
<a className={styles.btn} href="./uniscript/">
<span> Uniscript</span>
<div className={styles.btnBg}></div>{' '}
<div className={styles.btnBg}></div>
{' '}
</a>
</section>
);
@@ -33,10 +33,10 @@ export const NotificationOperation: ICommand = {
value.indexOf('Success') > -1
? 'success'
: value.indexOf('Info') > -1
? 'info'
: value.indexOf('Warning') > -1
? 'warning'
: 'error';
? 'info'
: value.indexOf('Warning') > -1
? 'warning'
: 'error';
notificationService.show({
type,
content: value || 'Notification Content',
@@ -14,6 +14,7 @@
* limitations under the License.
*/
/* eslint-disable node/prefer-global/process */
import type { ICommand, IStyleData, IWorkbookData } from '@univerjs/core';
import { CommandType, ISnapshotPersistenceService, IUniverInstanceService, ObjectMatrix } from '@univerjs/core';
import type { IAccessor } from '@wendellhu/redi';
@@ -65,8 +66,9 @@ export const SaveSnapshotOptions: ICommand = {
const sheet = snapshot.sheets[sheetId];
snapshot.sheets = { [sheetId]: sheet };
snapshot.sheetOrder = [sheetId];
break;
}
// eslint-disable-next-line no-fallthrough
case 'workbook': {
const text = JSON.stringify(filterStyle(snapshot), null, 2);
// navigator.clipboard.writeText(text);
+2 -2
View File
@@ -28,7 +28,7 @@ import { UniverSheetsNumfmtPlugin } from '@univerjs/sheets-numfmt';
import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
import { UniverUIPlugin } from '@univerjs/ui';
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { Mosaic, MosaicWindow } from 'react-mosaic-component';
import { DEFAULT_WORKBOOK_DATA_DEMO } from '../data';
@@ -99,7 +99,7 @@ export const App = (
/>
);
ReactDOM.render(App, document.getElementById('container'));
createRoot(document.getElementById('container')!).render(App);
factory('app-a')();
factory('app-b')();
-1
View File
@@ -84,7 +84,6 @@ univer.registerPlugin(UniverSheetsFindPlugin);
univer.createUniverSheet(DEFAULT_WORKBOOK_DATA_DEMO);
declare global {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Window {
univer?: Univer;
}
-1
View File
@@ -45,7 +45,6 @@ univer.createUniverSlide(DEFAULT_SLIDE_DATA);
// use for console test
declare global {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Window {
univer?: Univer;
}
@@ -14,8 +14,6 @@
* limitations under the License.
*/
/* eslint-disable */
const activeSheet = univerAPI.getCurrentSheet().getActiveSheet();
// Set A1:B2 to bold
-1
View File
@@ -69,7 +69,6 @@ univer.registerPlugin(UniverUniscriptPlugin, {
univer.createUniverSheet(UNISCRIT_WORKBOOK_DATA_DEMO);
declare global {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Window {
univer?: Univer;
}
+4 -10
View File
@@ -1,14 +1,8 @@
{
"extends": "../tsconfig.json",
"extends": "@univerjs/infra/tsconfigs/base",
"compilerOptions": {
"rootDir": "./src",
"outDir": "local",
"downlevelIteration": true,
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"strictPropertyInitialization": false,
"types": ["esbuild-style-plugin"]
"types": ["esbuild-style-plugin"],
"outDir": "local"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "local"]
"include": ["src"]
}
+18 -23
View File
@@ -1,29 +1,31 @@
{
"name": "univer",
"type": "module",
"version": "0.1.0-beta.2",
"private": true,
"author": "DreamNum Inc. <developer@univer.ai>",
"license": "Apache-2.0",
"engines": {
"node": "^18.17.0",
"pnpm": "^8.6.2"
},
"scripts": {
"prepare": "husky install",
"pre-commit": "lint-staged",
"dev:demo": "turbo dev:demo",
"dev:storybook": "storybook dev -p 6006 --no-open",
"lint": "eslint --cache **/src/**/*.{tsx,ts}",
"lint:fix": "eslint --cache **/src/**/*.{tsx,ts} --fix",
"lint:style": "stylelint **/*.less",
"lint:types": "tsc --noEmit -p tsconfig.eslint.json",
"lint:types": "turbo lint:types",
"test": "turbo test -- --passWithNoTests",
"coverage": "turbo coverage -- --passWithNoTests",
"build": "turbo build",
"build:demo": "turbo build:demo",
"build:storybook": "storybook build"
"build:storybook": "storybook build",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"engines": {
"node": "^18.17.0",
"pnpm": "^8.6.2"
},
"author": "DreamNum Inc. <developer@univer.ai>",
"license": "Apache-2.0",
"devDependencies": {
"@antfu/eslint-config": "^2.6.3",
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.4",
"@playwright/test": "^1.40.1",
@@ -37,32 +39,25 @@
"@storybook/testing-library": "^0.2.2",
"@types/node": "^20.10.6",
"@types/react": "^18.2.46",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"@typescript-eslint/parser": "^6.19.1",
"@univerjs/design": "workspace:*",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-header": "~3.1.1",
"eslint-plugin-import-newlines": "^1.3.4",
"eslint-plugin-prettier": "^5.1.2",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^3.0.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
"prettier": "^3.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"storybook": "^7.6.7",
"stylelint": "^15.11.0",
"stylelint-config-clean-order": "^5.2.0",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-standard-less": "^2.0.0",
"stylelint-config-standard": "^34.0.0",
"stylelint": "^15.11.0",
"stylelint-config-standard-less": "^2.0.0",
"turbo": "^1.11.2",
"typescript": "^5.3.3"
},
+17 -15
View File
@@ -1,9 +1,20 @@
{
"name": "@univerjs/core",
"version": "0.1.0-beta.2",
"private": false,
"description": "Core library for Univer.",
"keywords": [],
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"homepage": "https://github.com/dream-num/univer",
"repository": {
"type": "git",
"url": "https://github.com/dream-num/univer.git"
},
"keywords": [],
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"main": "./lib/cjs/index.js",
"module": "./lib/es/index.js",
"types": "./lib/types/index.d.ts",
@@ -24,28 +35,22 @@
}
}
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"private": false,
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"lint:types": "tsc --noEmit",
"build": "tsc && vite build"
},
"license": "Apache-2.0",
"homepage": "https://github.com/dream-num/univer",
"repository": {
"type": "git",
"url": "https://github.com/dream-num/univer.git"
"peerDependencies": {
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
},
"dependencies": {
"@wendellhu/redi": "^0.12.13",
@@ -56,15 +61,12 @@
},
"devDependencies": {
"@types/numeral": "^2.0.5",
"@univerjs/infra": "workspace:*",
"@vitest/coverage-istanbul": "^1.1.1",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-externals": "^0.6.2",
"vitest": "^1.1.1"
},
"peerDependencies": {
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
}
}
@@ -14,8 +14,6 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Ctor, Injector } from '@wendellhu/redi';
import type { Plugin, PluginCtor } from '../plugin/plugin';
-2
View File
@@ -14,8 +14,6 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { IDisposable } from '@wendellhu/redi';
import { Inject, Injector } from '@wendellhu/redi';
-2
View File
@@ -14,8 +14,6 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Injector } from '@wendellhu/redi';
import type { DocumentDataModel } from '../docs/data-model/document-data-model';
-3
View File
@@ -14,9 +14,6 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
// eslint-disable-next-line no-magic-numbers
export function throttle<T extends (...args: any[]) => any>(fn: T, wait: number = 16): T {
let lastTime = 0;
let timer: number | null = null;
-3
View File
@@ -14,8 +14,6 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Ctor, Injector } from '@wendellhu/redi';
export type PluginCtor<T extends Plugin> = Ctor<T> & { type: PluginType };
@@ -42,7 +40,6 @@ export abstract class Plugin {
this._name = name;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onStarting(injector: Injector): void {}
onReady(): void {}
@@ -161,7 +161,6 @@ export const ICommandService = createIdentifier<ICommandService>('anywhere.comma
* The registry of commands.
*/
export class CommandRegistry {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private readonly _commands = new Map<string, ICommand>();
registerCommand(command: ICommand): IDisposable {
@@ -385,7 +384,7 @@ export class CommandService implements ICommandService {
try {
result = this._injector.invoke(command.handler, params) as R;
if (result instanceof Promise) {
throw new Error('[CommandService]: Command handler should not return a promise.');
throw new TypeError('[CommandService]: Command handler should not return a promise.');
}
this._commandExecutingLevel--;
@@ -443,7 +442,7 @@ class MultiCommand implements IMultiCommand {
for (const item of this._implementations) {
const preconditions = item.command.preconditions;
if (!preconditions || (preconditions && preconditions(contextService))) {
logService.debug(`[MultiCommand]`, `executing implementation "${item.command.name}".`);
logService.debug('[MultiCommand]', `executing implementation "${item.command.name}".`);
const result = await injector.invoke(item.command.handler, params);
if (result) {
return true;
@@ -33,7 +33,6 @@ export interface IConfigService {
}
export class ConfigService implements IConfigService {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private readonly _config: Map<string, any> = new Map();
getConfig<T>(id: string): Nullable<T> {
@@ -55,7 +55,6 @@ export const LifecycleToModules = new Map<LifecycleStages, Array<DependencyIdent
* Register some modules here that will automatically run when Univer progressed to a certain lifecycle stage
*/
export function OnLifecycle(lifecycleStage: LifecycleStages, identifier: DependencyIdentifier<unknown>) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const decorator = function decorator(_: Ctor<unknown>) {
runOnLifecycle(lifecycleStage, identifier);
};
@@ -36,7 +36,6 @@ function getValue(locale: ILocales[LocaleType], key: string): Nullable<string> {
try {
if (locale[key]) return locale[key] as string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return key.split('.').reduce((a: any, b: string) => a[b], locale);
} catch (error) {
console.warn('Key %s not found', key);
@@ -20,7 +20,7 @@ import { createIdentifier } from '@wendellhu/redi';
import { Disposable } from '../../shared/lifecycle';
export enum LogLevel /* eslint-disable no-magic-numbers */ {
export enum LogLevel {
SILENT = 0,
ERROR = 1,
WARN = 2,
@@ -28,7 +28,6 @@ export enum LogLevel /* eslint-disable no-magic-numbers */ {
VERBOSE = 4,
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ArgsType = any[];
export interface ILogService {
+6 -6
View File
@@ -52,7 +52,7 @@ export class ColorBuilder {
return this.asRgbColor();
}
case ColorType.UNSUPPORTED: {
throw Error('unsupported color type');
throw new Error('unsupported color type');
}
}
}
@@ -118,9 +118,9 @@ export class Color {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
let string = null;
if (result) {
const r = parseInt(result[1], 16);
const g = parseInt(result[2], 16);
const b = parseInt(result[3], 16);
const r = Number.parseInt(result[1], 16);
const g = Number.parseInt(result[2], 16);
const b = Number.parseInt(result[3], 16);
string = `rgba(${r},${g},${b})`;
}
return string;
@@ -283,10 +283,10 @@ export class RgbColor extends Color {
static RGB_COLOR_AMT: number = 0;
static RGBA_EXTRACT: RegExp = new RegExp(
`\\s*rgba\\s*\\((\\s*\\d+\\s*),(\\s*\\d+\\s*),(\\s*\\d+\\s*),(\\s*\\d.\\d|\\d\\s*)\\)\\s*`
'\\s*rgba\\s*\\((\\s*\\d+\\s*),(\\s*\\d+\\s*),(\\s*\\d+\\s*),(\\s*\\d.\\d|\\d\\s*)\\)\\s*'
);
static RGB_EXTRACT: RegExp = new RegExp(`\\s*rgb\\s*\\((\\s*\\d+\\s*),(\\s*\\d+\\s*),(\\s*\\d+\\s*)\\)\\s*`);
static RGB_EXTRACT: RegExp = new RegExp('\\s*rgb\\s*\\((\\s*\\d+\\s*),(\\s*\\d+\\s*),(\\s*\\d+\\s*)\\)\\s*');
private _cssString: string;
-2
View File
@@ -23,11 +23,9 @@ import type { Nullable } from '../common/type-utils';
import type { Observer } from '../observer/observable';
import { isObserver } from '../observer/observable';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function toDisposable(observer: Nullable<Observer<any>>): IDisposable;
export function toDisposable(subscription: SubscriptionLike): IDisposable;
export function toDisposable(callback: () => void): IDisposable;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function toDisposable(v: SubscriptionLike | (() => void) | Nullable<Observer<any>>): IDisposable {
let disposed = false;
+2 -2
View File
@@ -30,5 +30,5 @@ export const getTypeFromPermissionItemList = (list: PermissionPoint[]) =>
list.some((item) => item.status === PermissionStatus.INIT)
? PermissionStatus.INIT
: list.some((item) => item.status === PermissionStatus.FETCHING)
? PermissionStatus.FETCHING
: PermissionStatus.DONE;
? PermissionStatus.FETCHING
: PermissionStatus.DONE;
+1
View File
@@ -184,6 +184,7 @@ export class Rectangle {
ranges[0]
);
}
static getRelativeRange = (range: IRange, originRange: IRange) =>
({
startRow: range.startRow - originRange.startRow,
+8 -8
View File
@@ -364,11 +364,11 @@ export class Range {
if (p && Array.isArray(p.body?.textRuns)) {
return isAllFormatInTextRuns('ul', p.body?.textRuns!) === BooleanNumber.TRUE
? {
s: BooleanNumber.TRUE,
}
s: BooleanNumber.TRUE,
}
: {
s: BooleanNumber.FALSE,
};
s: BooleanNumber.FALSE,
};
}
return this.getUnderlines()[0][0];
@@ -397,11 +397,11 @@ export class Range {
if (p && Array.isArray(p.body?.textRuns)) {
return isAllFormatInTextRuns('st', p.body?.textRuns!) === BooleanNumber.TRUE
? {
s: BooleanNumber.TRUE,
}
s: BooleanNumber.TRUE,
}
: {
s: BooleanNumber.FALSE,
};
s: BooleanNumber.FALSE,
};
}
return this.getStrikeThroughs()[0][0];
+12 -15
View File
@@ -71,31 +71,28 @@ export enum VerticalAlign {
*/
export enum WrapStrategy {
UNSPECIFIED,
/**
* Lines that are longer than the cell width will be written in the next cell over, so long as that cell is empty. If the next cell over is non-empty, this behaves the same as CLIP . The text will never wrap to the next line unless the user manually inserts a new line. Example:
| First sentence. |
| Manual newline that is very long. <- Text continues into next cell
| Next newline. |
* | First sentence. |
* | Manual newline that is very long. <- Text continues into next cell
* | Next newline. |
*/
OVERFLOW,
/**
* Lines that are longer than the cell width will be clipped. The text will never wrap to the next line unless the user manually inserts a new line. Example:
| First sentence. |
| Manual newline t| <- Text is clipped
| Next newline. |
* | First sentence. |
* | Manual newline t| <- Text is clipped
* | Next newline. |
*/
CLIP,
/**
* Words that are longer than a line are wrapped at the character level rather than clipped. Example:
| Cell has a |
| loooooooooo| <- Word is broken.
| ong word. |
* | Cell has a |
* | loooooooooo| <- Word is broken.
* | ong word. |
*/
WRAP,
}
@@ -50,7 +50,6 @@ export interface ICellDataForSheetInterceptor extends ICellData {
isInArrayFormulaRange?: Nullable<boolean>;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isICellData(value: any): value is ICellData {
return (
value &&
+5 -9
View File
@@ -1,13 +1,9 @@
{
"extends": "../../tsconfig.json",
"extends": "@univerjs/infra/tsconfigs/base",
"compilerOptions": {
"rootDir": ".",
"outDir": "lib/types",
"downlevelIteration": true,
"esModuleInterop": true,
"jsx": "preserve",
"emitDecoratorMetadata": true,
"strictPropertyInitialization": false
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src/**/*"]
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src"]
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "@univerjs/infra/tsconfigs/node",
"include": ["vite.config.ts"]
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { resolve } from 'path';
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config';
import dts from 'vite-plugin-dts';
import { name } from './package.json';
+16 -15
View File
@@ -1,10 +1,18 @@
{
"name": "@univerjs/design",
"version": "0.1.0-beta.2",
"private": false,
"description": "UI component library for building exceptional Univer.",
"keywords": [],
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"keywords": [],
"sideEffects": [
"**/*.css"
],
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"main": "./lib/cjs/index.js",
"module": "./lib/es/index.js",
"types": "./lib/types/index.d.ts",
@@ -26,26 +34,24 @@
"./lib/*": "./lib/*"
}
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"sideEffects": [
"**/*.css"
],
"private": false,
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"lint:types": "tsc --noEmit",
"build": "tsc && vite build"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0",
"rxjs": "^7.8.1"
},
"dependencies": {
"@rc-component/color-picker": "^1.4.1",
"@rc-component/trigger": "^1.18.2",
@@ -70,6 +76,7 @@
"@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18",
"@types/react-transition-group": "^4.4.10",
"@univerjs/infra": "workspace:*",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/coverage-istanbul": "^1.1.1",
"happy-dom": "^12.10.3",
@@ -77,12 +84,6 @@
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-externals": "^0.6.2",
"vitest": "^1.1.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0",
"rxjs": "^7.8.1"
}
}
@@ -83,10 +83,10 @@ export function Avatar(props: IAvatarProps) {
const sizeStyle =
typeof size === 'number'
? {
width: size,
height: size,
lineHeight: `${size}px`,
}
width: size,
height: size,
lineHeight: `${size}px`,
}
: {};
const _className = clsx(styles.avatar, {
@@ -16,23 +16,23 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Avatar } from '../Avatar';
describe('Avatar', () => {
test('renders correctly', () => {
it('renders correctly', () => {
const { container } = render(<Avatar size="small">Jane Doe</Avatar>);
expect(container);
});
test('renders the children', () => {
it('renders the children', () => {
const { getByText } = render(<Avatar>Test</Avatar>);
const childrenElement = getByText('Test');
expect(childrenElement).not.toBeNull();
});
test('renders the image', () => {
it('renders the image', () => {
const { container } = render(<Avatar src="test.png" />);
expect(container.querySelector('img')).not.toBeNull();
@@ -16,17 +16,17 @@
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Button } from '../Button';
describe('Button', () => {
test('renders correctly', () => {
it('renders correctly', () => {
const { container } = render(<Button type="primary">btn1</Button>);
expect(container);
});
test('click onClick function', () => {
it('click onClick function', () => {
let a = 1;
const { container } = render(
@@ -44,7 +44,7 @@ describe('Button', () => {
expect(a).toEqual(2);
});
test('should prevent event when button is disabled', () => {
it('should prevent event when button is disabled', () => {
let a = 1;
const { container } = render(
@@ -83,32 +83,34 @@ export function CascaderList(props: ICascaderListProps) {
return (
<section className={styles.cascaderList}>
{activeOptions.map((options, index) =>
options.length ? (
<ul key={index} className={styles.cascaderListBoard}>
{options.map((option) => (
<li
key={option.value}
className={clsx(styles.cascaderListItem, {
[styles.cascaderListItemActive]: option.value === value[index],
})}
>
<a
className={styles.cascaderListOption}
onClick={() => handleChange(index, option.value)}
options.length
? (
<ul key={index} className={styles.cascaderListBoard}>
{options.map((option) => (
<li
key={option.value}
className={clsx(styles.cascaderListItem, {
[styles.cascaderListItemActive]: option.value === value[index],
})}
>
<span className={styles.cascaderListCheckMark}>
{option.value === value[index] && <CheckMarkSingle />}
</span>
<span>{option.label}</span>
</a>
</li>
))}
</ul>
) : (
<section key={index} className={styles.cascaderListEmpty}>
</section>
)
<a
className={styles.cascaderListOption}
onClick={() => handleChange(index, option.value)}
>
<span className={styles.cascaderListCheckMark}>
{option.value === value[index] && <CheckMarkSingle />}
</span>
<span>{option.label}</span>
</a>
</li>
))}
</ul>
)
: (
<section key={index} className={styles.cascaderListEmpty}>
</section>
)
)}
{value.length <= 0 && <section className={styles.cascaderListEmpty}></section>}
</section>
@@ -16,7 +16,7 @@
import { fireEvent, render } from '@testing-library/react';
import React, { useState } from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Checkbox } from '../../checkbox/Checkbox';
import { CheckboxGroup } from '../CheckboxGroup';
@@ -35,7 +35,7 @@ describe('CheckboxGroup', () => {
</CheckboxGroup>
);
test('click Checkbox', async () => {
it('click Checkbox', async () => {
render(group);
let result = ['0'];
@@ -16,14 +16,14 @@
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Checkbox } from '../Checkbox';
describe('Checkbox', () => {
const component = <Checkbox value="0">text</Checkbox>;
test('click Checkbox', async () => {
it('click Checkbox', async () => {
const { container } = render(component);
fireEvent.click(container.querySelector('input')!);
@@ -16,14 +16,14 @@
import { render } from '@testing-library/react';
import React, { useContext } from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import type { ILocale } from '../../../locale';
import { enUS, zhCN } from '../../../locale';
import { ConfigContext, ConfigProvider } from '../ConfigProvider';
describe('ConfigProvider', () => {
test('should render correctly', () => {
it('should render correctly', () => {
let _mountContainer: HTMLElement | null = null;
let _locale: ILocale | null = null;
@@ -48,7 +48,7 @@ describe('ConfigProvider', () => {
root.unmount();
});
test('should render correctly when mountContainer is not document.body', () => {
it('should render correctly when mountContainer is not document.body', () => {
const mountContainer = document.createElement('div');
document.body.appendChild(mountContainer);
@@ -73,7 +73,7 @@ describe('ConfigProvider', () => {
root.unmount();
});
test('should render correctly when locale is invalid', () => {
it('should render correctly when locale is invalid', () => {
const mountContainer = document.createElement('div');
document.body.appendChild(mountContainer);
@@ -16,12 +16,12 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Container } from '../Container';
describe('Container', () => {
test('should display initial Container', () => {
it('should display initial Container', () => {
const { container } = render(<Container>container content Text</Container>);
expect(container.textContent).toMatch('container content Text');
@@ -77,28 +77,30 @@ export function Dialog(props: IDialogProps) {
const { mountContainer } = useContext(ConfigContext);
const TitleIfDraggable = draggable ? (
<div
style={{
width: '100%',
cursor: 'pointer',
}}
onMouseOver={() => {
if (dragDisabled) {
setDragDisabled(false);
}
}}
onMouseOut={() => {
setDragDisabled(true);
}}
onFocus={() => {}}
onBlur={() => {}}
>
{title}
</div>
) : (
title
);
const TitleIfDraggable = draggable
? (
<div
style={{
width: '100%',
cursor: 'pointer',
}}
onMouseOver={() => {
if (dragDisabled) {
setDragDisabled(false);
}
}}
onMouseOut={() => {
setDragDisabled(true);
}}
onFocus={() => {}}
onBlur={() => {}}
>
{title}
</div>
)
: (
title
);
const modalRender = (modal: React.ReactNode) =>
draggable ? <Draggable disabled={dragDisabled}>{modal}</Draggable> : modal;
@@ -32,14 +32,14 @@ export default meta;
export const Playground = {
render() {
function handleRedirect() {
window.open('https://univer-icons.vercel.app/', '_blank');
window.open('https://univer.ai/icons', '_blank');
}
return (
<>
Check out our icon library at{' '}
Check out our icon library at
<Button type="link" onClick={handleRedirect}>
https://univer-icons.vercel.app/
https://univer.ai/icons
</Button>
</>
);
@@ -16,13 +16,13 @@
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Button } from '../../button/Button';
import { Message } from '../Message';
describe('Message', () => {
test('renders correctly', () => {
it('renders correctly', () => {
const message = new Message(document.body);
const { container } = render(
@@ -16,7 +16,7 @@
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Radio } from '../../radio/Radio';
import { RadioGroup } from '../RadioGroup';
@@ -35,7 +35,7 @@ describe('RadioGroup', () => {
</RadioGroup>
);
test('click Radio', async () => {
it('click Radio', async () => {
render(group);
fireEvent.click(screen.getByText('1'));
@@ -16,14 +16,14 @@
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Radio } from '../Radio';
describe('Radio', () => {
const component = <Radio value="0">text</Radio>;
test('click Radio', async () => {
it('click Radio', async () => {
const { container } = render(component);
fireEvent.click(container.querySelector('input')!);
@@ -16,12 +16,12 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Scrollbar } from '../Scrollbar';
describe('Scrollbar', () => {
test('should not render thumb', () => {
it('should not render thumb', () => {
const { container } = render(
<section style={{ height: '100px' }}>
<Scrollbar>
@@ -71,7 +71,8 @@ export const SelectGroup = {
{
label: (
<span>
Option 3-2 <strong>xxx</strong>
Option 3-2
<strong>xxx</strong>
</span>
),
value: 'option32',
@@ -34,7 +34,8 @@ export interface ISliderProps {
*/
min?: number;
/** The maximum value the slider can slide to
/**
* The maximum value the slider can slide to
* @default 400
*/
max?: number;
@@ -45,7 +46,8 @@ export interface ISliderProps {
*/
disabled?: boolean;
/** The maximum value the slider can slide to
/**
* The maximum value the slider can slide to
* @default 100
*/
resetPoint?: number;
@@ -176,7 +178,7 @@ export function Slider(props: ISliderProps) {
<Dropdown
placement="topLeft"
overlay={
overlay={(
<div className={styles.sliderShortcuts}>
{shortcuts?.map((item) => (
<a
@@ -188,13 +190,19 @@ export function Slider(props: ISliderProps) {
onClick={() => onChange && onChange(item)}
>
{item === value && <span className={styles.sliderShortcutIcon}></span>}
<span>{item}%</span>
<span>
{item}
%
</span>
</a>
))}
</div>
}
)}
>
<a className={styles.sliderValue}>{value}%</a>
<a className={styles.sliderValue}>
{value}
%
</a>
</Dropdown>
</div>
);
@@ -16,17 +16,17 @@
import { fireEvent, render, screen } from '@testing-library/react';
import React, { useState } from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Slider } from '../Slider';
describe('Slider', () => {
test('renders correctly', () => {
it('renders correctly', () => {
const { container } = render(<Slider min={0} max={100} value={90} />);
expect(container);
});
test('renders correctly with resetPoint', () => {
it('renders correctly with resetPoint', () => {
let value = 90;
function handleChange(point: number) {
@@ -40,7 +40,7 @@ describe('Slider', () => {
expect(value).toBe(30);
});
test('renders correctly with steps', () => {
it('renders correctly with steps', () => {
let result = 10;
function Demo() {
const [value, setValue] = useState(result);
@@ -66,7 +66,7 @@ describe('Slider', () => {
expect(result).toBe(20);
});
test('renders correctly with shortcuts', () => {
it('renders correctly with shortcuts', () => {
let result = 10;
function Demo() {
const [value, setValue] = useState(result);
@@ -16,12 +16,12 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { Tree } from '../Tree';
describe('Tree', () => {
test('defaultExpandAll', async () => {
it('defaultExpandAll', async () => {
const { container } = render(
<Tree
data={[
-2
View File
@@ -14,8 +14,6 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import type React from 'react';
import { useEffect, useRef, useState } from 'react';
import type { Observable, Subscription } from 'rxjs';
+5 -6
View File
@@ -1,10 +1,9 @@
{
"extends": "../../tsconfig.json",
"extends": "@univerjs/infra/tsconfigs/base",
"compilerOptions": {
"rootDir": ".",
"outDir": "lib",
"esModuleInterop": true,
"strictPropertyInitialization": false,
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src/**/*"]
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src"]
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "@univerjs/infra/tsconfigs/node",
"include": ["vite.config.ts"]
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { resolve } from 'path';
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
+20 -19
View File
@@ -1,10 +1,18 @@
{
"name": "@univerjs/docs-ui",
"version": "0.1.0-beta.2",
"private": false,
"description": "Univer normal ui-plugin-docs",
"keywords": [],
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"keywords": [],
"sideEffects": [
"**/*.css"
],
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"main": "./lib/cjs/index.js",
"module": "./lib/es/index.js",
"types": "./lib/types/index.d.ts",
@@ -26,26 +34,28 @@
"./lib/*": "./lib/*"
}
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"sideEffects": [
"**/*.css"
],
"private": false,
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"lint:types": "tsc --noEmit",
"build": "tsc && vite build"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@univerjs/design": "workspace:*",
"@univerjs/docs": "workspace:*",
"@univerjs/engine-render": "workspace:*",
"@univerjs/ui": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"react": ">=16.9.0"
},
"dependencies": {
"@univerjs/core": "workspace:*",
"@univerjs/design": "workspace:*",
@@ -58,6 +68,7 @@
},
"devDependencies": {
"@types/react": "^18.2.47",
"@univerjs/infra": "workspace:*",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/coverage-istanbul": "^1.1.1",
"happy-dom": "^12.10.3",
@@ -65,16 +76,6 @@
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-externals": "^0.6.2",
"vitest": "^1.1.1"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@univerjs/design": "workspace:*",
"@univerjs/docs": "workspace:*",
"@univerjs/engine-render": "workspace:*",
"@univerjs/ui": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"react": ">=16.9.0"
}
}
+5 -6
View File
@@ -1,10 +1,9 @@
{
"extends": "../../tsconfig.json",
"extends": "@univerjs/infra/tsconfigs/base",
"compilerOptions": {
"rootDir": ".",
"outDir": "lib",
"esModuleInterop": true,
"strictPropertyInitialization": false
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src/**/*"]
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src"]
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "@univerjs/infra/tsconfigs/node",
"include": ["vite.config.ts"]
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { resolve } from 'path';
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
+16 -15
View File
@@ -1,10 +1,15 @@
{
"name": "@univerjs/docs",
"version": "0.1.0-beta.2",
"private": false,
"description": "UniverSheet normal base-docs",
"keywords": [],
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"keywords": [],
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"main": "./lib/cjs/index.js",
"module": "./lib/es/index.js",
"types": "./lib/types/index.d.ts",
@@ -25,23 +30,27 @@
}
}
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"private": false,
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"lint:types": "tsc --noEmit",
"build": "tsc && vite build"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@univerjs/engine-render": "workspace:*",
"@univerjs/sheets": "workspace:*",
"@univerjs/ui": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
},
"dependencies": {
"@univerjs/core": "workspace:*",
"@univerjs/engine-render": "workspace:*",
@@ -51,21 +60,13 @@
"rxjs": "^7.8.1"
},
"devDependencies": {
"@univerjs/infra": "workspace:*",
"@vitest/coverage-istanbul": "^1.1.1",
"happy-dom": "^12.10.3",
"less": "^4.2.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-externals": "^0.6.2",
"vitest": "^1.1.1"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@univerjs/engine-render": "workspace:*",
"@univerjs/sheets": "workspace:*",
"@univerjs/ui": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
}
}
@@ -26,7 +26,7 @@ import {
IUniverInstanceService,
MemoryCursor,
} from '@univerjs/core';
import { type TextRange } from '@univerjs/engine-render';
import type { TextRange } from '@univerjs/engine-render';
import { TextSelectionManagerService } from '../../services/text-selection-manager.service';
import type { IRichTextEditingMutationParams } from '../mutations/core-editing.mutation';
@@ -337,8 +337,8 @@ function getReverseFormatValueInSelection(
return /bl|it/.test(key)
? BooleanNumber.FALSE
: /ul|st/.test(key)
? {
? {
s: BooleanNumber.FALSE,
}
: BaselineOffset.NORMAL;
: BaselineOffset.NORMAL;
}
@@ -149,23 +149,23 @@ export const ListOperationCommand: ICommand<IListOperationCommandParams> = {
paragraphs: [
isAlreadyOrdered
? {
paragraphStyle,
startIndex: 0,
}
paragraphStyle,
startIndex: 0,
}
: {
...paragraph,
startIndex: 0,
bullet: {
...(paragraph.bullet ?? {
nestingLevel: 0,
textStyle: {
fs: 20,
},
}),
listType,
listId,
},
},
...paragraph,
startIndex: 0,
bullet: {
...(paragraph.bullet ?? {
nestingLevel: 0,
textStyle: {
fs: 20,
},
}),
listType,
listId,
},
},
],
},
segmentId,
@@ -108,8 +108,8 @@ export class MoveCursorController extends Disposable {
const { startOffset, endOffset, style, collapsed, direction: rangeDirection } = activeRange;
if (allRanges.length > 1) {
let min = Infinity;
let max = -Infinity;
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
for (const range of allRanges) {
min = Math.min(min, range.startOffset!);
@@ -130,11 +130,11 @@ export class MoveCursorController extends Disposable {
const anchorOffset = collapsed
? startOffset
: rangeDirection === RANGE_DIRECTION.FORWARD
? startOffset
: endOffset;
? startOffset
: endOffset;
let focusOffset = collapsed ? endOffset : rangeDirection === RANGE_DIRECTION.FORWARD ? endOffset : startOffset;
const dataStreamLength = docDataModel.getBody()!.dataStream.length ?? Infinity;
const dataStreamLength = docDataModel.getBody()!.dataStream.length ?? Number.POSITIVE_INFINITY;
if (direction === Direction.LEFT || direction === Direction.RIGHT) {
const preSpan = skeleton.findNodeByCharIndex(focusOffset - 1);
@@ -208,14 +208,14 @@ export class MoveCursorController extends Disposable {
const { startOffset, endOffset, style, collapsed } = activeRange;
const dataStreamLength = docDataModel.getBody()!.dataStream.length ?? Infinity;
const dataStreamLength = docDataModel.getBody()!.dataStream.length ?? Number.POSITIVE_INFINITY;
if (direction === Direction.LEFT || direction === Direction.RIGHT) {
let cursor;
if (!activeRange.collapsed || allRanges.length > 1) {
let min = Infinity;
let max = -Infinity;
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
for (const range of allRanges) {
min = Math.min(min, range.startOffset!);
@@ -323,7 +323,7 @@ export class MoveCursorController extends Disposable {
const divide = span.parent;
if (divide == null) {
return -Infinity;
return Number.NEGATIVE_INFINITY;
}
const divideLeft = divide.left;
@@ -340,7 +340,7 @@ export class MoveCursorController extends Disposable {
span?: IDocumentSkeletonSpan;
distance: number;
} = {
distance: Infinity,
distance: Number.POSITIVE_INFINITY,
};
for (const divide of line.divides) {
+5 -6
View File
@@ -1,10 +1,9 @@
{
"extends": "../../tsconfig.json",
"extends": "@univerjs/infra/tsconfigs/base",
"compilerOptions": {
"rootDir": ".",
"outDir": "lib",
"esModuleInterop": true,
"strictPropertyInitialization": false
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src/**/*"]
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src"]
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "@univerjs/infra/tsconfigs/node",
"include": ["vite.config.ts"]
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { resolve } from 'path';
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config';
import dts from 'vite-plugin-dts';
import { name } from './package.json';
+13 -12
View File
@@ -1,10 +1,15 @@
{
"name": "@univerjs/engine-formula",
"version": "0.1.0-beta.2",
"private": false,
"description": "UniverSheet normal base-formula-engine",
"keywords": [],
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"keywords": [],
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"main": "./lib/cjs/index.js",
"module": "./lib/es/index.js",
"types": "./lib/types/index.d.ts",
@@ -25,23 +30,24 @@
}
}
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"private": false,
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"lint:types": "tsc --noEmit",
"build": "tsc && vite build"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
},
"dependencies": {
"@univerjs/core": "workspace:*",
"@wendellhu/redi": "^0.12.13",
@@ -50,17 +56,12 @@
},
"devDependencies": {
"@types/big.js": "^6.2.2",
"@univerjs/infra": "workspace:*",
"@vitest/coverage-istanbul": "^1.1.1",
"less": "^4.2.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-externals": "^0.6.2",
"vitest": "^1.1.1"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
}
}
@@ -85,7 +85,7 @@ class CustomFunction extends BaseFunction {
function createFunction(functionString: string, functionName: string) {
const instance = new CustomFunction(functionName);
// eslint-disable-next-line @typescript-eslint/no-implied-eval
// eslint-disable-next-line no-new-func
const functionCalculate = new Function(`return ${functionString}`)();
instance.calculateCustom = functionCalculate;
@@ -14,8 +14,6 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ConcatenateType } from '../../basics/common';
import { ErrorType } from '../../basics/error-type';
import { ObjectClassType } from '../../basics/object-class-type';
@@ -16,7 +16,7 @@
import { ErrorType } from '../../../basics/error-type';
import { valueObjectCompare } from '../../../engine/utils/object-compare';
import { type ArrayValueObject } from '../../../engine/value-object/array-value-object';
import type { ArrayValueObject } from '../../../engine/value-object/array-value-object';
import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { BaseFunction } from '../../base-function';
@@ -43,9 +43,9 @@ export class Sumif extends BaseFunction {
// sumRange has the same dimensions as range
const sumRangeArray = sumRange
? (sumRange as ArrayValueObject).slice(
[0, (range as ArrayValueObject).getRowCount()],
[0, (range as ArrayValueObject).getColumnCount()]
)
[0, (range as ArrayValueObject).getRowCount()],
[0, (range as ArrayValueObject).getColumnCount()]
)
: (range as ArrayValueObject);
if (!sumRangeArray) {
+5 -6
View File
@@ -1,10 +1,9 @@
{
"extends": "../../tsconfig.json",
"extends": "@univerjs/infra/tsconfigs/base",
"compilerOptions": {
"rootDir": ".",
"outDir": "lib",
"esModuleInterop": true,
"strictPropertyInitialization": false
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src/**/*"]
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src"]
}
@@ -0,0 +1,4 @@
{
"extends": "@univerjs/infra/tsconfigs/node",
"include": ["vite.config.ts"]
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { resolve } from 'path';
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config';
import dts from 'vite-plugin-dts';
import { name } from './package.json';
@@ -31,7 +31,7 @@ export default defineConfig(({ mode }) => ({
external: [
'@univerjs/core',
'@wendellhu/redi',
'rxjs'
'rxjs',
],
output: {
globals: {
+11 -10
View File
@@ -1,10 +1,15 @@
{
"name": "@univerjs/engine-numfmt",
"version": "0.1.0-beta.2",
"private": false,
"description": "UniverSheet normal plugin UI manager",
"keywords": [],
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"keywords": [],
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"main": "./lib/cjs/index.js",
"module": "./lib/es/index.js",
"types": "./lib/types/index.d.ts",
@@ -25,36 +30,32 @@
}
}
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"private": false,
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"lint:types": "tsc --noEmit",
"build": "tsc && vite build"
},
"peerDependencies": {
"@univerjs/core": "workspace:*"
},
"dependencies": {
"@univerjs/core": "workspace:*",
"numfmt": "^2.5.2"
},
"devDependencies": {
"@univerjs/infra": "workspace:*",
"@vitest/coverage-istanbul": "^1.1.1",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-externals": "^0.6.2",
"vitest": "^1.1.1"
},
"peerDependencies": {
"@univerjs/core": "workspace:*"
}
}
+5 -6
View File
@@ -1,10 +1,9 @@
{
"extends": "../../tsconfig.json",
"extends": "@univerjs/infra/tsconfigs/base",
"compilerOptions": {
"rootDir": ".",
"outDir": "lib",
"esModuleInterop": true,
"types": ["./src/types/index.d.ts"]
"rootDir": "src",
"outDir": "lib/types"
},
"include": ["src/**/*"]
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src"]
}
@@ -0,0 +1,4 @@
{
"extends": "@univerjs/infra/tsconfigs/node",
"include": ["vite.config.ts"]
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { resolve } from 'path';
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config';
import dts from 'vite-plugin-dts';
import { name } from './package.json';
+13 -12
View File
@@ -1,10 +1,15 @@
{
"name": "@univerjs/engine-render",
"version": "0.1.0-beta.2",
"private": false,
"description": "UniverSheet normal base-render",
"keywords": [],
"author": "DreamNum <developer@univer.ai>",
"license": "Apache-2.0",
"keywords": [],
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"main": "./lib/cjs/index.js",
"module": "./lib/es/index.js",
"types": "./lib/types/index.d.ts",
@@ -25,23 +30,24 @@
}
}
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"private": false,
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"lint:types": "tsc --noEmit",
"build": "tsc && vite build"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
},
"dependencies": {
"@univerjs/core": "workspace:*",
"@wendellhu/redi": "^0.12.13",
@@ -50,17 +56,12 @@
"rxjs": "^7.8.1"
},
"devDependencies": {
"@univerjs/infra": "workspace:*",
"@vitest/coverage-istanbul": "^1.1.1",
"less": "^4.2.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-externals": "^0.6.2",
"vitest": "^1.1.1"
},
"peerDependencies": {
"@univerjs/core": "workspace:*",
"@wendellhu/redi": ">=0.12.12",
"rxjs": ">=7.0.0"
}
}

Some files were not shown because too many files have changed in this diff Show More