import { ShadowlessElement, WithDisposable } from '@blockeditor/lit';
import { css, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { startDrag } from '../../../utils/drag.js';
import { getResultInRange } from '../../../utils/utils.js';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../consts.js';
import type { DataViewTableColumnManager } from '../../table-view-manager.js';
type GroupRectList = {
top: number;
bottom: number;
}[];
@customElement('data-view-table-vertical-indicator')
export class TableVerticalIndicator extends WithDisposable(ShadowlessElement) {
static override styles = css`
data-view-table-vertical-indicator {
position: fixed;
left: 0;
top: 0;
z-index: 1;
pointer-events: none;
}
.vertical-indicator-container {
position: absolute;
pointer-events: none;
}
.vertical-indicator-group {
position: absolute;
z-index: 1;
width: 100%;
background-color: var(--affine-hover-color);
pointer-events: none;
}
.vertical-indicator-group::after {
position: absolute;
z-index: 1;
width: 2px;
height: 100%;
content: '';
right: 0;
background-color: var(--affine-primary-color);
border-radius: 1px;
}
.with-shadow.vertical-indicator-group::after {
box-shadow: 0px 0px 8px 0px rgba(30, 150, 235, 0.35);
}
`;
@property({ attribute: false })
top!: number;
@property({ attribute: false })
left!: number;
@property({ attribute: false })
width!: number;
@property({ attribute: false })
lines!: GroupRectList;
@property({ attribute: false })
shadow = false;
protected override render(): unknown {
const containerStyle = styleMap({
top: `${this.top}px`,
left: `${this.left}px`,
width: `${Math.max(this.width, 1)}px`,
});
return html`
${repeat(this.lines, ({ top, bottom }) => {
const groupStyle = styleMap({
top: `${top}px`,
height: `${bottom - top}px`,
});
const groupClass = classMap({
'with-shadow': this.shadow,
'vertical-indicator-group': true,
});
return html`
`;
})}
`;
}
}
export const getTableGroupRects = (tableContainer: HTMLElement) => {
const tableRect = tableContainer.getBoundingClientRect();
const groups = tableContainer.querySelectorAll(
'affine-data-view-table-group'
);
return Array.from(groups).map(group => {
const groupRect = group.getBoundingClientRect();
const top =
group
.querySelector('affine-database-column-header')
?.getBoundingClientRect().top ?? groupRect.top;
const bottom =
group
.querySelector('.affine-database-block-rows')
?.getBoundingClientRect().bottom ?? groupRect.bottom;
return {
top: top - tableRect.top,
bottom: bottom - tableRect.top,
};
});
};
export const startDragWidthAdjustmentBar = (
evt: PointerEvent,
tableContainer: HTMLElement,
width: number,
column: DataViewTableColumnManager
) => {
const scale = column.width / width;
const tableRect = tableContainer.getBoundingClientRect();
const left =
tableContainer
.querySelector(
`affine-database-header-column[data-column-id='${column.id}']`
)
?.getBoundingClientRect().left ?? 0;
const rectList = getTableGroupRects(tableContainer);
const preview = getVerticalIndicator();
preview.display(column.width * scale, tableRect.top, rectList, left);
tableContainer.style.pointerEvents = 'none';
startDrag<{ width: number }>(evt, {
onDrag: () => ({ width: column.width }),
onMove: ({ x }) => {
const width = Math.round(
getResultInRange((x - left) / scale, DEFAULT_COLUMN_MIN_WIDTH, Infinity)
);
preview.display(width * scale, tableRect.top, rectList, left);
return {
width,
};
},
onDrop: ({ width }) => {
column.updateWidth(width);
},
onClear: () => {
tableContainer.style.pointerEvents = 'auto';
preview.remove();
},
});
};
let preview: VerticalIndicator | null = null;
type VerticalIndicator = {
display: (
width: number,
top: number,
lines: GroupRectList,
left: number,
shadow?: boolean
) => void;
remove: () => void;
};
export const getVerticalIndicator = (): VerticalIndicator => {
if (!preview) {
const dragBar = new TableVerticalIndicator();
dragBar.classList.add('blockeditor-overlay');
preview = {
display(
width: number,
top: number,
lines: GroupRectList,
left: number,
shadow = false
) {
document.body.append(dragBar);
dragBar.left = left;
dragBar.lines = lines;
dragBar.top = top;
dragBar.width = width;
dragBar.shadow = shadow;
},
remove() {
dragBar.remove();
},
};
}
return preview;
};