mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-22 08:47:10 +08:00
feat(editor): add grouping support for member property of the database block (#12243)
close: BS-3433 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced advanced group-by configurations for database blocks with user membership support. - Added a React hook for fetching and displaying user information in member-related components. - Enabled dynamic user and membership data types in database properties. - **Improvements** - Replaced context-based service access with a dependency injection system for shared services and state. - Enhanced type safety and consistency across group-by UI components and data handling. - Centralized group data management with a new Group class and refined group trait logic. - **Bug Fixes** - Improved reliability and consistency in retrieving and rendering user and group information. - **Style** - Removed obsolete member selection styles for cleaner UI code. - **Chores** - Registered external group-by configurations via dependency injection. - Refactored internal APIs for data sources, views, and group-by matchers to use service-based patterns. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
import { createContextKey } from '@blocksuite/data-view';
|
||||
import { createIdentifier } from '@blocksuite/global/di';
|
||||
import type { EditorHost } from '@blocksuite/std';
|
||||
|
||||
export const HostContextKey = createContextKey<EditorHost | undefined>(
|
||||
'editor-host',
|
||||
undefined
|
||||
);
|
||||
export const EditorHostKey = createIdentifier<EditorHost>('editor-host');
|
||||
|
||||
@@ -57,6 +57,9 @@ type SpacialProperty = {
|
||||
valueGet: (rowId: string, propertyId: string) => unknown;
|
||||
};
|
||||
export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
override get parentProvider() {
|
||||
return this._model.store.provider;
|
||||
}
|
||||
spacialProperties: Record<string, SpacialProperty> = {
|
||||
'created-time': {
|
||||
valueSet: () => {},
|
||||
@@ -186,9 +189,13 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
);
|
||||
});
|
||||
|
||||
constructor(model: DatabaseBlockModel) {
|
||||
constructor(
|
||||
model: DatabaseBlockModel,
|
||||
init?: (dataSource: DatabaseBlockDataSource) => void
|
||||
) {
|
||||
super();
|
||||
this._model = model;
|
||||
this._model = model; // ensure invariants first
|
||||
init?.(this); // then allow external initialisation
|
||||
}
|
||||
|
||||
private _runCapture() {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
type DataViewWidget,
|
||||
type DataViewWidgetProps,
|
||||
defineUniComponent,
|
||||
ExternalGroupByConfigProvider,
|
||||
renderUniLit,
|
||||
type SingleView,
|
||||
uniMap,
|
||||
@@ -47,7 +48,7 @@ import { css, html, nothing, unsafeCSS } from 'lit';
|
||||
|
||||
import { popSideDetail } from './components/layout.js';
|
||||
import { DatabaseConfigExtension } from './config.js';
|
||||
import { HostContextKey } from './context/host-context.js';
|
||||
import { EditorHostKey } from './context/host-context.js';
|
||||
import { DatabaseBlockDataSource } from './data-source.js';
|
||||
import { BlockRenderer } from './detail-panel/block-renderer.js';
|
||||
import { NoteRenderer } from './detail-panel/note-renderer.js';
|
||||
@@ -333,8 +334,17 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
||||
|
||||
get dataSource(): DatabaseBlockDataSource {
|
||||
if (!this._dataSource) {
|
||||
this._dataSource = new DatabaseBlockDataSource(this.model);
|
||||
this._dataSource.contextSet(HostContextKey, this.host);
|
||||
this._dataSource = new DatabaseBlockDataSource(this.model, dataSource => {
|
||||
dataSource.serviceSet(EditorHostKey, this.host);
|
||||
this.std.provider
|
||||
.getAll(ExternalGroupByConfigProvider)
|
||||
.forEach(config => {
|
||||
dataSource.serviceSet(
|
||||
ExternalGroupByConfigProvider(config.name),
|
||||
config
|
||||
);
|
||||
});
|
||||
});
|
||||
const id = currentViewStorage.getCurrentView(this.model.id);
|
||||
if (id && this.dataSource.viewManager.viewGet(id)) {
|
||||
this.dataSource.viewManager.setCurrentView(id);
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { DatabaseBlockAdapterExtensions } from './adapters/extension.js';
|
||||
import { databaseSlashMenuConfig } from './configs/slash-menu.js';
|
||||
|
||||
export const DatabaseBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:database'),
|
||||
BlockViewExtension('affine:database', literal`affine-database`),
|
||||
DatabaseBlockAdapterExtensions,
|
||||
SlashMenuConfigExtension('affine:database', databaseSlashMenuConfig),
|
||||
].flat();
|
||||
@@ -4,7 +4,6 @@ export * from './config';
|
||||
export * from './context';
|
||||
export * from './data-source';
|
||||
export * from './database-block';
|
||||
export * from './database-spec';
|
||||
export * from './detail-panel/block-renderer';
|
||||
export * from './detail-panel/note-renderer';
|
||||
export * from './properties';
|
||||
|
||||
@@ -15,7 +15,7 @@ import { computed } from '@preact/signals-core';
|
||||
import { html, nothing, type PropertyValues } from 'lit';
|
||||
import { createRef, ref } from 'lit/directives/ref.js';
|
||||
|
||||
import { HostContextKey } from '../../context/host-context.js';
|
||||
import { EditorHostKey } from '../../context/host-context.js';
|
||||
import {
|
||||
inlineLinkNodeStyle,
|
||||
linkCellStyle,
|
||||
@@ -88,7 +88,7 @@ export class LinkCell extends BaseCellRenderer<string, string> {
|
||||
};
|
||||
|
||||
get std() {
|
||||
const host = this.view.contextGet(HostContextKey);
|
||||
const host = this.view.serviceGet(EditorHostKey);
|
||||
return host?.std;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import { computed, effect, signal } from '@preact/signals-core';
|
||||
import { ref } from 'lit/directives/ref.js';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import { HostContextKey } from '../../context/host-context.js';
|
||||
import { EditorHostKey } from '../../context/host-context.js';
|
||||
import type { DatabaseBlockComponent } from '../../database-block.js';
|
||||
import {
|
||||
richTextCellStyle,
|
||||
@@ -87,7 +87,7 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
|
||||
|
||||
get inlineManager() {
|
||||
return this.view
|
||||
.contextGet(HostContextKey)
|
||||
.serviceGet(EditorHostKey)
|
||||
?.std.get(DefaultInlineManagerExtension.identifier);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
|
||||
}
|
||||
|
||||
get host() {
|
||||
return this.view.contextGet(HostContextKey);
|
||||
return this.view.serviceGet(EditorHostKey);
|
||||
}
|
||||
|
||||
private readonly richText$ = signal<RichText>();
|
||||
@@ -398,7 +398,7 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
|
||||
}
|
||||
|
||||
private get std() {
|
||||
return this.view.contextGet(HostContextKey)?.std;
|
||||
return this.view.serviceGet(EditorHostKey)?.std;
|
||||
}
|
||||
|
||||
insertDelta = (delta: DeltaInsert<AffineTextAttributes>) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Text } from '@blocksuite/store';
|
||||
import * as Y from 'yjs';
|
||||
import zod from 'zod';
|
||||
|
||||
import { HostContextKey } from '../../context/host-context.js';
|
||||
import { EditorHostKey } from '../../context/host-context.js';
|
||||
import { isLinkedDoc } from '../../utils/title-doc.js';
|
||||
|
||||
export const richTextColumnType = propertyType('rich-text');
|
||||
@@ -43,7 +43,7 @@ export const richTextPropertyModelConfig = richTextColumnType.modelConfig({
|
||||
},
|
||||
toJson: ({ value, dataSource }) => {
|
||||
if (!value) return null;
|
||||
const host = dataSource.contextGet(HostContextKey);
|
||||
const host = dataSource.serviceGet(EditorHostKey);
|
||||
if (host) {
|
||||
const collection = host.std.workspace;
|
||||
const yText = toYText(value);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Text } from '@blocksuite/store';
|
||||
import { Doc } from 'yjs';
|
||||
import zod from 'zod';
|
||||
|
||||
import { HostContextKey } from '../../context/host-context.js';
|
||||
import { EditorHostKey } from '../../context/host-context.js';
|
||||
import { isLinkedDoc } from '../../utils/title-doc.js';
|
||||
|
||||
export const titleColumnType = propertyType('title');
|
||||
@@ -28,7 +28,7 @@ export const titlePropertyModelConfig = titleColumnType.modelConfig({
|
||||
},
|
||||
toJson: ({ value, dataSource }) => {
|
||||
if (!value) return '';
|
||||
const host = dataSource.contextGet(HostContextKey);
|
||||
const host = dataSource.serviceGet(EditorHostKey);
|
||||
if (host) {
|
||||
const collection = host.std.workspace;
|
||||
const deltas = value.deltas$.value;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { property } from 'lit/decorators.js';
|
||||
import { createRef, ref } from 'lit/directives/ref.js';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import { HostContextKey } from '../../context/host-context.js';
|
||||
import { EditorHostKey } from '../../context/host-context.js';
|
||||
import type { DatabaseBlockComponent } from '../../database-block.js';
|
||||
import { getSingleDocIdFromText } from '../../utils/title-doc.js';
|
||||
import {
|
||||
@@ -32,7 +32,7 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
|
||||
docId$ = signal<string>();
|
||||
|
||||
get host() {
|
||||
return this.view.contextGet(HostContextKey);
|
||||
return this.view.serviceGet(EditorHostKey);
|
||||
}
|
||||
|
||||
get inlineEditor() {
|
||||
@@ -50,7 +50,7 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
|
||||
}
|
||||
|
||||
get std() {
|
||||
return this.view.contextGet(HostContextKey)?.std;
|
||||
return this.view.serviceGet(EditorHostKey)?.std;
|
||||
}
|
||||
|
||||
private readonly _onCopy = (e: ClipboardEvent) => {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { ColumnDataType } from '@blocksuite/affine-model';
|
||||
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
Container,
|
||||
createScope,
|
||||
type GeneralServiceIdentifier,
|
||||
type ServiceProvider,
|
||||
} from '@blocksuite/global/di';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
@@ -8,7 +14,6 @@ import type { DatabaseFlags } from '../types.js';
|
||||
import type { ViewConvertConfig } from '../view/convert.js';
|
||||
import type { DataViewDataType, ViewMeta } from '../view/data-view.js';
|
||||
import type { ViewManager } from '../view-manager/view-manager.js';
|
||||
import type { DataViewContextKey } from './context.js';
|
||||
|
||||
export interface DataSource {
|
||||
readonly$: ReadonlySignal<boolean>;
|
||||
@@ -65,7 +70,9 @@ export interface DataSource {
|
||||
propertyDelete(id: string): void;
|
||||
propertyCanDelete(propertyId: string): boolean;
|
||||
|
||||
contextGet<T>(key: DataViewContextKey<T>): T;
|
||||
provider: ServiceProvider;
|
||||
serviceGet<T>(key: GeneralServiceIdentifier<T>): T | null;
|
||||
serviceGetOrCreate<T>(key: GeneralServiceIdentifier<T>, create: () => T): T;
|
||||
|
||||
viewConverts: ViewConvertConfig[];
|
||||
viewManager: ViewManager;
|
||||
@@ -91,6 +98,8 @@ export interface DataSource {
|
||||
viewMetaGetById$(viewId: string): ReadonlySignal<ViewMeta | undefined>;
|
||||
}
|
||||
|
||||
export const DataSourceScope = createScope('data-source');
|
||||
|
||||
export abstract class DataSourceBase implements DataSource {
|
||||
propertyTypeCanSet(propertyId: string): boolean {
|
||||
return !this.isFixedProperty(propertyId);
|
||||
@@ -101,7 +110,9 @@ export abstract class DataSourceBase implements DataSource {
|
||||
propertyCanDelete(propertyId: string): boolean {
|
||||
return !this.isFixedProperty(propertyId);
|
||||
}
|
||||
context = new Map<symbol, unknown>();
|
||||
protected container = new Container();
|
||||
|
||||
abstract get parentProvider(): ServiceProvider;
|
||||
|
||||
abstract featureFlags$: ReadonlySignal<DatabaseFlags>;
|
||||
|
||||
@@ -144,12 +155,26 @@ export abstract class DataSourceBase implements DataSource {
|
||||
return computed(() => this.cellValueGet(rowId, propertyId));
|
||||
}
|
||||
|
||||
contextGet<T>(key: DataViewContextKey<T>): T {
|
||||
return (this.context.get(key.key) as T) ?? key.defaultValue;
|
||||
get provider() {
|
||||
return this.container.provider(DataSourceScope, this.parentProvider);
|
||||
}
|
||||
|
||||
contextSet<T>(key: DataViewContextKey<T>, value: T): void {
|
||||
this.context.set(key.key, value);
|
||||
serviceGet<T>(key: GeneralServiceIdentifier<T>): T | null {
|
||||
return this.provider.getOptional(key);
|
||||
}
|
||||
|
||||
serviceSet<T>(key: GeneralServiceIdentifier<T>, value: T): void {
|
||||
this.container.addValue(key, value, { scope: DataSourceScope });
|
||||
}
|
||||
|
||||
serviceGetOrCreate<T>(key: GeneralServiceIdentifier<T>, create: () => T): T {
|
||||
const result = this.serviceGet(key);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
const value = create();
|
||||
this.serviceSet(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
abstract propertyAdd(
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
export interface DataViewContextKey<T> {
|
||||
key: symbol;
|
||||
defaultValue: T;
|
||||
}
|
||||
|
||||
export const createContextKey = <T>(
|
||||
name: string,
|
||||
defaultValue: T
|
||||
): DataViewContextKey<T> => ({
|
||||
key: Symbol(name),
|
||||
defaultValue,
|
||||
});
|
||||
@@ -1,2 +1 @@
|
||||
export * from './base.js';
|
||||
export * from './context.js';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { GroupBy } from '../common/types.js';
|
||||
import type { DataSource } from '../data-source/index.js';
|
||||
import type { PropertyMetaConfig } from '../property/property-config.js';
|
||||
import { groupByMatcher } from './matcher.js';
|
||||
import { getGroupByService } from './matcher.js';
|
||||
|
||||
export const defaultGroupBy = (
|
||||
dataSource: DataSource,
|
||||
@@ -9,7 +9,8 @@ export const defaultGroupBy = (
|
||||
propertyId: string,
|
||||
data: NonNullable<unknown>
|
||||
): GroupBy | undefined => {
|
||||
const name = groupByMatcher.match(
|
||||
const groupByService = getGroupByService(dataSource);
|
||||
const name = groupByService?.matcher.match(
|
||||
propertyMeta.config.jsonValue.type({ data, dataSource })
|
||||
)?.name;
|
||||
return name != null
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import hash from '@emotion/hash';
|
||||
|
||||
import { MatcherCreator } from '../logical/matcher.js';
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
import { t } from '../logical/type-presets.js';
|
||||
import { createUniComponentFromWebComponent } from '../utils/uni-component/uni-component.js';
|
||||
import { BooleanGroupView } from './renderer/boolean-group.js';
|
||||
@@ -8,15 +8,23 @@ import { NumberGroupView } from './renderer/number-group.js';
|
||||
import { SelectGroupView } from './renderer/select-group.js';
|
||||
import { StringGroupView } from './renderer/string-group.js';
|
||||
import type { GroupByConfig } from './types.js';
|
||||
|
||||
const groupByMatcherCreator = new MatcherCreator<GroupByConfig>();
|
||||
const ungroups = {
|
||||
export const createGroupByConfig = <
|
||||
Data extends Record<string, unknown>,
|
||||
MatchType extends TypeInstance,
|
||||
GroupValue = unknown,
|
||||
>(
|
||||
config: GroupByConfig<Data, MatchType, GroupValue>
|
||||
): GroupByConfig => {
|
||||
return config as never as GroupByConfig;
|
||||
};
|
||||
export const ungroups = {
|
||||
key: 'Ungroups',
|
||||
value: null,
|
||||
};
|
||||
export const groupByMatchers = [
|
||||
groupByMatcherCreator.createMatcher(t.tag.instance(), {
|
||||
createGroupByConfig({
|
||||
name: 'select',
|
||||
matchType: t.tag.instance(),
|
||||
groupName: (type, value) => {
|
||||
if (t.tag.is(type) && type.data) {
|
||||
return type.data.find(v => v.id === value)?.value ?? '';
|
||||
@@ -48,11 +56,12 @@ export const groupByMatchers = [
|
||||
},
|
||||
view: createUniComponentFromWebComponent(SelectGroupView),
|
||||
}),
|
||||
groupByMatcherCreator.createMatcher(t.array.instance(t.tag.instance()), {
|
||||
createGroupByConfig({
|
||||
name: 'multi-select',
|
||||
groupName: (type, value) => {
|
||||
if (t.tag.is(type) && type.data) {
|
||||
return type.data.find(v => v.id === value)?.value ?? '';
|
||||
matchType: t.array.instance(t.tag.instance()),
|
||||
groupName: (type, value: string | null) => {
|
||||
if (t.array.is(type) && t.tag.is(type.element) && type.element.data) {
|
||||
return type.element.data.find(v => v.id === value)?.value ?? '';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
@@ -94,8 +103,9 @@ export const groupByMatchers = [
|
||||
},
|
||||
view: createUniComponentFromWebComponent(SelectGroupView),
|
||||
}),
|
||||
groupByMatcherCreator.createMatcher(t.string.instance(), {
|
||||
createGroupByConfig({
|
||||
name: 'text',
|
||||
matchType: t.string.instance(),
|
||||
groupName: (_type, value) => {
|
||||
return `${value ?? ''}`;
|
||||
},
|
||||
@@ -115,15 +125,16 @@ export const groupByMatchers = [
|
||||
},
|
||||
view: createUniComponentFromWebComponent(StringGroupView),
|
||||
}),
|
||||
groupByMatcherCreator.createMatcher(t.number.instance(), {
|
||||
createGroupByConfig({
|
||||
name: 'number',
|
||||
groupName: (_type, value) => {
|
||||
matchType: t.number.instance(),
|
||||
groupName: (_type, value: number | null) => {
|
||||
return `${value ?? ''}`;
|
||||
},
|
||||
defaultKeys: _type => {
|
||||
return [ungroups];
|
||||
},
|
||||
valuesGroup: (value, _type) => {
|
||||
valuesGroup: (value: number | null, _type) => {
|
||||
if (typeof value !== 'number') {
|
||||
return [ungroups];
|
||||
}
|
||||
@@ -137,8 +148,9 @@ export const groupByMatchers = [
|
||||
addToGroup: value => (typeof value === 'number' ? value * 10 : null),
|
||||
view: createUniComponentFromWebComponent(NumberGroupView),
|
||||
}),
|
||||
groupByMatcherCreator.createMatcher(t.boolean.instance(), {
|
||||
createGroupByConfig({
|
||||
name: 'boolean',
|
||||
matchType: t.boolean.instance(),
|
||||
groupName: (_type, value) => {
|
||||
return `${value?.toString() ?? ''}`;
|
||||
},
|
||||
|
||||
@@ -5,10 +5,10 @@ import { nothing } from 'lit';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import { renderUniLit } from '../utils/uni-component/uni-component.js';
|
||||
import type { GroupData } from './trait.js';
|
||||
import type { Group } from './trait.js';
|
||||
import type { GroupRenderProps } from './types.js';
|
||||
|
||||
function GroupHeaderCount(group: GroupData) {
|
||||
function GroupHeaderCount(group: Group) {
|
||||
const cards = group.rows;
|
||||
if (!cards.length) {
|
||||
return;
|
||||
@@ -16,32 +16,25 @@ function GroupHeaderCount(group: GroupData) {
|
||||
return html` <div class="group-header-count">${cards.length}</div>`;
|
||||
}
|
||||
const GroupTitleMobile = (
|
||||
groupData: GroupData,
|
||||
groupData: Group,
|
||||
ops: {
|
||||
readonly: boolean;
|
||||
clickAdd: (evt: MouseEvent) => void;
|
||||
clickOps: (evt: MouseEvent) => void;
|
||||
}
|
||||
) => {
|
||||
const data = groupData.manager.config$.value;
|
||||
if (!data) return nothing;
|
||||
const type = groupData.tType;
|
||||
if (!type) return nothing;
|
||||
|
||||
const icon =
|
||||
groupData.value == null
|
||||
? ''
|
||||
: html` <uni-lit
|
||||
class="group-header-icon"
|
||||
.uni="${groupData.manager.property$.value?.icon}"
|
||||
.uni="${groupData.property.icon}"
|
||||
></uni-lit>`;
|
||||
const props: GroupRenderProps = {
|
||||
value: groupData.value,
|
||||
data: groupData.property.data$.value,
|
||||
updateData: groupData.manager.updateData,
|
||||
updateValue: value =>
|
||||
groupData.manager.updateValue(
|
||||
groupData.rows.map(row => row.rowId),
|
||||
value
|
||||
),
|
||||
group: groupData,
|
||||
readonly: ops.readonly,
|
||||
};
|
||||
|
||||
@@ -103,7 +96,7 @@ const GroupTitleMobile = (
|
||||
<div
|
||||
style="display:flex;align-items:center;gap: 8px;overflow: hidden;height: 22px;"
|
||||
>
|
||||
${icon} ${renderUniLit(data.view, props)} ${columnName}
|
||||
${icon} ${renderUniLit(groupData.view, props)} ${columnName}
|
||||
${GroupHeaderCount(groupData)}
|
||||
</div>
|
||||
${ops.readonly
|
||||
@@ -120,7 +113,7 @@ const GroupTitleMobile = (
|
||||
};
|
||||
|
||||
export const GroupTitle = (
|
||||
groupData: GroupData,
|
||||
groupData: Group,
|
||||
ops: {
|
||||
readonly: boolean;
|
||||
clickAdd: (evt: MouseEvent) => void;
|
||||
@@ -130,25 +123,18 @@ export const GroupTitle = (
|
||||
if (IS_MOBILE) {
|
||||
return GroupTitleMobile(groupData, ops);
|
||||
}
|
||||
const data = groupData.manager.config$.value;
|
||||
if (!data) return nothing;
|
||||
const type = groupData.tType;
|
||||
if (!type) return nothing;
|
||||
|
||||
const icon =
|
||||
groupData.value == null
|
||||
? ''
|
||||
: html` <uni-lit
|
||||
class="group-header-icon"
|
||||
.uni="${groupData.manager.property$.value?.icon}"
|
||||
.uni="${groupData.property.icon}"
|
||||
></uni-lit>`;
|
||||
const props: GroupRenderProps = {
|
||||
value: groupData.value,
|
||||
data: groupData.property.data$.value,
|
||||
updateData: groupData.manager.updateData,
|
||||
updateValue: value =>
|
||||
groupData.manager.updateValue(
|
||||
groupData.rows.map(row => row.rowId),
|
||||
value
|
||||
),
|
||||
group: groupData,
|
||||
readonly: ops.readonly,
|
||||
};
|
||||
|
||||
@@ -228,7 +214,7 @@ export const GroupTitle = (
|
||||
<div
|
||||
style="display:flex;align-items:center;gap: 8px;overflow: hidden;height: 22px;"
|
||||
>
|
||||
${icon} ${renderUniLit(data.view, props)} ${columnName}
|
||||
${icon} ${renderUniLit(groupData.view, props)} ${columnName}
|
||||
${GroupHeaderCount(groupData)}
|
||||
</div>
|
||||
${ops.readonly
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export * from './define.js';
|
||||
export * from './matcher.js';
|
||||
export * from './trait.js';
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
import { Matcher } from '../logical/matcher.js';
|
||||
import { createIdentifier } from '@blocksuite/global/di';
|
||||
|
||||
import type { DataSource } from '../data-source/base.js';
|
||||
import { Matcher_ } from '../logical/matcher.js';
|
||||
import { groupByMatchers } from './define.js';
|
||||
import type { GroupByConfig } from './types.js';
|
||||
|
||||
export const groupByMatcher = new Matcher<GroupByConfig>(groupByMatchers);
|
||||
export const createGroupByMatcher = (list: GroupByConfig[]) => {
|
||||
return new Matcher_(list, v => v.matchType);
|
||||
};
|
||||
|
||||
export class GroupByService {
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
allExternalGroupByConfig(): GroupByConfig[] {
|
||||
return Array.from(
|
||||
this.dataSource.provider.getAll(ExternalGroupByConfigProvider).values()
|
||||
);
|
||||
}
|
||||
|
||||
get matcher() {
|
||||
return createGroupByMatcher([
|
||||
...this.allExternalGroupByConfig(),
|
||||
...groupByMatchers,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
export const GroupByProvider =
|
||||
createIdentifier<GroupByService>('group-by-service');
|
||||
|
||||
export const getGroupByService = (dataSource: DataSource) => {
|
||||
return dataSource.serviceGetOrCreate(
|
||||
GroupByProvider,
|
||||
() => new GroupByService(dataSource)
|
||||
);
|
||||
};
|
||||
|
||||
export const ExternalGroupByConfigProvider = createIdentifier<GroupByConfig>(
|
||||
'external-group-by-config'
|
||||
);
|
||||
|
||||
@@ -2,24 +2,39 @@ import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
||||
import { ShadowlessElement } from '@blocksuite/std';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import type { Group } from '../trait.js';
|
||||
import type { GroupRenderProps } from '../types.js';
|
||||
|
||||
export class BaseGroup<Data extends NonNullable<unknown>, Value>
|
||||
export class BaseGroup<JsonValue, Data extends Record<string, unknown>>
|
||||
extends SignalWatcher(WithDisposable(ShadowlessElement))
|
||||
implements GroupRenderProps<Data, Value>
|
||||
implements GroupRenderProps<JsonValue, Data>
|
||||
{
|
||||
@property({ attribute: false })
|
||||
accessor data!: Data;
|
||||
accessor group!: Group<unknown, JsonValue, Data>;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor readonly!: boolean;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor updateData: ((data: Data) => void) | undefined = undefined;
|
||||
updateData(data: Data) {
|
||||
this.group.manager.updateData(data);
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor updateValue: ((value: Value) => void) | undefined = undefined;
|
||||
updateValue(value: JsonValue) {
|
||||
this.group.manager.updateValue(
|
||||
this.group.rows.map(row => row.rowId),
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor value!: Value;
|
||||
get value(): JsonValue {
|
||||
return this.group.value as JsonValue;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this.group.tType;
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this.group.property.data$.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { css, html } from 'lit';
|
||||
|
||||
import { BaseGroup } from './base.js';
|
||||
|
||||
export class BooleanGroupView extends BaseGroup<NonNullable<unknown>, boolean> {
|
||||
export class BooleanGroupView extends BaseGroup<boolean, NonNullable<unknown>> {
|
||||
static override styles = css`
|
||||
.data-view-group-title-boolean-view {
|
||||
display: flex;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { css, html } from 'lit';
|
||||
|
||||
import { BaseGroup } from './base.js';
|
||||
|
||||
export class NumberGroupView extends BaseGroup<NonNullable<unknown>, number> {
|
||||
export class NumberGroupView extends BaseGroup<number, NonNullable<unknown>> {
|
||||
static override styles = css`
|
||||
.data-view-group-title-number-view {
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -12,10 +12,10 @@ import type { SelectTag } from '../../logical/index.js';
|
||||
import { BaseGroup } from './base.js';
|
||||
|
||||
export class SelectGroupView extends BaseGroup<
|
||||
string,
|
||||
{
|
||||
options: SelectTag[];
|
||||
},
|
||||
string
|
||||
}
|
||||
> {
|
||||
static override styles = css`
|
||||
data-view-group-title-select-view {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { css, html } from 'lit';
|
||||
|
||||
import { BaseGroup } from './base.js';
|
||||
|
||||
export class StringGroupView extends BaseGroup<NonNullable<unknown>, string> {
|
||||
export class StringGroupView extends BaseGroup<string, NonNullable<unknown>> {
|
||||
static override styles = css`
|
||||
.data-view-group-title-string-view {
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
sortable,
|
||||
} from '../utils/wc-dnd/sort/sort-context.js';
|
||||
import { verticalListSortingStrategy } from '../utils/wc-dnd/sort/strategies/index.js';
|
||||
import { groupByMatcher } from './matcher.js';
|
||||
import { getGroupByService } from './matcher.js';
|
||||
import type { GroupTrait } from './trait.js';
|
||||
import type { GroupRenderProps } from './types.js';
|
||||
|
||||
@@ -142,21 +142,22 @@ export class GroupSetting extends SignalWatcher(
|
||||
groups,
|
||||
group => group?.key ?? 'default key',
|
||||
group => {
|
||||
if (!group) return;
|
||||
const type = group.property.dataType$.value;
|
||||
if (!type) return;
|
||||
const props: GroupRenderProps = {
|
||||
value: group.value,
|
||||
data: group.property.data$.value,
|
||||
group,
|
||||
readonly: true,
|
||||
};
|
||||
const config = group.manager.config$.value;
|
||||
return html` <div
|
||||
${sortable(group.key)}
|
||||
${dragHandler(group.key)}
|
||||
class="dv-hover dv-round-4 group-item"
|
||||
>
|
||||
<div class="group-item-drag-bar"></div>
|
||||
<div style="padding: 0 4px;position:relative;">
|
||||
${renderUniLit(config?.view, props)}
|
||||
<div
|
||||
style="padding: 0 4px;position:relative;pointer-events: none"
|
||||
>
|
||||
${renderUniLit(group.view, props)}
|
||||
<div
|
||||
style="position:absolute;left: 0;top: 0;right: 0;bottom: 0;"
|
||||
></div>
|
||||
@@ -198,7 +199,8 @@ export const selectGroupByProperty = (
|
||||
if (!dataType) {
|
||||
return false;
|
||||
}
|
||||
return !!groupByMatcher.match(dataType);
|
||||
const groupByService = getGroupByService(view.manager.dataSource);
|
||||
return !!groupByService?.matcher.match(dataType);
|
||||
})
|
||||
.map<MenuConfig>(property => {
|
||||
return menu.action({
|
||||
|
||||
@@ -12,89 +12,112 @@ import type { Property } from '../view-manager/property.js';
|
||||
import type { Row } from '../view-manager/row.js';
|
||||
import type { SingleView } from '../view-manager/single-view.js';
|
||||
import { defaultGroupBy } from './default.js';
|
||||
import { groupByMatcher } from './matcher.js';
|
||||
export type GroupData = {
|
||||
manager: GroupTrait;
|
||||
property: Property;
|
||||
key: string;
|
||||
name: string;
|
||||
type: TypeInstance;
|
||||
value: unknown;
|
||||
rows: Row[];
|
||||
import { getGroupByService } from './matcher.js';
|
||||
import type { GroupByConfig } from './types.js';
|
||||
|
||||
export type GroupInfo<
|
||||
RawValue = unknown,
|
||||
JsonValue = unknown,
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> = {
|
||||
config: GroupByConfig;
|
||||
property: Property<RawValue, JsonValue, Data>;
|
||||
tType: TypeInstance;
|
||||
};
|
||||
|
||||
export class Group<
|
||||
RawValue = unknown,
|
||||
JsonValue = unknown,
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> {
|
||||
rows: Row[] = [];
|
||||
constructor(
|
||||
public readonly key: string,
|
||||
public readonly value: JsonValue,
|
||||
private readonly groupInfo: GroupInfo<RawValue, JsonValue, Data>,
|
||||
public readonly manager: GroupTrait
|
||||
) {}
|
||||
|
||||
get property() {
|
||||
return this.groupInfo.property;
|
||||
}
|
||||
|
||||
name$ = computed(() => {
|
||||
const type = this.property.dataType$.value;
|
||||
if (!type) {
|
||||
return '';
|
||||
}
|
||||
return this.groupInfo.config.groupName(type, this.value);
|
||||
});
|
||||
|
||||
private get config() {
|
||||
return this.groupInfo.config;
|
||||
}
|
||||
|
||||
get tType() {
|
||||
return this.groupInfo.tType;
|
||||
}
|
||||
get view() {
|
||||
return this.config.view;
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupTrait {
|
||||
config$ = computed(() => {
|
||||
groupInfo$ = computed<GroupInfo | undefined>(() => {
|
||||
const groupBy = this.groupBy$.value;
|
||||
if (!groupBy) {
|
||||
return;
|
||||
}
|
||||
const result = groupByMatcher.find(v => v.data.name === groupBy.name);
|
||||
const property = this.view.propertyGetOrCreate(groupBy.columnId);
|
||||
if (!property) {
|
||||
return;
|
||||
}
|
||||
const tType = property.dataType$.value;
|
||||
if (!tType) {
|
||||
return;
|
||||
}
|
||||
const groupByService = getGroupByService(this.view.manager.dataSource);
|
||||
const result = groupByService?.matcher.match(tType);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
return result.data;
|
||||
return {
|
||||
config: result,
|
||||
property,
|
||||
tType: tType,
|
||||
};
|
||||
});
|
||||
|
||||
property$ = computed(() => {
|
||||
const groupBy = this.groupBy$.value;
|
||||
if (!groupBy) {
|
||||
staticInfo$ = computed(() => {
|
||||
const groupInfo = this.groupInfo$.value;
|
||||
if (!groupInfo) {
|
||||
return;
|
||||
}
|
||||
return this.view.propertyGetOrCreate(groupBy.columnId);
|
||||
});
|
||||
|
||||
staticGroupDataMap$ = computed<
|
||||
Record<string, Omit<GroupData, 'rows'>> | undefined
|
||||
>(() => {
|
||||
const config = this.config$.value;
|
||||
const property = this.property$.value;
|
||||
const tType = property?.dataType$.value;
|
||||
if (!config || !tType || !property) {
|
||||
return;
|
||||
}
|
||||
return Object.fromEntries(
|
||||
config.defaultKeys(tType).map(({ key, value }) => [
|
||||
key,
|
||||
{
|
||||
key,
|
||||
property,
|
||||
name: config.groupName(tType, value),
|
||||
manager: this,
|
||||
type: tType,
|
||||
value,
|
||||
},
|
||||
])
|
||||
const staticMap = Object.fromEntries(
|
||||
groupInfo.config
|
||||
.defaultKeys(groupInfo.tType)
|
||||
.map(({ key, value }) => [key, new Group(key, value, groupInfo, this)])
|
||||
);
|
||||
return {
|
||||
staticMap,
|
||||
groupInfo,
|
||||
};
|
||||
});
|
||||
|
||||
groupDataMap$ = computed<Record<string, GroupData> | undefined>(() => {
|
||||
const staticGroupMap = this.staticGroupDataMap$.value;
|
||||
const config = this.config$.value;
|
||||
const groupBy = this.groupBy$.value;
|
||||
const property = this.property$.value;
|
||||
const tType = property?.dataType$.value;
|
||||
if (!staticGroupMap || !config || !groupBy || !tType || !property) {
|
||||
groupDataMap$ = computed(() => {
|
||||
const staticInfo = this.staticInfo$.value;
|
||||
if (!staticInfo) {
|
||||
return;
|
||||
}
|
||||
const groupMap: Record<string, GroupData> = Object.fromEntries(
|
||||
Object.entries(staticGroupMap).map(([k, v]) => [k, { ...v, rows: [] }])
|
||||
);
|
||||
const { staticMap, groupInfo } = staticInfo;
|
||||
const groupMap: Record<string, Group> = { ...staticMap };
|
||||
this.view.rows$.value.forEach(row => {
|
||||
const value = this.view.cellGetOrCreate(row.rowId, groupBy.columnId)
|
||||
const value = this.view.cellGetOrCreate(row.rowId, groupInfo.property.id)
|
||||
.jsonValue$.value;
|
||||
const keys = config.valuesGroup(value, tType);
|
||||
const keys = groupInfo.config.valuesGroup(value, groupInfo.tType);
|
||||
keys.forEach(({ key, value }) => {
|
||||
if (!groupMap[key]) {
|
||||
groupMap[key] = {
|
||||
key,
|
||||
property: property,
|
||||
name: config.groupName(tType, value),
|
||||
manager: this,
|
||||
value,
|
||||
rows: [],
|
||||
type: tType,
|
||||
};
|
||||
groupMap[key] = new Group(key, value, groupInfo, this);
|
||||
}
|
||||
groupMap[key].rows.push(row);
|
||||
});
|
||||
@@ -115,30 +138,30 @@ export class GroupTrait {
|
||||
});
|
||||
return sortedGroup
|
||||
.map(key => groupMap[key])
|
||||
.filter((v): v is GroupData => v != null);
|
||||
.filter((v): v is Group => v != null);
|
||||
}),
|
||||
this.view.isLocked$
|
||||
);
|
||||
|
||||
updateData = (data: NonNullable<unknown>) => {
|
||||
const propertyId = this.propertyId;
|
||||
if (!propertyId) {
|
||||
const property = this.property$.value;
|
||||
if (!property) {
|
||||
return;
|
||||
}
|
||||
this.view.propertyGetOrCreate(propertyId).dataUpdate(() => data);
|
||||
this.view.propertyGetOrCreate(property.id).dataUpdate(() => data);
|
||||
};
|
||||
|
||||
get addGroup() {
|
||||
const type = this.property$.value?.type$.value;
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
return this.view.manager.dataSource.propertyMetaGet(type)?.config.addGroup;
|
||||
return this.property$.value?.meta$.value?.config.addGroup;
|
||||
}
|
||||
|
||||
get propertyId() {
|
||||
return this.groupBy$.value?.columnId;
|
||||
}
|
||||
property$ = computed(() => {
|
||||
const groupInfo = this.groupInfo$.value;
|
||||
if (!groupInfo) {
|
||||
return;
|
||||
}
|
||||
return groupInfo.property;
|
||||
});
|
||||
|
||||
constructor(
|
||||
private readonly groupBy$: ReadonlySignal<GroupBy | undefined>,
|
||||
@@ -158,18 +181,20 @@ export class GroupTrait {
|
||||
|
||||
addToGroup(rowId: string, key: string) {
|
||||
const groupMap = this.groupDataMap$.value;
|
||||
const propertyId = this.propertyId;
|
||||
if (!groupMap || !propertyId) {
|
||||
const groupInfo = this.groupInfo$.value;
|
||||
if (!groupMap || !groupInfo) {
|
||||
return;
|
||||
}
|
||||
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
||||
const addTo = groupInfo.config.addToGroup ?? (value => value);
|
||||
const v = groupMap[key]?.value;
|
||||
if (v != null) {
|
||||
const newValue = addTo(
|
||||
v,
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value
|
||||
this.view.cellGetOrCreate(rowId, groupInfo.property.id).jsonValue$.value
|
||||
);
|
||||
this.view.cellGetOrCreate(rowId, propertyId).valueSet(newValue);
|
||||
this.view
|
||||
.cellGetOrCreate(rowId, groupInfo.property.id)
|
||||
.valueSet(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,11 +254,12 @@ export class GroupTrait {
|
||||
return;
|
||||
}
|
||||
if (fromGroupKey !== toGroupKey) {
|
||||
const propertyId = this.propertyId;
|
||||
const propertyId = this.property$.value?.id;
|
||||
if (!propertyId) {
|
||||
return;
|
||||
}
|
||||
const remove = this.config$.value?.removeFromGroup ?? (() => null);
|
||||
const remove =
|
||||
this.groupInfo$.value?.config.removeFromGroup ?? (() => null);
|
||||
const group = fromGroupKey != null ? groupMap[fromGroupKey] : undefined;
|
||||
let newValue: unknown = null;
|
||||
if (group) {
|
||||
@@ -242,7 +268,8 @@ export class GroupTrait {
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value
|
||||
);
|
||||
}
|
||||
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
||||
const addTo =
|
||||
this.groupInfo$.value?.config.addToGroup ?? (value => value);
|
||||
newValue = addTo(groupMap[toGroupKey]?.value ?? null, newValue);
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValueSet(newValue);
|
||||
}
|
||||
@@ -275,11 +302,12 @@ export class GroupTrait {
|
||||
if (!groupMap) {
|
||||
return;
|
||||
}
|
||||
const propertyId = this.propertyId;
|
||||
const propertyId = this.property$.value?.id;
|
||||
if (!propertyId) {
|
||||
return;
|
||||
}
|
||||
const remove = this.config$.value?.removeFromGroup ?? (() => undefined);
|
||||
const remove =
|
||||
this.groupInfo$.value?.config.removeFromGroup ?? (() => undefined);
|
||||
const newValue = remove(
|
||||
groupMap[key]?.value ?? null,
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value
|
||||
@@ -288,7 +316,7 @@ export class GroupTrait {
|
||||
}
|
||||
|
||||
updateValue(rows: string[], value: unknown) {
|
||||
const propertyId = this.propertyId;
|
||||
const propertyId = this.property$.value?.id;
|
||||
if (!propertyId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
import type { UniComponent } from '@blocksuite/affine-shared/types';
|
||||
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
import type { TypeInstance, ValueTypeOf } from '../logical/type.js';
|
||||
import type { Group } from './trait.js';
|
||||
export interface GroupRenderProps<
|
||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||
JsonValue = unknown,
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> {
|
||||
data: Data;
|
||||
updateData?: (data: Data) => void;
|
||||
value: JsonValue;
|
||||
updateValue?: (value: JsonValue) => void;
|
||||
group: Group<unknown, JsonValue, Data>;
|
||||
readonly: boolean;
|
||||
}
|
||||
|
||||
export type GroupByConfig<
|
||||
JsonValue = unknown,
|
||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||
MatchType extends TypeInstance = TypeInstance,
|
||||
GroupValue = unknown,
|
||||
> = {
|
||||
name: string;
|
||||
groupName: (type: TypeInstance, value: unknown) => string;
|
||||
defaultKeys: (type: TypeInstance) => {
|
||||
matchType: MatchType;
|
||||
groupName: (type: MatchType, value: GroupValue | null) => string;
|
||||
defaultKeys: (type: MatchType) => {
|
||||
key: string;
|
||||
value: JsonValue;
|
||||
value: GroupValue | null;
|
||||
}[];
|
||||
valuesGroup: (
|
||||
value: unknown,
|
||||
type: TypeInstance
|
||||
value: ValueTypeOf<MatchType> | null,
|
||||
type: MatchType
|
||||
) => {
|
||||
key: string;
|
||||
value: JsonValue;
|
||||
value: GroupValue | null;
|
||||
}[];
|
||||
addToGroup?: (value: JsonValue, oldValue: JsonValue) => JsonValue;
|
||||
removeFromGroup?: (value: JsonValue, oldValue: JsonValue) => JsonValue;
|
||||
view: UniComponent<GroupRenderProps<Data, JsonValue>>;
|
||||
addToGroup?: (
|
||||
value: GroupValue | null,
|
||||
oldValue: ValueTypeOf<MatchType> | null
|
||||
) => ValueTypeOf<MatchType> | null;
|
||||
removeFromGroup?: (
|
||||
value: GroupValue | null,
|
||||
oldValue: ValueTypeOf<MatchType> | null
|
||||
) => ValueTypeOf<MatchType> | null;
|
||||
view: UniComponent<GroupRenderProps<GroupValue | null, Data>>;
|
||||
};
|
||||
|
||||
@@ -68,3 +68,46 @@ export class Matcher<Data, Type extends TypeInstance = TypeInstance> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export class Matcher_<Value, Type extends TypeInstance> {
|
||||
constructor(
|
||||
private readonly list: Value[],
|
||||
private readonly getType: (value: Value) => Type,
|
||||
private readonly matchFunc: (
|
||||
type: Type,
|
||||
target: TypeInstance
|
||||
) => boolean = (type, target) => typeSystem.unify(target, type)
|
||||
) {}
|
||||
all(): Value[] {
|
||||
return this.list;
|
||||
}
|
||||
|
||||
allMatched(type: TypeInstance): Value[] {
|
||||
const result: Value[] = [];
|
||||
for (const t of this.list) {
|
||||
const tType = this.getType(t);
|
||||
if (this.matchFunc(tType, type)) {
|
||||
result.push(t);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
find(f: (data: Value) => boolean): Value | undefined {
|
||||
return this.list.find(f);
|
||||
}
|
||||
|
||||
isMatched(type: Type, target: TypeInstance) {
|
||||
return this.matchFunc(type, target);
|
||||
}
|
||||
|
||||
match(type: TypeInstance) {
|
||||
for (const t of this.list) {
|
||||
const tType = this.getType(t);
|
||||
if (this.matchFunc(tType, type)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import type {
|
||||
UserListService,
|
||||
UserService,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import * as zod from 'zod';
|
||||
import Zod from 'zod';
|
||||
|
||||
@@ -12,6 +16,11 @@ export const SelectTagSchema = Zod.object({
|
||||
color: Zod.string(),
|
||||
value: Zod.string(),
|
||||
});
|
||||
export const UserInfoSchema = Zod.object({
|
||||
userService: Zod.custom<UserService>(() => true),
|
||||
userListService: Zod.custom<UserListService>(() => true),
|
||||
});
|
||||
export type UserInfo = Zod.TypeOf<typeof UserInfoSchema>;
|
||||
export const unknown = defineDataType('Unknown', zod.never(), zod.unknown());
|
||||
export const dt = {
|
||||
number: defineDataType('Number', zod.number(), zod.number()),
|
||||
@@ -22,6 +31,7 @@ export const dt = {
|
||||
url: defineDataType('URL', zod.string(), zod.string()),
|
||||
image: defineDataType('Image', zod.string(), zod.string()),
|
||||
tag: defineDataType('Tag', zod.array(SelectTagSchema), zod.string()),
|
||||
user: defineDataType('User', UserInfoSchema, zod.string()),
|
||||
};
|
||||
export const t = {
|
||||
unknown,
|
||||
@@ -53,4 +63,5 @@ export const converts: TypeConvertConfig[] = [
|
||||
),
|
||||
createTypeConvert(t.richText.instance(), t.string.instance(), value => value),
|
||||
createTypeConvert(t.url.instance(), t.string.instance(), value => value),
|
||||
createTypeConvert(t.user.instance(), t.string.instance(), value => value),
|
||||
];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||
import type { GeneralServiceIdentifier } from '@blocksuite/global/di';
|
||||
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
|
||||
|
||||
import type { DataViewContextKey } from '../data-source/context.js';
|
||||
import type { Variable } from '../expression/types.js';
|
||||
import type { PropertyMetaConfig } from '../property/property-config.js';
|
||||
import type { TraitKey } from '../traits/key.js';
|
||||
@@ -61,7 +61,8 @@ export interface SingleView {
|
||||
type?: string
|
||||
): string | undefined;
|
||||
|
||||
contextGet<T>(key: DataViewContextKey<T>): T;
|
||||
serviceGet<T>(key: GeneralServiceIdentifier<T>): T | null;
|
||||
serviceGetOrCreate<T>(key: GeneralServiceIdentifier<T>, create: () => T): T;
|
||||
|
||||
traitGet<T>(key: TraitKey<T>): T | undefined;
|
||||
|
||||
@@ -201,8 +202,12 @@ export abstract class SingleViewBase<
|
||||
return new CellBase(this, propertyId, rowId);
|
||||
}
|
||||
|
||||
contextGet<T>(key: DataViewContextKey<T>): T {
|
||||
return this.dataSource.contextGet(key);
|
||||
serviceGet<T>(key: GeneralServiceIdentifier<T>): T | null {
|
||||
return this.dataSource.serviceGet(key);
|
||||
}
|
||||
|
||||
serviceGetOrCreate<T>(key: GeneralServiceIdentifier<T>, create: () => T): T {
|
||||
return this.dataSource.serviceGetOrCreate(key, create);
|
||||
}
|
||||
|
||||
dataUpdate(updater: (viewData: ViewData) => Partial<ViewData>): void {
|
||||
|
||||
@@ -2,7 +2,7 @@ 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, groupByMatcher, t } from '../../core/index.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 { KanbanSingleView } from './kanban-view-manager.js';
|
||||
@@ -34,10 +34,11 @@ 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 && !!groupByMatcher.match(dataType);
|
||||
return dataType && !!groupByService?.matcher.match(dataType);
|
||||
});
|
||||
const getWeight = (columnId: string) => {
|
||||
const dataType = viewManager.dataSource.propertyDataTypeGet(columnId);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { html } from 'lit/static-html.js';
|
||||
|
||||
import type { DataViewRenderer } from '../../../core/data-view.js';
|
||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Group } from '../../../core/group-by/trait.js';
|
||||
import { dragHandler } from '../../../core/utils/wc-dnd/dnd-context.js';
|
||||
import type { KanbanSingleView } from '../kanban-view-manager.js';
|
||||
|
||||
@@ -137,7 +137,7 @@ export class MobileKanbanGroup extends SignalWatcher(
|
||||
accessor dataViewEle!: DataViewRenderer;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group!: GroupData;
|
||||
accessor group!: Group;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor view!: KanbanSingleView;
|
||||
|
||||
@@ -55,7 +55,7 @@ export const popCardMenu = (
|
||||
})
|
||||
.map(group => {
|
||||
return menu.action({
|
||||
name: group.value != null ? group.name : 'Ungroup',
|
||||
name: group.value != null ? group.name$.value : 'Ungroup',
|
||||
select: () => {
|
||||
groupTrait.moveCardTo(
|
||||
cardId,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { html } from 'lit/static-html.js';
|
||||
|
||||
import type { DataViewRenderer } from '../../../core/data-view.js';
|
||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Group } from '../../../core/group-by/trait.js';
|
||||
import { dragHandler } from '../../../core/utils/wc-dnd/dnd-context.js';
|
||||
import type { KanbanSingleView } from '../kanban-view-manager.js';
|
||||
|
||||
@@ -201,7 +201,7 @@ export class KanbanGroup extends SignalWatcher(
|
||||
accessor dataViewEle!: DataViewRenderer;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group!: GroupData;
|
||||
accessor group!: Group;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor view!: KanbanSingleView;
|
||||
|
||||
@@ -60,7 +60,7 @@ export const popCardMenu = (
|
||||
})
|
||||
.map(group => {
|
||||
return menu.action({
|
||||
name: group.value != null ? group.name : 'Ungroup',
|
||||
name: group.value != null ? group.name$.value : 'Ungroup',
|
||||
select: () => {
|
||||
selection.moveCard(rowId, group.key);
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { DataViewRenderer } from '../../../core/data-view.js';
|
||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Group } from '../../../core/group-by/trait.js';
|
||||
import type { Row } from '../../../core/index.js';
|
||||
import { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
|
||||
import type { DataViewTable } from '../pc/table-view.js';
|
||||
@@ -185,7 +185,7 @@ export class MobileTableGroup extends SignalWatcher(
|
||||
accessor dataViewEle!: DataViewRenderer;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
|
||||
@query('.affine-database-block-rows')
|
||||
accessor rowsContainer: HTMLElement | null = null;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { css, html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { GroupData } from '../../../../../../core/group-by/trait';
|
||||
import type { Group } from '../../../../../../core/group-by/trait';
|
||||
import { LEFT_TOOL_BAR_WIDTH, STATS_BAR_HEIGHT } from '../../../../consts';
|
||||
import type { TableSingleView } from '../../../../table-view-manager';
|
||||
|
||||
@@ -38,7 +38,7 @@ export class VirtualDataBaseColumnStats extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor view!: TableSingleView;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { typeSystem } from '../../../../../../core';
|
||||
import type { GroupData } from '../../../../../../core/group-by/trait';
|
||||
import type { Group } from '../../../../../../core/group-by/trait';
|
||||
import { statsFunctions } from '../../../../../../core/statistics';
|
||||
import type { StatisticsConfig } from '../../../../../../core/statistics/types';
|
||||
import type { TableProperty } from '../../../../table-view-manager';
|
||||
@@ -236,7 +236,7 @@ export class VirtualDatabaseColumnStatsCell extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { nothing } from 'lit';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import {
|
||||
type GroupData,
|
||||
type Group,
|
||||
type GroupRenderProps,
|
||||
renderUniLit,
|
||||
} from '../../../../../core';
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
show,
|
||||
} from './group-title-css';
|
||||
|
||||
function GroupHeaderCount(group: GroupData) {
|
||||
function GroupHeaderCount(group: Group) {
|
||||
const cards = group.rows;
|
||||
if (!cards.length) {
|
||||
return;
|
||||
@@ -27,7 +27,7 @@ function GroupHeaderCount(group: GroupData) {
|
||||
}
|
||||
|
||||
export const GroupTitle = (
|
||||
groupData: GroupData,
|
||||
groupData: Group,
|
||||
ops: {
|
||||
groupHover: boolean;
|
||||
readonly: boolean;
|
||||
@@ -35,24 +35,20 @@ export const GroupTitle = (
|
||||
clickOps: (evt: MouseEvent) => void;
|
||||
}
|
||||
) => {
|
||||
const data = groupData.manager.config$.value;
|
||||
if (!data) return nothing;
|
||||
const view = groupData.view;
|
||||
const type = groupData.property.dataType$.value;
|
||||
if (!view || !type) {
|
||||
return nothing;
|
||||
}
|
||||
const icon =
|
||||
groupData.value == null
|
||||
? ''
|
||||
: html` <uni-lit
|
||||
class="${groupHeaderIcon}"
|
||||
.uni="${groupData.manager.property$.value?.icon}"
|
||||
.uni="${groupData.property.icon}"
|
||||
></uni-lit>`;
|
||||
const props: GroupRenderProps = {
|
||||
value: groupData.value,
|
||||
data: groupData.property.data$.value,
|
||||
updateData: groupData.manager.updateData,
|
||||
updateValue: value =>
|
||||
groupData.manager.updateValue(
|
||||
groupData.rows.map(row => row.rowId),
|
||||
value
|
||||
),
|
||||
group: groupData,
|
||||
readonly: ops.readonly,
|
||||
};
|
||||
|
||||
@@ -65,7 +61,7 @@ export const GroupTitle = (
|
||||
const opsClass = clsx(ops.groupHover && show, groupHeaderOps);
|
||||
return html`
|
||||
<div class="${groupTitleRow}">
|
||||
${icon} ${renderUniLit(data.view, props)} ${columnName}
|
||||
${icon} ${renderUniLit(view, props)} ${columnName}
|
||||
${GroupHeaderCount(groupData)}
|
||||
</div>
|
||||
${!ops.readonly
|
||||
|
||||
@@ -7,7 +7,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import type { GroupData } from '../../../../../../core/group-by/trait';
|
||||
import type { Group } from '../../../../../../core/group-by/trait';
|
||||
import type { Row } from '../../../../../../core/view-manager/row';
|
||||
import type {
|
||||
TableProperty,
|
||||
@@ -84,7 +84,7 @@ export class DataViewColumnPreview extends SignalWatcher(
|
||||
accessor container!: HTMLElement;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -40,7 +40,10 @@ import {
|
||||
} from '../../../../../../core/utils/wc-dnd/dnd-context';
|
||||
import type { Property } from '../../../../../../core/view-manager/property';
|
||||
import { numberFormats } from '../../../../../../property-presets/number/utils/formats';
|
||||
import { ShowQuickSettingBarContextKey } from '../../../../../../widget-presets/quick-setting-bar/context';
|
||||
import {
|
||||
createDefaultShowQuickSettingBar,
|
||||
ShowQuickSettingBarKey,
|
||||
} from '../../../../../../widget-presets/quick-setting-bar/context';
|
||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../../../consts';
|
||||
import type {
|
||||
TableProperty,
|
||||
@@ -193,7 +196,10 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
}
|
||||
|
||||
private _toggleQuickSettingBar(show = true) {
|
||||
const map = this.tableViewManager.contextGet(ShowQuickSettingBarContextKey);
|
||||
const map = this.tableViewManager.serviceGetOrCreate(
|
||||
ShowQuickSettingBarKey,
|
||||
createDefaultShowQuickSettingBar
|
||||
);
|
||||
map.value = {
|
||||
...map.value,
|
||||
[this.tableViewManager.id]: show,
|
||||
|
||||
@@ -14,7 +14,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { DataViewRenderer } from '../../../core/data-view.js';
|
||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Group } from '../../../core/group-by/trait.js';
|
||||
import type { Row } from '../../../core/index.js';
|
||||
import { createDndContext } from '../../../core/utils/wc-dnd/dnd-context.js';
|
||||
import { defaultActivators } from '../../../core/utils/wc-dnd/sensors/index.js';
|
||||
@@ -150,7 +150,7 @@ export class TableGroup extends SignalWatcher(
|
||||
};
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor view!: TableSingleView;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import type { GroupData } from '../../../../core/group-by/trait.js';
|
||||
import type { Group } from '../../../../core/group-by/trait.js';
|
||||
import type { Row } from '../../../../core/index.js';
|
||||
import type {
|
||||
TableProperty,
|
||||
@@ -84,7 +84,7 @@ export class DataViewColumnPreview extends SignalWatcher(
|
||||
accessor container!: HTMLElement;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -40,7 +40,10 @@ import {
|
||||
} from '../../../../core/utils/wc-dnd/dnd-context.js';
|
||||
import type { Property } from '../../../../core/view-manager/property.js';
|
||||
import { numberFormats } from '../../../../property-presets/number/utils/formats.js';
|
||||
import { ShowQuickSettingBarContextKey } from '../../../../widget-presets/quick-setting-bar/context.js';
|
||||
import {
|
||||
createDefaultShowQuickSettingBar,
|
||||
ShowQuickSettingBarKey,
|
||||
} from '../../../../widget-presets/quick-setting-bar/context.js';
|
||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../consts.js';
|
||||
import type {
|
||||
TableProperty,
|
||||
@@ -193,7 +196,10 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
}
|
||||
|
||||
private _toggleQuickSettingBar(show = true) {
|
||||
const map = this.tableViewManager.contextGet(ShowQuickSettingBarContextKey);
|
||||
const map = this.tableViewManager.serviceGetOrCreate(
|
||||
ShowQuickSettingBarKey,
|
||||
createDefaultShowQuickSettingBar
|
||||
);
|
||||
map.value = {
|
||||
...map.value,
|
||||
[this.tableViewManager.id]: show,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { css, html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Group } from '../../../core/group-by/trait.js';
|
||||
import { LEFT_TOOL_BAR_WIDTH, STATS_BAR_HEIGHT } from '../consts.js';
|
||||
import type { TableSingleView } from '../table-view-manager.js';
|
||||
|
||||
@@ -38,7 +38,7 @@ export class DataBaseColumnStats extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor view!: TableSingleView;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { css, html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Group } from '../../../core/group-by/trait.js';
|
||||
import { typeSystem } from '../../../core/index.js';
|
||||
import { statsFunctions } from '../../../core/statistics/index.js';
|
||||
import type { StatisticsConfig } from '../../../core/statistics/types.js';
|
||||
@@ -236,7 +236,7 @@ export class DatabaseColumnStatsCell extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor group: GroupData | undefined = undefined;
|
||||
accessor group: Group | undefined = undefined;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { createIdentifier } from '@blocksuite/global/di';
|
||||
import { type Signal, signal } from '@preact/signals-core';
|
||||
|
||||
import { createContextKey } from '../../core/index.js';
|
||||
|
||||
export const ShowQuickSettingBarContextKey = createContextKey<
|
||||
export const ShowQuickSettingBarKey = createIdentifier<
|
||||
Signal<Record<string, boolean>>
|
||||
>('show-quick-setting-bar', signal({}));
|
||||
>('show-quick-setting-bar');
|
||||
|
||||
export const createDefaultShowQuickSettingBar = () => {
|
||||
return signal<Record<string, boolean>>({});
|
||||
};
|
||||
|
||||
@@ -6,7 +6,10 @@ import {
|
||||
type DataViewWidgetProps,
|
||||
defineUniComponent,
|
||||
} from '../../core/index.js';
|
||||
import { ShowQuickSettingBarContextKey } from './context.js';
|
||||
import {
|
||||
createDefaultShowQuickSettingBar,
|
||||
ShowQuickSettingBarKey,
|
||||
} from './context.js';
|
||||
import { renderFilterBar } from './filter/index.js';
|
||||
import { renderSortBar } from './sort/index.js';
|
||||
|
||||
@@ -17,7 +20,12 @@ export const widgetQuickSettingBar = defineUniComponent(
|
||||
Boolean
|
||||
);
|
||||
if (!IS_MOBILE) {
|
||||
if (!view.contextGet(ShowQuickSettingBarContextKey).value[view.id]) {
|
||||
if (
|
||||
!view.serviceGetOrCreate(
|
||||
ShowQuickSettingBarKey,
|
||||
createDefaultShowQuickSettingBar
|
||||
).value[view.id]
|
||||
) {
|
||||
return html``;
|
||||
}
|
||||
if (!barList.length) {
|
||||
|
||||
@@ -11,7 +11,10 @@ import { filterTraitKey } from '../../../../core/filter/trait.js';
|
||||
import type { FilterGroup } from '../../../../core/filter/types.js';
|
||||
import { emptyFilterGroup } from '../../../../core/filter/utils.js';
|
||||
import { WidgetBase } from '../../../../core/widget/widget-base.js';
|
||||
import { ShowQuickSettingBarContextKey } from '../../../quick-setting-bar/context.js';
|
||||
import {
|
||||
createDefaultShowQuickSettingBar,
|
||||
ShowQuickSettingBarKey,
|
||||
} from '../../../quick-setting-bar/context.js';
|
||||
|
||||
const styles = css`
|
||||
.affine-database-filter-button {
|
||||
@@ -100,7 +103,10 @@ export class DataViewHeaderToolsFilter extends WidgetBase {
|
||||
}
|
||||
|
||||
toggleShowFilter(show?: boolean) {
|
||||
const map = this.view.contextGet(ShowQuickSettingBarContextKey);
|
||||
const map = this.view.serviceGetOrCreate(
|
||||
ShowQuickSettingBarKey,
|
||||
createDefaultShowQuickSettingBar
|
||||
);
|
||||
map.value = {
|
||||
...map.value,
|
||||
[this.view.id]: show ?? !map.value[this.view.id],
|
||||
|
||||
@@ -10,7 +10,10 @@ import { popCreateSort } from '../../../../core/sort/add-sort.js';
|
||||
import { sortTraitKey } from '../../../../core/sort/manager.js';
|
||||
import { createSortUtils } from '../../../../core/sort/utils.js';
|
||||
import { WidgetBase } from '../../../../core/widget/widget-base.js';
|
||||
import { ShowQuickSettingBarContextKey } from '../../../quick-setting-bar/context.js';
|
||||
import {
|
||||
createDefaultShowQuickSettingBar,
|
||||
ShowQuickSettingBarKey,
|
||||
} from '../../../quick-setting-bar/context.js';
|
||||
import { popSortRoot } from '../../../quick-setting-bar/sort/root-panel.js';
|
||||
|
||||
const styles = css`
|
||||
@@ -106,7 +109,10 @@ export class DataViewHeaderToolsSort extends WidgetBase {
|
||||
}
|
||||
|
||||
toggleShowQuickSettingBar(show?: boolean) {
|
||||
const map = this.view.contextGet(ShowQuickSettingBarContextKey);
|
||||
const map = this.view.serviceGetOrCreate(
|
||||
ShowQuickSettingBarKey,
|
||||
createDefaultShowQuickSettingBar
|
||||
);
|
||||
map.value = {
|
||||
...map.value,
|
||||
[this.view.id]: show ?? !map.value[this.view.id],
|
||||
|
||||
Reference in New Issue
Block a user