mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-08 10:33:44 +00:00
Compare commits
5 Commits
v0.26.1-be
...
renovate/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96d1b65850 | ||
|
|
8192a492d9 | ||
|
|
31e11b2563 | ||
|
|
5a36acea7b | ||
|
|
8ce620e2e6 |
2
.github/actions/deploy/deploy.mjs
vendored
2
.github/actions/deploy/deploy.mjs
vendored
@@ -42,7 +42,7 @@ const replicaConfig = {
|
||||
};
|
||||
|
||||
const cpuConfig = {
|
||||
beta: { front: '2', graphql: '1', doc: '1' },
|
||||
beta: { front: '1', graphql: '1', doc: '1' },
|
||||
canary: { front: '500m', graphql: '1', doc: '500m' },
|
||||
};
|
||||
|
||||
|
||||
2
.github/helm/affine/Chart.yaml
vendored
2
.github/helm/affine/Chart.yaml
vendored
@@ -3,4 +3,4 @@ name: affine
|
||||
description: AFFiNE cloud chart
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.26.0"
|
||||
appVersion: "0.26.1"
|
||||
|
||||
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: doc
|
||||
description: AFFiNE doc server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.26.0"
|
||||
appVersion: "0.26.1"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
2
.github/helm/affine/charts/front/Chart.yaml
vendored
2
.github/helm/affine/charts/front/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: front
|
||||
description: AFFiNE front server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.26.0"
|
||||
appVersion: "0.26.1"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
4
.github/helm/affine/charts/front/values.yaml
vendored
4
.github/helm/affine/charts/front/values.yaml
vendored
@@ -30,8 +30,8 @@ podSecurityContext:
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: '2'
|
||||
memory: 4Gi
|
||||
cpu: '1'
|
||||
memory: 2Gi
|
||||
|
||||
probe:
|
||||
initialDelaySeconds: 20
|
||||
|
||||
@@ -3,7 +3,7 @@ name: graphql
|
||||
description: AFFiNE GraphQL server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.26.0"
|
||||
appVersion: "0.26.1"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
72
.github/workflows/sync-i18n.yml
vendored
72
.github/workflows/sync-i18n.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Sync I18n with Crowdin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- canary
|
||||
paths:
|
||||
- 'packages/frontend/i18n/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Crowdin action
|
||||
id: crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
auto_approve_imported: true
|
||||
import_eq_suggestions: true
|
||||
export_only_approved: true
|
||||
skip_untranslated_strings: true
|
||||
localization_branch_name: l10n_crowdin_translations
|
||||
create_pull_request: true
|
||||
pull_request_title: 'chore(i18n): sync translations'
|
||||
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
|
||||
pull_request_base_branch_name: 'canary'
|
||||
config: packages/frontend/i18n/crowdin.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
i18n-codegen:
|
||||
needs: synchronize-with-crowdin
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: l10n_crowdin_translations
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
full-cache: true
|
||||
|
||||
- name: Run i18n codegen
|
||||
run: yarn affine @affine/i18n build
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add .
|
||||
git commit -m "chore(i18n): i18n codegen"
|
||||
git push origin l10n_crowdin_translations
|
||||
13
.vscode/settings.template.json
vendored
13
.vscode/settings.template.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"eslint.packageManager": "yarn",
|
||||
"prisma.pinToPrisma6": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "file",
|
||||
@@ -14,11 +14,13 @@
|
||||
"testid",
|
||||
"schemars"
|
||||
],
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts, ${capture}.d.ts.map",
|
||||
"package.json": ".browserslist*, .circleci*, .codecov, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lighthouserc.*, .lintstagedrc*, .markdownlint*, .mocha*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, api-extractor.json, apollo.config.*, appveyor*, ava.config.*, azure-pipelines*, bower.json, build.config.*, commitlint*, crowdin*, cypress.*, dangerfile*, dlint.json, dprint.json, firebase.json, grunt*, gulp*, histoire.config.*, jasmine.*, jenkins*, jest.config.*, jsconfig.*, karma*, lerna*, lighthouserc.*, lint-staged*, nest-cli.*, netlify*, nodemon*, nx.*, package-lock.json, package.nls*.json, phpcs.xml, playwright.config.*, pm2.*, pnpm*, prettier*, pullapprove*, puppeteer.config.*, pyrightconfig.json, release-tasks.sh, renovate*, rollup.config.*, stylelint*, tsconfig.*, tsdoc.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, vitest.config.*, webpack*, workspace.json, xo.config.*, yarn*, babel.*, .babelrc, project.json",
|
||||
"Cargo.toml": "Cargo.lock",
|
||||
"README.md": "LICENSE, CHANGELOG.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md"
|
||||
"package.json": ".browserslist*, .circleci*, .codecov, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, eslint.*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lighthouserc.*, .lintstagedrc*, .markdownlint*, .mocha*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, api-extractor.json, apollo.config.*, appveyor*, ava.config.*, azure-pipelines*, bower.json, build.config.*, commitlint*, dangerfile*, dlint.json, dprint.json, firebase.json, grunt*, gulp*, histoire.config.*, jasmine.*, jenkins*, jest.config.*, jsconfig.*, karma*, lerna*, lighthouserc.*, lint-staged*, nest-cli.*, netlify*, nodemon*, nx.*, package-lock.json, package.nls*.json, phpcs.xml, playwright.config.*, pm2.*, pnpm*, prettier*, pullapprove*, puppeteer.config.*, pyrightconfig.json, release-tasks.sh, renovate*, rollup.config.*, stylelint*, tsconfig.*, tsdoc.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, vitest.*, webpack*, workspace.json, xo.config.*, yarn*, babel.*, .babelrc, project.json, oxlint.json, nyc.config.*",
|
||||
"Cargo.toml": "Cargo.lock, rust-toolchain*, rustfmt.toml, .taplo.toml",
|
||||
"README.md": "LICENSE*, CHANGELOG.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md, README.*",
|
||||
".gitignore": ".gitattributes, .dockerignore, .eslintignore, .prettierignore, .stylelintignore, .tslintignore, .yarnignore"
|
||||
},
|
||||
"[rust]": {
|
||||
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
||||
@@ -32,5 +34,6 @@
|
||||
"vitest.include": ["packages/**/*.spec.ts", "packages/**/*.spec.tsx"],
|
||||
"rust-analyzer.check.extraEnv": {
|
||||
"DATABASE_URL": "sqlite:affine.db"
|
||||
}
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
|
||||
35
README.md
35
README.md
@@ -21,23 +21,6 @@
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div align="left" valign="middle">
|
||||
<a href="https://runblaze.dev">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://www.runblaze.dev/logo_dark.png">
|
||||
<img align="right" src="https://www.runblaze.dev/logo_light.png" height="102px"/>
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
<br style="display: none;"/>
|
||||
|
||||
_Special thanks to [Blaze](https://runblaze.dev) for their support of this project. They provide high-performance Apple Silicon macOS and Linux (AMD64 & ARM64) runners for GitHub Actions, greatly reducing our automated build times._
|
||||
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div align="center">
|
||||
<a href="https://affine.pro">Home Page</a> |
|
||||
<a href="https://affine.pro/redirect/discord">Discord</a> |
|
||||
@@ -107,10 +90,10 @@ Thanks for checking us out, we appreciate your interest and sincerely hope that
|
||||
|
||||
## Contributing
|
||||
|
||||
| Bug Reports | Feature Requests | Questions/Discussions | AFFiNE Community |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| [Create a bug report](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=bug%2Cproduct-review&template=BUG-REPORT.yml&title=TITLE) | [Submit a feature request](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=feat%2Cproduct-review&template=FEATURE-REQUEST.yml&title=TITLE) | [Check GitHub Discussion](https://github.com/toeverything/AFFiNE/discussions) | [Vist the AFFiNE Community](https://community.affine.pro) |
|
||||
| Something isn't working as expected | An idea for a new feature, or improvements | Discuss and ask questions | A place to ask, learn and engage with others |
|
||||
| Bug Reports | Feature Requests | Questions/Discussions | AFFiNE Community |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
||||
| [Create a bug report](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=bug%2Cproduct-review&template=BUG-REPORT.yml&title=TITLE) | [Submit a feature request](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=feat%2Cproduct-review&template=FEATURE-REQUEST.yml&title=TITLE) | [Check GitHub Discussion](https://github.com/toeverything/AFFiNE/discussions) | [Visit the AFFiNE Community](https://community.affine.pro) |
|
||||
| Something isn't working as expected | An idea for a new feature, or improvements | Discuss and ask questions | A place to ask, learn and engage with others |
|
||||
|
||||
Calling all developers, testers, tech writers and more! Contributions of all types are more than welcome, you can read more in [docs/types-of-contributions.md](docs/types-of-contributions.md). If you are interested in contributing code, read our [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) and feel free to check out our GitHub issues to get stuck in to show us what you’re made of.
|
||||
|
||||
@@ -169,8 +152,10 @@ Welcome to the AFFiNE blog section! Here, you’ll find the latest insights, tip
|
||||
We would also like to give thanks to open-source projects that make AFFiNE possible:
|
||||
|
||||
- [Blocksuite](https://github.com/toeverything/BlockSuite) - 💠 BlockSuite is the open-source collaborative editor project behind AFFiNE.
|
||||
- [y-octo](https://github.com/y-crdt/y-octo) - 🐙 y-octo is a native, high-performance, thread-safe YJS CRDT implementation, serving as the core engine enabling the AFFiNE Client/Server to achieve "local-first" functionality.
|
||||
- [OctoBase](https://github.com/toeverything/OctoBase) - 🐙 OctoBase is the open-source database behind AFFiNE, local-first, yet collaborative. A light-weight, scalable, data engine written in Rust.
|
||||
- [yjs](https://github.com/yjs/yjs) - Fundamental support of CRDTs for our implementation on state management and data sync.
|
||||
|
||||
- [yjs](https://github.com/yjs/yjs) - Fundamental support of CRDTs for our implementation on state management and data sync on web.
|
||||
- [electron](https://github.com/electron/electron) - Build cross-platform desktop apps with JavaScript, HTML, and CSS.
|
||||
- [React](https://github.com/facebook/react) - The library for web and native user interfaces.
|
||||
- [napi-rs](https://github.com/napi-rs/napi-rs) - A framework for building compiled Node.js add-ons in Rust via Node-API.
|
||||
@@ -221,12 +206,6 @@ See [BUILDING.md] for instructions on how to build AFFiNE from source code.
|
||||
We welcome contributions from everyone.
|
||||
See [docs/contributing/tutorial.md](./docs/contributing/tutorial.md) for details.
|
||||
|
||||
## Thanks
|
||||
|
||||
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" width="153" height="30" alt="Chromatic" /></a>
|
||||
|
||||
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
|
||||
|
||||
## License
|
||||
|
||||
### Editions
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0",
|
||||
"version": "0.26.1",
|
||||
"devDependencies": {
|
||||
"@vanilla-extract/vite-plugin": "^5.0.0",
|
||||
"msw": "^2.12.4",
|
||||
|
||||
@@ -41,5 +41,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -48,5 +48,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -48,5 +48,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -39,5 +39,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -67,5 +67,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -82,5 +82,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -48,5 +48,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
459
blocksuite/affine/data-view/src/__tests__/kanban.unit.spec.ts
Normal file
459
blocksuite/affine/data-view/src/__tests__/kanban.unit.spec.ts
Normal file
@@ -0,0 +1,459 @@
|
||||
import { signal } from '@preact/signals-core';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { GroupBy } from '../core/common/types.js';
|
||||
import type { DataSource } from '../core/data-source/base.js';
|
||||
import { groupByMatchers } from '../core/group-by/define.js';
|
||||
import { t } from '../core/logical/type-presets.js';
|
||||
import { checkboxPropertyModelConfig } from '../property-presets/checkbox/define.js';
|
||||
import { multiSelectPropertyModelConfig } from '../property-presets/multi-select/define.js';
|
||||
import { selectPropertyModelConfig } from '../property-presets/select/define.js';
|
||||
import { textPropertyModelConfig } from '../property-presets/text/define.js';
|
||||
import {
|
||||
canGroupable,
|
||||
ensureKanbanGroupColumn,
|
||||
pickKanbanGroupColumn,
|
||||
resolveKanbanGroupBy,
|
||||
} from '../view-presets/kanban/group-by-utils.js';
|
||||
import { materializeKanbanColumns } from '../view-presets/kanban/kanban-view-manager.js';
|
||||
import type { KanbanCard } from '../view-presets/kanban/pc/card.js';
|
||||
import { KanbanDragController } from '../view-presets/kanban/pc/controller/drag.js';
|
||||
import type { KanbanGroup } from '../view-presets/kanban/pc/group.js';
|
||||
|
||||
type Column = {
|
||||
id: string;
|
||||
type: string;
|
||||
data?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
type TestPropertyMeta = {
|
||||
type: string;
|
||||
config: {
|
||||
kanbanGroup?: {
|
||||
enabled: boolean;
|
||||
mutable?: boolean;
|
||||
};
|
||||
propertyData: {
|
||||
default: () => Record<string, unknown>;
|
||||
};
|
||||
jsonValue: {
|
||||
type: (options: {
|
||||
data: Record<string, unknown>;
|
||||
dataSource: DataSource;
|
||||
}) => unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type MockDataSource = {
|
||||
properties$: ReturnType<typeof signal<string[]>>;
|
||||
provider: {
|
||||
getAll: () => Map<unknown, unknown>;
|
||||
};
|
||||
serviceGetOrCreate: (key: unknown, create: () => unknown) => unknown;
|
||||
propertyTypeGet: (propertyId: string) => string | undefined;
|
||||
propertyMetaGet: (type: string) => TestPropertyMeta | undefined;
|
||||
propertyDataGet: (propertyId: string) => Record<string, unknown>;
|
||||
propertyDataTypeGet: (propertyId: string) => unknown;
|
||||
propertyAdd: (
|
||||
_position: unknown,
|
||||
ops?: {
|
||||
type?: string;
|
||||
}
|
||||
) => string;
|
||||
propertyDataSet: (propertyId: string, data: Record<string, unknown>) => void;
|
||||
};
|
||||
|
||||
const asDataSource = (dataSource: object): DataSource =>
|
||||
dataSource as DataSource;
|
||||
|
||||
const toTestMeta = <TData extends Record<string, unknown>>(
|
||||
type: string,
|
||||
config: {
|
||||
kanbanGroup?: {
|
||||
enabled: boolean;
|
||||
mutable?: boolean;
|
||||
};
|
||||
propertyData: {
|
||||
default: () => TData;
|
||||
};
|
||||
jsonValue: {
|
||||
type: (options: { data: TData; dataSource: DataSource }) => unknown;
|
||||
};
|
||||
}
|
||||
): TestPropertyMeta => ({
|
||||
type,
|
||||
config: {
|
||||
kanbanGroup: config.kanbanGroup,
|
||||
propertyData: {
|
||||
default: () => config.propertyData.default(),
|
||||
},
|
||||
jsonValue: {
|
||||
type: ({ data, dataSource }) =>
|
||||
config.jsonValue.type({
|
||||
data: data as TData,
|
||||
dataSource,
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const immutableBooleanMeta = toTestMeta('immutable-boolean', {
|
||||
...checkboxPropertyModelConfig.config,
|
||||
kanbanGroup: {
|
||||
enabled: true,
|
||||
mutable: false,
|
||||
},
|
||||
});
|
||||
|
||||
const createMockDataSource = (columns: Column[]): MockDataSource => {
|
||||
const properties$ = signal(columns.map(column => column.id));
|
||||
const typeById = new Map(columns.map(column => [column.id, column.type]));
|
||||
const dataById = new Map(
|
||||
columns.map(column => [column.id, column.data ?? {}])
|
||||
);
|
||||
const services = new Map<unknown, unknown>();
|
||||
|
||||
const metaEntries: Array<[string, TestPropertyMeta]> = [
|
||||
[
|
||||
checkboxPropertyModelConfig.type,
|
||||
toTestMeta(
|
||||
checkboxPropertyModelConfig.type,
|
||||
checkboxPropertyModelConfig.config
|
||||
),
|
||||
],
|
||||
[
|
||||
selectPropertyModelConfig.type,
|
||||
toTestMeta(
|
||||
selectPropertyModelConfig.type,
|
||||
selectPropertyModelConfig.config
|
||||
),
|
||||
],
|
||||
[
|
||||
multiSelectPropertyModelConfig.type,
|
||||
toTestMeta(
|
||||
multiSelectPropertyModelConfig.type,
|
||||
multiSelectPropertyModelConfig.config
|
||||
),
|
||||
],
|
||||
[
|
||||
textPropertyModelConfig.type,
|
||||
toTestMeta(textPropertyModelConfig.type, textPropertyModelConfig.config),
|
||||
],
|
||||
[immutableBooleanMeta.type, immutableBooleanMeta],
|
||||
];
|
||||
const metaByType = new Map(metaEntries);
|
||||
|
||||
const asRecord = (value: unknown): Record<string, unknown> =>
|
||||
typeof value === 'object' && value != null
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
let autoColumnId = 0;
|
||||
|
||||
const dataSource = {
|
||||
properties$,
|
||||
provider: {
|
||||
getAll: () => new Map<unknown, unknown>(),
|
||||
},
|
||||
serviceGetOrCreate: (key: unknown, create: () => unknown) => {
|
||||
if (!services.has(key)) {
|
||||
services.set(key, create());
|
||||
}
|
||||
return services.get(key);
|
||||
},
|
||||
propertyTypeGet: (propertyId: string) => typeById.get(propertyId),
|
||||
propertyMetaGet: (type: string) => metaByType.get(type),
|
||||
propertyDataGet: (propertyId: string) => asRecord(dataById.get(propertyId)),
|
||||
propertyDataTypeGet: (propertyId: string) => {
|
||||
const type = typeById.get(propertyId);
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const meta = metaByType.get(type);
|
||||
if (!meta) {
|
||||
return;
|
||||
}
|
||||
return meta.config.jsonValue.type({
|
||||
data: asRecord(dataById.get(propertyId)),
|
||||
dataSource: asDataSource(dataSource),
|
||||
});
|
||||
},
|
||||
propertyAdd: (
|
||||
_position: unknown,
|
||||
ops?: {
|
||||
type?: string;
|
||||
}
|
||||
) => {
|
||||
const type = ops?.type ?? selectPropertyModelConfig.type;
|
||||
const id = `auto-${++autoColumnId}`;
|
||||
const meta = metaByType.get(type);
|
||||
const data = meta?.config.propertyData.default() ?? {};
|
||||
|
||||
typeById.set(id, type);
|
||||
dataById.set(id, data);
|
||||
properties$.value = [...properties$.value, id];
|
||||
return id;
|
||||
},
|
||||
propertyDataSet: (propertyId: string, data: Record<string, unknown>) => {
|
||||
dataById.set(propertyId, data);
|
||||
},
|
||||
};
|
||||
|
||||
return dataSource;
|
||||
};
|
||||
|
||||
const createDragController = () => {
|
||||
type DragLogic = ConstructorParameters<typeof KanbanDragController>[0];
|
||||
return new KanbanDragController({} as DragLogic);
|
||||
};
|
||||
|
||||
describe('kanban', () => {
|
||||
describe('group-by define', () => {
|
||||
it('boolean group should not include ungroup bucket', () => {
|
||||
const booleanGroup = groupByMatchers.find(
|
||||
group => group.name === 'boolean'
|
||||
);
|
||||
expect(booleanGroup).toBeDefined();
|
||||
|
||||
const keys = booleanGroup!
|
||||
.defaultKeys(t.boolean.instance())
|
||||
.map(group => group.key);
|
||||
|
||||
expect(keys).toEqual(['true', 'false']);
|
||||
});
|
||||
|
||||
it('boolean group should fallback invalid values to false bucket', () => {
|
||||
const booleanGroup = groupByMatchers.find(
|
||||
group => group.name === 'boolean'
|
||||
);
|
||||
expect(booleanGroup).toBeDefined();
|
||||
|
||||
const groups = booleanGroup!.valuesGroup(undefined, t.boolean.instance());
|
||||
expect(groups).toEqual([{ key: 'false', value: false }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('columns materialization', () => {
|
||||
it('appends missing properties while preserving existing order and state', () => {
|
||||
const columns = [{ id: 'status', hide: true }, { id: 'title' }];
|
||||
|
||||
const next = materializeKanbanColumns(columns, [
|
||||
'title',
|
||||
'status',
|
||||
'date',
|
||||
]);
|
||||
|
||||
expect(next).toEqual([
|
||||
{ id: 'status', hide: true },
|
||||
{ id: 'title' },
|
||||
{ id: 'date' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('drops stale columns that no longer exist in data source', () => {
|
||||
const columns = [{ id: 'title' }, { id: 'removed', hide: true }];
|
||||
|
||||
const next = materializeKanbanColumns(columns, ['title']);
|
||||
|
||||
expect(next).toEqual([{ id: 'title' }]);
|
||||
});
|
||||
|
||||
it('returns original reference when columns are already materialized', () => {
|
||||
const columns = [{ id: 'title' }, { id: 'status', hide: true }];
|
||||
|
||||
const next = materializeKanbanColumns(columns, ['title', 'status']);
|
||||
|
||||
expect(next).toBe(columns);
|
||||
});
|
||||
});
|
||||
|
||||
describe('drag indicator', () => {
|
||||
it('shows drop preview when insert position exists', () => {
|
||||
const controller = createDragController();
|
||||
const position = {
|
||||
group: {} as KanbanGroup,
|
||||
position: 'end' as const,
|
||||
};
|
||||
controller.getInsertPosition = vi.fn().mockReturnValue(position);
|
||||
|
||||
const displaySpy = vi.spyOn(controller.dropPreview, 'display');
|
||||
const removeSpy = vi.spyOn(controller.dropPreview, 'remove');
|
||||
|
||||
const result = controller.showIndicator({} as MouseEvent, undefined);
|
||||
|
||||
expect(result).toBe(position);
|
||||
expect(displaySpy).toHaveBeenCalledWith(
|
||||
position.group,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
expect(removeSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('removes drop preview when insert position does not exist', () => {
|
||||
const controller = createDragController();
|
||||
controller.getInsertPosition = vi.fn().mockReturnValue(undefined);
|
||||
|
||||
const displaySpy = vi.spyOn(controller.dropPreview, 'display');
|
||||
const removeSpy = vi.spyOn(controller.dropPreview, 'remove');
|
||||
|
||||
const result = controller.showIndicator({} as MouseEvent, undefined);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(displaySpy).not.toHaveBeenCalled();
|
||||
expect(removeSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('forwards hovered card to drop preview for precise insertion cursor', () => {
|
||||
const controller = createDragController();
|
||||
const hoveredCard = document.createElement(
|
||||
'affine-data-view-kanban-card'
|
||||
) as KanbanCard;
|
||||
const positionCard = document.createElement(
|
||||
'affine-data-view-kanban-card'
|
||||
) as KanbanCard;
|
||||
const position = {
|
||||
group: {} as KanbanGroup,
|
||||
card: positionCard,
|
||||
position: { before: true, id: 'card-id' } as const,
|
||||
};
|
||||
controller.getInsertPosition = vi.fn().mockReturnValue(position);
|
||||
|
||||
const displaySpy = vi.spyOn(controller.dropPreview, 'display');
|
||||
|
||||
controller.showIndicator({} as MouseEvent, hoveredCard);
|
||||
|
||||
expect(displaySpy).toHaveBeenCalledWith(
|
||||
position.group,
|
||||
hoveredCard,
|
||||
position.card
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('group-by utils', () => {
|
||||
it('allows only kanban-enabled property types to group', () => {
|
||||
const dataSource = createMockDataSource([
|
||||
{ id: 'text', type: textPropertyModelConfig.type },
|
||||
{ id: 'select', type: selectPropertyModelConfig.type },
|
||||
{ id: 'multi-select', type: multiSelectPropertyModelConfig.type },
|
||||
{ id: 'checkbox', type: checkboxPropertyModelConfig.type },
|
||||
]);
|
||||
|
||||
expect(canGroupable(asDataSource(dataSource), 'text')).toBe(false);
|
||||
expect(canGroupable(asDataSource(dataSource), 'select')).toBe(true);
|
||||
expect(canGroupable(asDataSource(dataSource), 'multi-select')).toBe(true);
|
||||
expect(canGroupable(asDataSource(dataSource), 'checkbox')).toBe(true);
|
||||
});
|
||||
|
||||
it('prefers mutable group column over immutable ones', () => {
|
||||
const dataSource = createMockDataSource([
|
||||
{
|
||||
id: 'immutable-bool',
|
||||
type: 'immutable-boolean',
|
||||
},
|
||||
{
|
||||
id: 'checkbox',
|
||||
type: checkboxPropertyModelConfig.type,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(pickKanbanGroupColumn(asDataSource(dataSource))).toBe('checkbox');
|
||||
});
|
||||
|
||||
it('creates default status select column when no groupable column exists', () => {
|
||||
const dataSource = createMockDataSource([
|
||||
{
|
||||
id: 'text',
|
||||
type: textPropertyModelConfig.type,
|
||||
},
|
||||
]);
|
||||
|
||||
const statusColumnId = ensureKanbanGroupColumn(asDataSource(dataSource));
|
||||
|
||||
expect(statusColumnId).toBeTruthy();
|
||||
expect(dataSource.propertyTypeGet(statusColumnId!)).toBe(
|
||||
selectPropertyModelConfig.type
|
||||
);
|
||||
const options =
|
||||
(
|
||||
dataSource.propertyDataGet(statusColumnId!) as {
|
||||
options?: { value: string }[];
|
||||
}
|
||||
).options ?? [];
|
||||
expect(options.map(option => option.value)).toEqual([
|
||||
'Todo',
|
||||
'In Progress',
|
||||
'Done',
|
||||
]);
|
||||
});
|
||||
|
||||
it('defaults hideEmpty to true for non-option groups', () => {
|
||||
const dataSource = createMockDataSource([
|
||||
{
|
||||
id: 'checkbox',
|
||||
type: checkboxPropertyModelConfig.type,
|
||||
},
|
||||
]);
|
||||
|
||||
const next = resolveKanbanGroupBy(asDataSource(dataSource));
|
||||
expect(next?.columnId).toBe('checkbox');
|
||||
expect(next?.hideEmpty).toBe(true);
|
||||
expect(next?.name).toBe('boolean');
|
||||
});
|
||||
|
||||
it('defaults hideEmpty to false for select grouping', () => {
|
||||
const dataSource = createMockDataSource([
|
||||
{
|
||||
id: 'select',
|
||||
type: selectPropertyModelConfig.type,
|
||||
},
|
||||
]);
|
||||
|
||||
const next = resolveKanbanGroupBy(asDataSource(dataSource));
|
||||
expect(next?.columnId).toBe('select');
|
||||
expect(next?.hideEmpty).toBe(false);
|
||||
expect(next?.name).toBe('select');
|
||||
});
|
||||
|
||||
it('preserves sort and explicit hideEmpty when resolving groupBy', () => {
|
||||
const dataSource = createMockDataSource([
|
||||
{
|
||||
id: 'checkbox',
|
||||
type: checkboxPropertyModelConfig.type,
|
||||
},
|
||||
]);
|
||||
const current: GroupBy = {
|
||||
type: 'groupBy',
|
||||
columnId: 'checkbox',
|
||||
name: 'boolean',
|
||||
sort: { desc: true },
|
||||
hideEmpty: true,
|
||||
};
|
||||
|
||||
const next = resolveKanbanGroupBy(asDataSource(dataSource), current);
|
||||
|
||||
expect(next?.columnId).toBe('checkbox');
|
||||
expect(next?.sort).toEqual({ desc: true });
|
||||
expect(next?.hideEmpty).toBe(true);
|
||||
});
|
||||
|
||||
it('replaces current non-groupable column with a valid kanban column', () => {
|
||||
const dataSource = createMockDataSource([
|
||||
{ id: 'text', type: textPropertyModelConfig.type },
|
||||
{ id: 'checkbox', type: checkboxPropertyModelConfig.type },
|
||||
]);
|
||||
|
||||
const next = resolveKanbanGroupBy(asDataSource(dataSource), {
|
||||
type: 'groupBy',
|
||||
columnId: 'text',
|
||||
name: 'text',
|
||||
});
|
||||
|
||||
expect(next?.columnId).toBe('checkbox');
|
||||
expect(next?.name).toBe('boolean');
|
||||
expect(next?.hideEmpty).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -247,12 +247,13 @@ export const groupByMatchers: GroupByConfig[] = [
|
||||
matchType: t.boolean.instance(),
|
||||
groupName: (_t, v) => `${v?.toString() ?? ''}`,
|
||||
defaultKeys: _t => [
|
||||
ungroups,
|
||||
{ key: 'true', value: true },
|
||||
{ key: 'false', value: false },
|
||||
],
|
||||
valuesGroup: (v, _t) =>
|
||||
typeof v !== 'boolean' ? [ungroups] : [{ key: v.toString(), value: v }],
|
||||
typeof v !== 'boolean'
|
||||
? [{ key: 'false', value: false }]
|
||||
: [{ key: v.toString(), value: v }],
|
||||
addToGroup: (v: boolean | null, _old: boolean | null) => v,
|
||||
view: createUniComponentFromWebComponent(BooleanGroupView),
|
||||
}),
|
||||
|
||||
@@ -17,6 +17,7 @@ import { css, html, unsafeCSS } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import { canGroupable } from '../../view-presets/kanban/group-by-utils.js';
|
||||
import { KanbanSingleView } from '../../view-presets/kanban/kanban-view-manager.js';
|
||||
import { TableSingleView } from '../../view-presets/table/table-view-manager.js';
|
||||
import { dataViewCssVariable } from '../common/css-variable.js';
|
||||
@@ -278,6 +279,9 @@ export const selectGroupByProperty = (
|
||||
if (property.type$.value === 'title') {
|
||||
return false;
|
||||
}
|
||||
if (view instanceof KanbanSingleView) {
|
||||
return canGroupable(view.manager.dataSource, property.id);
|
||||
}
|
||||
const dataType = property.dataType$.value;
|
||||
if (!dataType) {
|
||||
return false;
|
||||
|
||||
@@ -16,6 +16,10 @@ export type GetJsonValueFromConfig<T> =
|
||||
export type PropertyConfig<Data, RawValue = unknown, JsonValue = unknown> = {
|
||||
name: string;
|
||||
hide?: boolean;
|
||||
kanbanGroup?: {
|
||||
enabled: boolean;
|
||||
mutable?: boolean;
|
||||
};
|
||||
propertyData: {
|
||||
schema: ZodType<Data>;
|
||||
default: () => Data;
|
||||
|
||||
@@ -21,6 +21,10 @@ const FALSE_VALUES = new Set([
|
||||
|
||||
export const checkboxPropertyModelConfig = checkboxPropertyType.modelConfig({
|
||||
name: 'Checkbox',
|
||||
kanbanGroup: {
|
||||
enabled: true,
|
||||
mutable: true,
|
||||
},
|
||||
propertyData: {
|
||||
schema: zod.object({}),
|
||||
default: () => ({}),
|
||||
|
||||
@@ -10,6 +10,10 @@ export const multiSelectPropertyType = propertyType('multi-select');
|
||||
export const multiSelectPropertyModelConfig =
|
||||
multiSelectPropertyType.modelConfig({
|
||||
name: 'Multi-select',
|
||||
kanbanGroup: {
|
||||
enabled: true,
|
||||
mutable: true,
|
||||
},
|
||||
propertyData: {
|
||||
schema: SelectPropertySchema,
|
||||
default: () => ({
|
||||
|
||||
@@ -11,6 +11,10 @@ export const SelectPropertySchema = zod.object({
|
||||
export type SelectPropertyData = zod.infer<typeof SelectPropertySchema>;
|
||||
export const selectPropertyModelConfig = selectPropertyType.modelConfig({
|
||||
name: 'Select',
|
||||
kanbanGroup: {
|
||||
enabled: true,
|
||||
mutable: true,
|
||||
},
|
||||
propertyData: {
|
||||
schema: SelectPropertySchema,
|
||||
default: () => ({
|
||||
|
||||
@@ -3,17 +3,9 @@ import { kanbanViewModel } from './kanban/index.js';
|
||||
import { tableViewModel } from './table/index.js';
|
||||
|
||||
export const viewConverts = [
|
||||
createViewConvert(tableViewModel, kanbanViewModel, data => {
|
||||
if (data.groupBy) {
|
||||
return {
|
||||
filter: data.filter,
|
||||
groupBy: data.groupBy,
|
||||
};
|
||||
}
|
||||
return {
|
||||
filter: data.filter,
|
||||
};
|
||||
}),
|
||||
createViewConvert(tableViewModel, kanbanViewModel, data => ({
|
||||
filter: data.filter,
|
||||
})),
|
||||
createViewConvert(kanbanViewModel, tableViewModel, data => ({
|
||||
filter: data.filter,
|
||||
groupBy: data.groupBy,
|
||||
|
||||
@@ -2,9 +2,9 @@ import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
|
||||
import type { GroupBy, GroupProperty } from '../../core/common/types.js';
|
||||
import type { FilterGroup } from '../../core/filter/types.js';
|
||||
import { defaultGroupBy, getGroupByService, t } from '../../core/index.js';
|
||||
import type { Sort } from '../../core/sort/types.js';
|
||||
import { type BasicViewDataType, viewType } from '../../core/view/data-view.js';
|
||||
import { resolveKanbanGroupBy } from './group-by-utils.js';
|
||||
import { KanbanSingleView } from './kanban-view-manager.js';
|
||||
|
||||
export const kanbanViewType = viewType('kanban');
|
||||
@@ -34,41 +34,16 @@ export const kanbanViewModel = kanbanViewType.createModel<KanbanViewData>({
|
||||
defaultName: 'Kanban View',
|
||||
dataViewManager: KanbanSingleView,
|
||||
defaultData: viewManager => {
|
||||
const groupByService = getGroupByService(viewManager.dataSource);
|
||||
const columns = viewManager.dataSource.properties$.value;
|
||||
const allowList = columns.filter(columnId => {
|
||||
const dataType = viewManager.dataSource.propertyDataTypeGet(columnId);
|
||||
return dataType && !!groupByService?.matcher.match(dataType);
|
||||
});
|
||||
const getWeight = (columnId: string) => {
|
||||
const dataType = viewManager.dataSource.propertyDataTypeGet(columnId);
|
||||
if (!dataType || t.string.is(dataType) || t.richText.is(dataType)) {
|
||||
return 0;
|
||||
}
|
||||
if (t.tag.is(dataType)) {
|
||||
return 3;
|
||||
}
|
||||
if (t.array.is(dataType)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
const columnId = allowList.sort((a, b) => getWeight(b) - getWeight(a))[0];
|
||||
if (!columnId) {
|
||||
const groupBy = resolveKanbanGroupBy(viewManager.dataSource);
|
||||
if (!groupBy) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.DatabaseBlockError,
|
||||
'no groupable column found'
|
||||
);
|
||||
}
|
||||
const type = viewManager.dataSource.propertyTypeGet(columnId);
|
||||
const meta = type && viewManager.dataSource.propertyMetaGet(type);
|
||||
const data = viewManager.dataSource.propertyDataGet(columnId);
|
||||
if (!columnId || !meta || !data) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.DatabaseBlockError,
|
||||
'not implement yet'
|
||||
);
|
||||
}
|
||||
|
||||
const columns = viewManager.dataSource.properties$.value;
|
||||
|
||||
return {
|
||||
columns: columns.map(id => ({
|
||||
id: id,
|
||||
@@ -78,7 +53,7 @@ export const kanbanViewModel = kanbanViewType.createModel<KanbanViewData>({
|
||||
op: 'and',
|
||||
conditions: [],
|
||||
},
|
||||
groupBy: defaultGroupBy(viewManager.dataSource, meta, columnId, data),
|
||||
groupBy,
|
||||
header: {
|
||||
titleColumn: viewManager.dataSource.properties$.value.find(
|
||||
id => viewManager.dataSource.propertyTypeGet(id) === 'title'
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
|
||||
import type { GroupBy } from '../../core/common/types.js';
|
||||
import { getTagColor } from '../../core/component/tags/colors.js';
|
||||
import type { DataSource } from '../../core/data-source/base.js';
|
||||
import { defaultGroupBy } from '../../core/group-by/default.js';
|
||||
import { getGroupByService } from '../../core/group-by/matcher.js';
|
||||
|
||||
type KanbanGroupCapability = 'mutable' | 'immutable' | 'none';
|
||||
|
||||
const KANBAN_DEFAULT_STATUS_OPTIONS = ['Todo', 'In Progress', 'Done'];
|
||||
const SHOW_EMPTY_GROUPS_BY_DEFAULT = new Set(['select', 'multi-select']);
|
||||
|
||||
export const getKanbanDefaultHideEmpty = (groupName?: string): boolean => {
|
||||
return !groupName || !SHOW_EMPTY_GROUPS_BY_DEFAULT.has(groupName);
|
||||
};
|
||||
|
||||
const getKanbanGroupCapability = (
|
||||
dataSource: DataSource,
|
||||
propertyId: string
|
||||
): KanbanGroupCapability => {
|
||||
const type = dataSource.propertyTypeGet(propertyId);
|
||||
if (!type) {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
const meta = dataSource.propertyMetaGet(type);
|
||||
const kanbanGroup = meta?.config.kanbanGroup;
|
||||
if (!kanbanGroup?.enabled) {
|
||||
return 'none';
|
||||
}
|
||||
return kanbanGroup.mutable ? 'mutable' : 'immutable';
|
||||
};
|
||||
|
||||
const hasMatchingGroupBy = (dataSource: DataSource, propertyId: string) => {
|
||||
const dataType = dataSource.propertyDataTypeGet(propertyId);
|
||||
if (!dataType) {
|
||||
return false;
|
||||
}
|
||||
const groupByService = getGroupByService(dataSource);
|
||||
return !!groupByService?.matcher.match(dataType);
|
||||
};
|
||||
|
||||
const createGroupByFromColumn = (
|
||||
dataSource: DataSource,
|
||||
columnId: string
|
||||
): GroupBy | undefined => {
|
||||
const type = dataSource.propertyTypeGet(columnId);
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const meta = dataSource.propertyMetaGet(type);
|
||||
if (!meta) {
|
||||
return;
|
||||
}
|
||||
return defaultGroupBy(
|
||||
dataSource,
|
||||
meta,
|
||||
columnId,
|
||||
dataSource.propertyDataGet(columnId)
|
||||
);
|
||||
};
|
||||
|
||||
export const canGroupable = (dataSource: DataSource, propertyId: string) => {
|
||||
return (
|
||||
getKanbanGroupCapability(dataSource, propertyId) !== 'none' &&
|
||||
hasMatchingGroupBy(dataSource, propertyId)
|
||||
);
|
||||
};
|
||||
|
||||
export const pickKanbanGroupColumn = (
|
||||
dataSource: DataSource,
|
||||
propertyIds: string[] = dataSource.properties$.value
|
||||
): string | undefined => {
|
||||
let immutableFallback: string | undefined;
|
||||
|
||||
for (const propertyId of propertyIds) {
|
||||
const capability = getKanbanGroupCapability(dataSource, propertyId);
|
||||
if (capability === 'none' || !hasMatchingGroupBy(dataSource, propertyId)) {
|
||||
continue;
|
||||
}
|
||||
if (capability === 'mutable') {
|
||||
return propertyId;
|
||||
}
|
||||
immutableFallback ??= propertyId;
|
||||
}
|
||||
|
||||
return immutableFallback;
|
||||
};
|
||||
|
||||
export const ensureKanbanGroupColumn = (
|
||||
dataSource: DataSource
|
||||
): string | undefined => {
|
||||
const columnId = pickKanbanGroupColumn(dataSource);
|
||||
if (columnId) {
|
||||
return columnId;
|
||||
}
|
||||
|
||||
const statusId = dataSource.propertyAdd('end', {
|
||||
type: 'select',
|
||||
name: 'Status',
|
||||
});
|
||||
if (!statusId) {
|
||||
return;
|
||||
}
|
||||
|
||||
dataSource.propertyDataSet(statusId, {
|
||||
options: KANBAN_DEFAULT_STATUS_OPTIONS.map(value => ({
|
||||
id: nanoid(),
|
||||
value,
|
||||
color: getTagColor(),
|
||||
})),
|
||||
});
|
||||
|
||||
return statusId;
|
||||
};
|
||||
|
||||
export const resolveKanbanGroupBy = (
|
||||
dataSource: DataSource,
|
||||
current?: GroupBy
|
||||
): GroupBy | undefined => {
|
||||
const keepColumnId =
|
||||
current?.columnId && canGroupable(dataSource, current.columnId)
|
||||
? current.columnId
|
||||
: undefined;
|
||||
|
||||
const columnId = keepColumnId ?? ensureKanbanGroupColumn(dataSource);
|
||||
if (!columnId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const next = createGroupByFromColumn(dataSource, columnId);
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
...next,
|
||||
sort: current?.sort,
|
||||
hideEmpty: current?.hideEmpty ?? getKanbanDefaultHideEmpty(next.name),
|
||||
};
|
||||
};
|
||||
@@ -17,7 +17,52 @@ import {
|
||||
import { fromJson } from '../../core/property/utils';
|
||||
import { PropertyBase } from '../../core/view-manager/property.js';
|
||||
import { SingleViewBase } from '../../core/view-manager/single-view.js';
|
||||
import type { KanbanViewData } from './define.js';
|
||||
import type { ViewManager } from '../../core/view-manager/view-manager.js';
|
||||
import type { KanbanViewColumn, KanbanViewData } from './define.js';
|
||||
import {
|
||||
getKanbanDefaultHideEmpty,
|
||||
resolveKanbanGroupBy,
|
||||
} from './group-by-utils.js';
|
||||
|
||||
const materializeColumnsByPropertyIds = (
|
||||
columns: KanbanViewColumn[],
|
||||
propertyIds: string[]
|
||||
) => {
|
||||
const needShow = new Set(propertyIds);
|
||||
const orderedColumns: KanbanViewColumn[] = [];
|
||||
|
||||
for (const column of columns) {
|
||||
if (needShow.has(column.id)) {
|
||||
orderedColumns.push(column);
|
||||
needShow.delete(column.id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of needShow) {
|
||||
orderedColumns.push({ id });
|
||||
}
|
||||
|
||||
return orderedColumns;
|
||||
};
|
||||
|
||||
export const materializeKanbanColumns = (
|
||||
columns: KanbanViewColumn[],
|
||||
propertyIds: string[]
|
||||
) => {
|
||||
const nextColumns = materializeColumnsByPropertyIds(columns, propertyIds);
|
||||
const unchanged =
|
||||
columns.length === nextColumns.length &&
|
||||
columns.every((column, index) => {
|
||||
const nextColumn = nextColumns[index];
|
||||
return (
|
||||
nextColumn != null &&
|
||||
column.id === nextColumn.id &&
|
||||
column.hide === nextColumn.hide
|
||||
);
|
||||
});
|
||||
|
||||
return unchanged ? columns : nextColumns;
|
||||
};
|
||||
|
||||
export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
propertiesRaw$ = computed(() => {
|
||||
@@ -61,16 +106,27 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
);
|
||||
|
||||
groupBy$ = computed(() => {
|
||||
return this.data$.value?.groupBy;
|
||||
const groupBy = this.data$.value?.groupBy;
|
||||
if (!groupBy || groupBy.hideEmpty != null) {
|
||||
return groupBy;
|
||||
}
|
||||
return {
|
||||
...groupBy,
|
||||
hideEmpty: getKanbanDefaultHideEmpty(groupBy.name),
|
||||
};
|
||||
});
|
||||
|
||||
groupTrait = this.traitSet(
|
||||
groupTraitKey,
|
||||
new GroupTrait(this.groupBy$, this, {
|
||||
groupBySet: groupBy => {
|
||||
const nextGroupBy = resolveKanbanGroupBy(
|
||||
this.manager.dataSource,
|
||||
groupBy
|
||||
);
|
||||
this.dataUpdate(() => {
|
||||
return {
|
||||
groupBy: groupBy,
|
||||
groupBy: nextGroupBy,
|
||||
};
|
||||
});
|
||||
},
|
||||
@@ -200,6 +256,23 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
return this.view?.mode ?? 'kanban';
|
||||
}
|
||||
|
||||
private materializeColumns() {
|
||||
const view = this.view;
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextColumns = materializeKanbanColumns(
|
||||
view.columns,
|
||||
this.dataSource.properties$.value
|
||||
);
|
||||
if (nextColumns === view.columns) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dataUpdate(() => ({ columns: nextColumns }));
|
||||
}
|
||||
|
||||
get view() {
|
||||
return this.data$.value;
|
||||
}
|
||||
@@ -289,6 +362,13 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
propertyGetOrCreate(columnId: string): KanbanColumn {
|
||||
return new KanbanColumn(this, columnId);
|
||||
}
|
||||
|
||||
constructor(viewManager: ViewManager, viewId: string) {
|
||||
super(viewManager, viewId);
|
||||
// Materialize view columns on view activation so newly added properties
|
||||
// can participate in hide/order operations in kanban.
|
||||
this.materializeColumns();
|
||||
}
|
||||
}
|
||||
|
||||
type KanbanColumnData = KanbanViewData['columns'][number];
|
||||
|
||||
@@ -190,7 +190,7 @@ const createDragPreview = (card: KanbanCard, x: number, y: number) => {
|
||||
div.className = 'with-data-view-css-variable';
|
||||
div.style.width = `${card.getBoundingClientRect().width}px`;
|
||||
div.style.position = 'fixed';
|
||||
// div.style.pointerEvents = 'none';
|
||||
div.style.pointerEvents = 'none';
|
||||
div.style.transform = 'rotate(-3deg)';
|
||||
div.style.left = `${x}px`;
|
||||
div.style.top = `${y}px`;
|
||||
@@ -209,8 +209,12 @@ const createDragPreview = (card: KanbanCard, x: number, y: number) => {
|
||||
};
|
||||
const createDropPreview = () => {
|
||||
const div = document.createElement('div');
|
||||
div.style.height = '2px';
|
||||
div.style.borderRadius = '1px';
|
||||
div.dataset.isDropPreview = 'true';
|
||||
div.style.pointerEvents = 'none';
|
||||
div.style.position = 'fixed';
|
||||
div.style.zIndex = '9999';
|
||||
div.style.height = '3px';
|
||||
div.style.borderRadius = '2px';
|
||||
div.style.backgroundColor = 'var(--affine-primary-color)';
|
||||
div.style.boxShadow = '0px 0px 8px 0px rgba(30, 150, 235, 0.35)';
|
||||
return {
|
||||
@@ -219,19 +223,50 @@ const createDropPreview = () => {
|
||||
self: KanbanCard | undefined,
|
||||
card?: KanbanCard
|
||||
) {
|
||||
const target = card ?? group.querySelector('.add-card');
|
||||
if (!target) {
|
||||
console.error('`target` is not found');
|
||||
return;
|
||||
}
|
||||
if (target.previousElementSibling === self || target === self) {
|
||||
if (card === self) {
|
||||
div.remove();
|
||||
return;
|
||||
}
|
||||
if (target.previousElementSibling === div) {
|
||||
|
||||
if (!card) {
|
||||
const cards = Array.from(
|
||||
group.querySelectorAll('affine-data-view-kanban-card')
|
||||
);
|
||||
const lastCard = cards[cards.length - 1];
|
||||
if (lastCard === self) {
|
||||
div.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let rect: DOMRect | undefined;
|
||||
let y = 0;
|
||||
if (card) {
|
||||
rect = card.getBoundingClientRect();
|
||||
y = rect.top;
|
||||
} else {
|
||||
const addCard = group.querySelector('.add-card');
|
||||
if (addCard instanceof HTMLElement) {
|
||||
rect = addCard.getBoundingClientRect();
|
||||
y = rect.top;
|
||||
}
|
||||
}
|
||||
if (!rect) {
|
||||
const body = group.querySelector('.group-body');
|
||||
if (body instanceof HTMLElement) {
|
||||
rect = body.getBoundingClientRect();
|
||||
y = rect.bottom;
|
||||
}
|
||||
}
|
||||
if (!rect) {
|
||||
div.remove();
|
||||
return;
|
||||
}
|
||||
target.insertAdjacentElement('beforebegin', div);
|
||||
|
||||
document.body.append(div);
|
||||
div.style.left = `${Math.round(rect.left)}px`;
|
||||
div.style.top = `${Math.round(y - 2)}px`;
|
||||
div.style.width = `${Math.round(rect.width)}px`;
|
||||
},
|
||||
remove() {
|
||||
div.remove();
|
||||
|
||||
@@ -11,6 +11,7 @@ import { html } from 'lit/static-html.js';
|
||||
|
||||
import { groupTraitKey } from '../../../core/group-by/trait.js';
|
||||
import type { SingleView } from '../../../core/index.js';
|
||||
import { canGroupable } from '../group-by-utils.js';
|
||||
|
||||
const styles = css`
|
||||
affine-data-view-kanban-header {
|
||||
@@ -43,7 +44,12 @@ export class KanbanHeader extends SignalWatcher(
|
||||
popMenu(popupTargetFromElement(e.target as HTMLElement), {
|
||||
options: {
|
||||
items: this.view.properties$.value
|
||||
.filter(column => column.id !== groupTrait.property$.value?.id)
|
||||
.filter(column => {
|
||||
if (column.id === groupTrait.property$.value?.id) {
|
||||
return false;
|
||||
}
|
||||
return canGroupable(this.view.manager.dataSource, column.id);
|
||||
})
|
||||
.map(column => {
|
||||
return menu.action({
|
||||
name: column.name$.value,
|
||||
|
||||
@@ -26,5 +26,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -40,5 +40,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -41,5 +41,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -51,5 +51,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -47,5 +47,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -50,5 +50,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -44,5 +44,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -56,5 +56,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -41,5 +41,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -77,5 +77,5 @@
|
||||
"@types/pdfmake": "^0.2.12",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -40,5 +40,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -38,5 +38,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -37,5 +37,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -38,5 +38,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"dependencies": {
|
||||
"@blocksuite/affine": "workspace:*"
|
||||
},
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -64,5 +64,5 @@
|
||||
"devDependencies": {
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -47,5 +47,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -42,5 +42,5 @@
|
||||
"!dist/__tests__",
|
||||
"shim.d.ts"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"vite-plugin-wasm": "^3.5.0",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"vite-plugin-wasm": "^3.5.0",
|
||||
"vite-plugin-web-components-hmr": "^0.1.3"
|
||||
},
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -19,5 +19,5 @@
|
||||
],
|
||||
"ext": "ts,md,json"
|
||||
},
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@affine/monorepo",
|
||||
"version": "0.26.0",
|
||||
"version": "0.26.1",
|
||||
"private": true,
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@affine/server-native",
|
||||
"version": "0.26.0",
|
||||
"version": "0.26.1",
|
||||
"engines": {
|
||||
"node": ">= 10.16.0 < 11 || >= 11.8.0"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@affine/server",
|
||||
"private": true,
|
||||
"version": "0.26.0",
|
||||
"version": "0.26.1",
|
||||
"description": "Affine Node.js server",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
"@types/debug": "^4.1.12",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"version": "0.26.0"
|
||||
"version": "0.26.1"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user