chore: merge blocksuite source code (#9213)

This commit is contained in:
Mirone
2024-12-20 15:38:06 +08:00
committed by GitHub
parent 2c9ef916f4
commit 30200ff86d
2031 changed files with 238888 additions and 229 deletions
@@ -0,0 +1,174 @@
import { ShadowlessElement } from '@blocksuite/block-std';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
import { computed, effect } from '@preact/signals-core';
import { css } from 'lit';
import { property, state } from 'lit/decorators.js';
import { createRef } from 'lit/directives/ref.js';
import {
type CellRenderProps,
type DataViewCellLifeCycle,
renderUniLit,
type SingleView,
} from '../../../core/index.js';
import type { TableColumn } from '../table-view-manager.js';
import { TableAreaSelection } from '../types.js';
export class MobileTableCell extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
static override styles = css`
mobile-table-cell {
display: flex;
align-items: start;
width: 100%;
height: 100%;
border: none;
outline: none;
}
mobile-table-cell * {
box-sizing: border-box;
}
mobile-table-cell uni-lit > *:first-child {
padding: 6px;
}
`;
private _cell = createRef<DataViewCellLifeCycle>();
@property({ attribute: false })
accessor column!: TableColumn;
@property({ attribute: false })
accessor rowId!: string;
cell$ = computed(() => {
return this.column.cellGet(this.rowId);
});
isEditing$ = computed(() => {
const selection = this.table?.props.selection$.value;
if (selection?.selectionType !== 'area') {
return false;
}
if (selection.groupKey !== this.groupKey) {
return false;
}
if (selection.focus.columnIndex !== this.columnIndex) {
return false;
}
if (selection.focus.rowIndex !== this.rowIndex) {
return false;
}
return selection.isEditing;
});
selectCurrentCell = (editing: boolean) => {
if (this.view.readonly$.value) {
return;
}
const setSelection = this.table?.props.setSelection;
const viewId = this.table?.props.view.id;
if (setSelection && viewId) {
if (editing && this.cell?.beforeEnterEditMode() === false) {
return;
}
setSelection({
viewId,
type: 'table',
...TableAreaSelection.create({
groupKey: this.groupKey,
focus: {
rowIndex: this.rowIndex,
columnIndex: this.columnIndex,
},
isEditing: editing,
}),
});
}
};
get cell(): DataViewCellLifeCycle | undefined {
return this._cell.value;
}
private get groupKey() {
return this.closest('mobile-table-group')?.group?.key;
}
private get readonly() {
return this.column.readonly$.value;
}
private get table() {
return this.closest('mobile-data-view-table');
}
override connectedCallback() {
super.connectedCallback();
if (this.column.readonly$.value) return;
this.disposables.add(
effect(() => {
const isEditing = this.isEditing$.value;
if (isEditing) {
this.isEditing = true;
this._cell.value?.onEnterEditMode();
} else {
this._cell.value?.onExitEditMode();
this.isEditing = false;
}
})
);
this.disposables.addFromEvent(this, 'click', () => {
if (!this.isEditing) {
this.selectCurrentCell(!this.column.readonly$.value);
}
});
}
override render() {
const renderer = this.column.renderer$.value;
if (!renderer) {
return;
}
const { edit, view } = renderer;
const uni = !this.readonly && this.isEditing && edit != null ? edit : view;
this.view.lockRows(this.isEditing);
this.dataset['editing'] = `${this.isEditing}`;
const props: CellRenderProps = {
cell: this.cell$.value,
isEditing: this.isEditing,
selectCurrentCell: this.selectCurrentCell,
};
return renderUniLit(uni, props, {
ref: this._cell,
style: {
display: 'contents',
},
});
}
@property({ attribute: false })
accessor columnId!: string;
@property({ attribute: false })
accessor columnIndex!: number;
@state()
accessor isEditing = false;
@property({ attribute: false })
accessor rowIndex!: number;
@property({ attribute: false })
accessor view!: SingleView;
}
declare global {
interface HTMLElementTagNameMap {
'mobile-table-cell': MobileTableCell;
}
}
@@ -0,0 +1,286 @@
import {
menu,
type MenuConfig,
popMenu,
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { ShadowlessElement } from '@blocksuite/block-std';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
import {
DeleteIcon,
DuplicateIcon,
InsertLeftIcon,
InsertRightIcon,
MoveLeftIcon,
MoveRightIcon,
ViewIcon,
} from '@blocksuite/icons/lit';
import { css } from 'lit';
import { property } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import { html } from 'lit/static-html.js';
import { inputConfig, typeConfig } from '../../../core/common/property-menu.js';
import type { Property } from '../../../core/view-manager/property.js';
import type { NumberPropertyDataType } from '../../../property-presets/index.js';
import { numberFormats } from '../../../property-presets/number/utils/formats.js';
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../consts.js';
import type { TableColumn, TableSingleView } from '../table-view-manager.js';
export class MobileTableColumnHeader extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
static override styles = css`
.mobile-table-column-header {
display: flex;
padding: 6px;
gap: 6px;
align-items: center;
}
.mobile-table-column-header-icon {
font-size: 18px;
color: ${unsafeCSSVarV2('database/textSecondary')};
display: flex;
align-items: center;
}
.mobile-table-column-header-name {
font-weight: 500;
font-size: 14px;
color: ${unsafeCSSVarV2('database/textSecondary')};
}
`;
private _clickColumn = () => {
if (this.tableViewManager.readonly$.value) {
return;
}
this.popMenu();
};
editTitle = () => {
this._clickColumn();
};
private popMenu(ele?: HTMLElement) {
const enableNumberFormatting =
this.tableViewManager.featureFlags$.value.enable_number_formatting;
popMenu(popupTargetFromElement(ele ?? this), {
options: {
title: {
text: 'Property settings',
},
items: [
inputConfig(this.column),
typeConfig(this.column),
// Number format begin
...(enableNumberFormatting
? [
menu.subMenu({
name: 'Number Format',
hide: () =>
!this.column.dataUpdate ||
this.column.type$.value !== 'number',
options: {
title: {
text: 'Number Format',
},
items: [
numberFormatConfig(this.column),
...numberFormats.map(format => {
const data = (
this.column as Property<
number,
NumberPropertyDataType
>
).data$.value;
return menu.action({
isSelected: data.format === format.type,
prefix: html`<span
style="font-size: var(--affine-font-base); scale: 1.2;"
>${format.symbol}</span
>`,
name: format.label,
select: () => {
if (data.format === format.type) return;
this.column.dataUpdate(() => ({
format: format.type,
}));
},
});
}),
],
},
}),
]
: []),
// Number format end
menu.group({
items: [
menu.action({
name: 'Hide In View',
prefix: ViewIcon(),
hide: () =>
this.column.hide$.value ||
this.column.type$.value === 'title',
select: () => {
this.column.hideSet(true);
},
}),
],
}),
menu.group({
items: [
menu.action({
name: 'Insert Left Column',
prefix: InsertLeftIcon(),
select: () => {
this.tableViewManager.propertyAdd({
id: this.column.id,
before: true,
});
Promise.resolve()
.then(() => {
const pre =
this.previousElementSibling?.previousElementSibling;
if (pre instanceof MobileTableColumnHeader) {
pre.editTitle();
pre.scrollIntoView({
inline: 'nearest',
block: 'nearest',
});
}
})
.catch(console.error);
},
}),
menu.action({
name: 'Insert Right Column',
prefix: InsertRightIcon(),
select: () => {
this.tableViewManager.propertyAdd({
id: this.column.id,
before: false,
});
Promise.resolve()
.then(() => {
const next = this.nextElementSibling?.nextElementSibling;
if (next instanceof MobileTableColumnHeader) {
next.editTitle();
next.scrollIntoView({
inline: 'nearest',
block: 'nearest',
});
}
})
.catch(console.error);
},
}),
menu.action({
name: 'Move Left',
prefix: MoveLeftIcon(),
hide: () => this.column.isFirst,
select: () => {
const preId = this.tableViewManager.propertyPreGet(
this.column.id
)?.id;
if (!preId) {
return;
}
this.tableViewManager.propertyMove(this.column.id, {
id: preId,
before: true,
});
},
}),
menu.action({
name: 'Move Right',
prefix: MoveRightIcon(),
hide: () => this.column.isLast,
select: () => {
const nextId = this.tableViewManager.propertyNextGet(
this.column.id
)?.id;
if (!nextId) {
return;
}
this.tableViewManager.propertyMove(this.column.id, {
id: nextId,
before: false,
});
},
}),
],
}),
menu.group({
items: [
menu.action({
name: 'Duplicate',
prefix: DuplicateIcon(),
hide: () =>
!this.column.duplicate || this.column.type$.value === 'title',
select: () => {
this.column.duplicate?.();
},
}),
menu.action({
name: 'Delete',
prefix: DeleteIcon(),
hide: () =>
!this.column.delete || this.column.type$.value === 'title',
select: () => {
this.column.delete?.();
},
class: {
'delete-item': true,
},
}),
],
}),
],
},
});
}
override render() {
const column = this.column;
const style = styleMap({
height: DEFAULT_COLUMN_TITLE_HEIGHT + 'px',
});
return html`
<div
style=${style}
class="mobile-table-column-header"
@click="${this._clickColumn}"
>
<uni-lit
class="mobile-table-column-header-icon"
.uni="${column.icon}"
></uni-lit>
<div class="mobile-table-column-header-name">${column.name$.value}</div>
</div>
`;
}
@property({ attribute: false })
accessor column!: TableColumn;
@property({ attribute: false })
accessor tableViewManager!: TableSingleView;
}
function numberFormatConfig(column: Property): MenuConfig {
return () =>
html` <affine-database-number-format-bar
.column="${column}"
></affine-database-number-format-bar>`;
}
declare global {
interface HTMLElementTagNameMap {
'mobile-table-column-header': MobileTableColumnHeader;
}
}
@@ -0,0 +1,200 @@
import {
menu,
popFilterableSimpleMenu,
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import { ShadowlessElement } from '@blocksuite/block-std';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
import { PlusIcon } from '@blocksuite/icons/lit';
import { css, html } from 'lit';
import { property, query } from 'lit/decorators.js';
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 { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
import type { DataViewTable } from '../pc/table-view.js';
import type { TableSingleView } from '../table-view-manager.js';
import { TableAreaSelection } from '../types.js';
const styles = css`
.data-view-table-group-add-row {
display: flex;
width: 100%;
height: 28px;
position: relative;
z-index: 0;
cursor: pointer;
transition: opacity 0.2s ease-in-out;
padding: 4px 8px;
border-bottom: 1px solid var(--affine-border-color);
}
.data-view-table-group-add-row-button {
position: sticky;
left: ${8 + LEFT_TOOL_BAR_WIDTH}px;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
user-select: none;
font-size: 12px;
line-height: 20px;
color: var(--affine-text-secondary-color);
}
`;
export class MobileTableGroup extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
static override styles = styles;
private clickAddRow = () => {
this.view.rowAdd('end', this.group?.key);
requestAnimationFrame(() => {
const selectionController = this.viewEle.selectionController;
const index = this.view.properties$.value.findIndex(
v => v.type$.value === 'title'
);
selectionController.selection = TableAreaSelection.create({
groupKey: this.group?.key,
focus: {
rowIndex: this.rows.length - 1,
columnIndex: index,
},
isEditing: true,
});
});
};
private clickAddRowInStart = () => {
this.view.rowAdd('start', this.group?.key);
requestAnimationFrame(() => {
const selectionController = this.viewEle.selectionController;
const index = this.view.properties$.value.findIndex(
v => v.type$.value === 'title'
);
selectionController.selection = TableAreaSelection.create({
groupKey: this.group?.key,
focus: {
rowIndex: 0,
columnIndex: index,
},
isEditing: true,
});
});
};
private clickGroupOptions = (e: MouseEvent) => {
const group = this.group;
if (!group) {
return;
}
const ele = e.currentTarget as HTMLElement;
popFilterableSimpleMenu(popupTargetFromElement(ele), [
menu.action({
name: 'Ungroup',
hide: () => group.value == null,
select: () => {
group.rows.forEach(id => {
group.manager.removeFromGroup(id, group.key);
});
},
}),
menu.action({
name: 'Delete Cards',
select: () => {
this.view.rowDelete(group.rows);
},
}),
]);
};
private renderGroupHeader = () => {
if (!this.group) {
return null;
}
return html`
<div
style="position: sticky;left: 0;width: max-content;padding: 6px 0;margin-bottom: 4px;display:flex;align-items:center;gap: 12px;max-width: 400px"
>
${GroupTitle(this.group, {
readonly: this.view.readonly$.value,
clickAdd: this.clickAddRowInStart,
clickOps: this.clickGroupOptions,
})}
</div>
`;
};
get rows() {
return this.group?.rows ?? this.view.rows$.value;
}
private renderRows(ids: string[]) {
return html`
<mobile-table-header
.renderGroupHeader="${this.renderGroupHeader}"
.tableViewManager="${this.view}"
></mobile-table-header>
<div class="mobile-affine-table-body">
${repeat(
ids,
id => id,
(id, idx) => {
return html` <mobile-table-row
data-row-index="${idx}"
data-row-id="${id}"
.dataViewEle="${this.dataViewEle}"
.view="${this.view}"
.rowId="${id}"
.rowIndex="${idx}"
></mobile-table-row>`;
}
)}
</div>
${this.view.readonly$.value
? null
: html` <div
class="data-view-table-group-add-row dv-hover"
@click="${this.clickAddRow}"
>
<div
class="data-view-table-group-add-row-button dv-icon-16"
data-test-id="affine-database-add-row-button"
role="button"
>
${PlusIcon()}<span style="font-size: 12px">New Record</span>
</div>
</div>`}
<affine-database-column-stats .view="${this.view}" .group="${this.group}">
</affine-database-column-stats>
`;
}
override render() {
return this.renderRows(this.rows);
}
@property({ attribute: false })
accessor dataViewEle!: DataViewRenderer;
@property({ attribute: false })
accessor group: GroupData | undefined = undefined;
@query('.affine-database-block-rows')
accessor rowsContainer: HTMLElement | null = null;
@property({ attribute: false })
accessor view!: TableSingleView;
@property({ attribute: false })
accessor viewEle!: DataViewTable;
}
declare global {
interface HTMLElementTagNameMap {
'mobile-table-group': MobileTableGroup;
}
}
@@ -0,0 +1,89 @@
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { ShadowlessElement } from '@blocksuite/block-std';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
import { PlusIcon } from '@blocksuite/icons/lit';
import { css, type TemplateResult } from 'lit';
import { property, query } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { html } from 'lit/static-html.js';
import type { TableSingleView } from '../table-view-manager.js';
export class MobileTableHeader extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
static override styles = css`
.mobile-table-add-column {
font-size: 18px;
color: ${unsafeCSSVarV2('icon/primary')};
margin-left: 8px;
display: flex;
align-items: center;
}
`;
private _onAddColumn = () => {
if (this.readonly) return;
this.tableViewManager.propertyAdd('end');
this.editLastColumnTitle();
};
editLastColumnTitle = () => {
const columns = this.querySelectorAll('mobile-table-column-header');
const column = columns.item(columns.length - 1);
column.editTitle();
};
private get readonly() {
return this.tableViewManager.readonly$.value;
}
override render() {
return html`
${this.renderGroupHeader?.()}
<div class="mobile-table-header mobile-table-row">
${repeat(
this.tableViewManager.properties$.value,
column => column.id,
(column, index) => {
const style = styleMap({
width: `${column.width$.value}px`,
border: index === 0 ? 'none' : undefined,
});
return html`
<mobile-table-column-header
style="${style}"
data-column-id="${column.id}"
data-column-index="${index}"
class="mobile-table-cell"
.column="${column}"
.tableViewManager="${this.tableViewManager}"
></mobile-table-column-header>
<div class="cell-divider" style="height: auto;"></div>
`;
}
)}
<div @click="${this._onAddColumn}" class="mobile-table-add-column">
${PlusIcon()}
</div>
<div class="scale-div" style="width: 1px;height: 1px;"></div>
</div>
`;
}
@property({ attribute: false })
accessor renderGroupHeader: (() => TemplateResult) | undefined;
@query('.scale-div')
accessor scaleDiv!: HTMLDivElement;
@property({ attribute: false })
accessor tableViewManager!: TableSingleView;
}
declare global {
interface HTMLElementTagNameMap {
'mobile-table-header': MobileTableHeader;
}
}
@@ -0,0 +1,46 @@
import {
menu,
popFilterableSimpleMenu,
type PopupTarget,
} from '@blocksuite/affine-components/context-menu';
import { DeleteIcon, ExpandFullIcon } from '@blocksuite/icons/lit';
import type { DataViewRenderer } from '../../../core/data-view.js';
import type { SingleView } from '../../../core/index.js';
export const popMobileRowMenu = (
target: PopupTarget,
rowId: string,
dataViewEle: DataViewRenderer,
view: SingleView
) => {
popFilterableSimpleMenu(target, [
menu.group({
items: [
menu.action({
name: 'Expand Row',
prefix: ExpandFullIcon(),
select: () => {
dataViewEle.openDetailPanel({
view: view,
rowId: rowId,
});
},
}),
],
}),
menu.group({
name: '',
items: [
menu.action({
name: 'Delete Row',
class: { 'delete-item': true },
prefix: DeleteIcon(),
select: () => {
view.rowDelete([rowId]);
},
}),
],
}),
]);
};
@@ -0,0 +1,148 @@
import { popupTargetFromElement } from '@blocksuite/affine-components/context-menu';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { ShadowlessElement } from '@blocksuite/block-std';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
import { CenterPeekIcon, MoreHorizontalIcon } from '@blocksuite/icons/lit';
import { css, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { html } from 'lit/static-html.js';
import type { DataViewRenderer } from '../../../core/data-view.js';
import type { TableSingleView } from '../table-view-manager.js';
import { popMobileRowMenu } from './menu.js';
export class MobileTableRow extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
static override styles = css`
.mobile-table-row {
width: 100%;
display: flex;
flex-direction: row;
border-bottom: 1px solid var(--affine-border-color);
position: relative;
min-height: 34px;
}
.mobile-row-ops {
position: relative;
width: 0;
margin-top: 5px;
height: max-content;
display: flex;
gap: 4px;
cursor: pointer;
justify-content: end;
right: 8px;
}
.affine-database-block-row:has([data-editing='true']) .mobile-row-ops {
visibility: hidden;
opacity: 0;
}
.mobile-row-op {
display: flex;
padding: 4px;
border-radius: 4px;
box-shadow: 0px 0px 4px 0px rgba(66, 65, 73, 0.14);
background-color: var(--affine-background-primary-color);
position: relative;
font-size: 16px;
color: ${unsafeCSSVarV2('icon/primary')};
}
`;
get groupKey() {
return this.closest('affine-data-view-table-group')?.group?.key;
}
override connectedCallback() {
super.connectedCallback();
this.classList.add('mobile-table-row');
}
protected override render(): unknown {
const view = this.view;
return html`
${repeat(
view.properties$.value,
v => v.id,
(column, i) => {
const clickDetail = () => {
this.dataViewEle.openDetailPanel({
view: this.view,
rowId: this.rowId,
});
};
const openMenu = (e: MouseEvent) => {
const ele = e.currentTarget as HTMLElement;
popMobileRowMenu(
popupTargetFromElement(ele),
this.rowId,
this.dataViewEle,
this.view
);
};
return html`
<div style="display: flex;">
<mobile-table-cell
class="mobile-table-cell"
style=${styleMap({
width: `${column.width$.value}px`,
border: i === 0 ? 'none' : undefined,
})}
.view="${view}"
.column="${column}"
.rowId="${this.rowId}"
data-row-id="${this.rowId}"
.rowIndex="${this.rowIndex}"
data-row-index="${this.rowIndex}"
.columnId="${column.id}"
data-column-id="${column.id}"
.columnIndex="${i}"
data-column-index="${i}"
>
</mobile-table-cell>
<div class="cell-divider"></div>
</div>
${!column.readonly$.value &&
column.view.mainProperties$.value.titleColumn === column.id
? html` <div class="mobile-row-ops">
<div class="mobile-row-op" @click="${clickDetail}">
${CenterPeekIcon()}
</div>
${!view.readonly$.value
? html` <div class="mobile-row-op" @click="${openMenu}">
${MoreHorizontalIcon()}
</div>`
: nothing}
</div>`
: nothing}
`;
}
)}
<div class="database-cell add-column-button"></div>
`;
}
@property({ attribute: false })
accessor dataViewEle!: DataViewRenderer;
@property({ attribute: false })
accessor rowId!: string;
@property({ attribute: false })
accessor rowIndex!: number;
@property({ attribute: false })
accessor view!: TableSingleView;
}
declare global {
interface HTMLElementTagNameMap {
'mobile-table-row': MobileTableRow;
}
}
@@ -0,0 +1,209 @@
import {
menu,
popMenu,
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { AddCursorIcon } from '@blocksuite/icons/lit';
import { css } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { html } from 'lit/static-html.js';
import type { GroupTrait } from '../../../core/group-by/trait.js';
import type { DataViewInstance } from '../../../core/index.js';
import { renderUniLit } from '../../../core/utils/uni-component/uni-component.js';
import { DataViewBase } from '../../../core/view/data-view-base.js';
import { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
import type { TableSingleView } from '../table-view-manager.js';
import type { TableViewSelectionWithType } from '../types.js';
export class MobileDataViewTable extends DataViewBase<
TableSingleView,
TableViewSelectionWithType
> {
static override styles = css`
.mobile-affine-database-table-wrapper {
position: relative;
width: 100%;
padding-bottom: 4px;
overflow-x: scroll;
overflow-y: hidden;
}
.mobile-affine-database-table-container {
position: relative;
width: fit-content;
min-width: 100%;
}
.cell-divider {
width: 1px;
height: 100%;
background-color: var(--affine-border-color);
}
`;
private _addRow = (
tableViewManager: TableSingleView,
position: InsertToPosition | number
) => {
if (this.readonly) return;
tableViewManager.rowAdd(position);
};
onWheel = (event: WheelEvent) => {
if (event.metaKey || event.ctrlKey) {
return;
}
const ele = event.currentTarget;
if (ele instanceof HTMLElement) {
if (ele.scrollWidth === ele.clientWidth) {
return;
}
event.stopPropagation();
}
};
renderAddGroup = (groupHelper: GroupTrait) => {
const addGroup = groupHelper.addGroup;
if (!addGroup) {
return;
}
const add = (e: MouseEvent) => {
const ele = e.currentTarget as HTMLElement;
popMenu(popupTargetFromElement(ele), {
options: {
items: [
menu.input({
onComplete: text => {
const column = groupHelper.property$.value;
if (column) {
column.dataUpdate(
() =>
addGroup({
text,
oldData: column.data$.value,
dataSource: this.props.view.manager.dataSource,
}) as never
);
}
},
}),
],
},
});
};
return html` <div style="display:flex;">
<div
class="dv-hover dv-round-8"
style="display:flex;align-items:center;gap: 10px;padding: 6px 12px 6px 8px;color: var(--affine-text-secondary-color);font-size: 12px;line-height: 20px;position: sticky;left: ${LEFT_TOOL_BAR_WIDTH}px;"
@click="${add}"
>
<div class="dv-icon-16" style="display:flex;">${AddCursorIcon()}</div>
<div>New Group</div>
</div>
</div>`;
};
get expose(): DataViewInstance {
return {
clearSelection: () => {},
addRow: position => {
this._addRow(this.props.view, position);
},
focusFirstCell: () => {},
showIndicator: _evt => {
return false;
},
hideIndicator: () => {
// this.dragController.dropPreview.remove();
},
moveTo: (_id, _evt) => {
// const result = this.dragController.getInsertPosition(evt);
// if (result) {
// this.props.view.rowMove(
// id,
// result.position,
// undefined,
// result.groupKey,
// );
// }
},
getSelection: () => {
throw new BlockSuiteError(
ErrorCode.DatabaseBlockError,
'Not implemented'
);
},
view: this.props.view,
eventTrace: this.props.eventTrace,
};
}
private get readonly() {
return this.props.view.readonly$.value;
}
private renderTable() {
const groups = this.props.view.groupTrait.groupsDataList$.value;
if (groups) {
return html`
<div style="display:flex;flex-direction: column;gap: 16px;">
${repeat(
groups,
v => v.key,
group => {
return html` <mobile-table-group
data-group-key="${group.key}"
.dataViewEle="${this.props.dataViewEle}"
.view="${this.props.view}"
.viewEle="${this}"
.group="${group}"
></mobile-table-group>`;
}
)}
${this.renderAddGroup(this.props.view.groupTrait)}
</div>
`;
}
return html` <mobile-table-group
.dataViewEle="${this.props.dataViewEle}"
.view="${this.props.view}"
.viewEle="${this}"
></mobile-table-group>`;
}
override render() {
const vPadding = this.props.virtualPadding$.value;
const wrapperStyle = styleMap({
marginLeft: `-${vPadding}px`,
marginRight: `-${vPadding}px`,
});
const containerStyle = styleMap({
paddingLeft: `${vPadding}px`,
paddingRight: `${vPadding}px`,
});
return html`
${renderUniLit(this.props.headerWidget, {
dataViewInstance: this.expose,
})}
<div class="mobile-affine-database-table-wrapper" style="${wrapperStyle}">
<div
class="mobile-affine-database-table-container"
style="${containerStyle}"
@wheel="${this.onWheel}"
>
${this.renderTable()}
</div>
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'mobile-data-view-table': MobileDataViewTable;
}
}