diff --git a/packages/duoyun-ui/src/elements/contextmenu.ts b/packages/duoyun-ui/src/elements/contextmenu.ts index d91f3b95..b5d827c5 100644 --- a/packages/duoyun-ui/src/elements/contextmenu.ts +++ b/packages/duoyun-ui/src/elements/contextmenu.ts @@ -25,7 +25,7 @@ type MenuOptions = { searchable?: boolean; }; type MenuObject = MenuOptions & { menu: Menu }; -type MenuOrMenuObject = Menu | MenuObject; +export type MenuOrMenuObject = Menu | MenuObject; export interface ContextMenuItem { text: string | TemplateResult; diff --git a/packages/duoyun-ui/src/elements/table.ts b/packages/duoyun-ui/src/elements/table.ts index ae95c7ec..a87459dd 100644 --- a/packages/duoyun-ui/src/elements/table.ts +++ b/packages/duoyun-ui/src/elements/table.ts @@ -20,7 +20,7 @@ import { icons } from '../lib/icons'; import { commonHandle } from '../lib/hotkeys'; import { focusStyle } from '../lib/styles'; -import { ContextMenuItem, ContextMenu } from './contextmenu'; +import { ContextMenu, MenuOrMenuObject } from './contextmenu'; import { DuoyunScrollBoxElement } from './scroll-box'; import type { SelectionChange } from './selection-box'; @@ -136,7 +136,6 @@ export type Column = { tooltip?: string; dataIndex?: keyof T | string[]; render?: (record: T) => string | TemplateResult; - getActions?: (record: T, evt: HTMLElement) => ContextMenuItem[]; getColSpan?: (record: T, arr: T[]) => number; getRowSpan?: (record: T, arr: T[]) => number; data?: Record; @@ -183,6 +182,7 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement @property rowKey?: string | string[]; @property getKey?: (record: T) => K; @property expandedRowRender?: (record: T) => undefined | string | TemplateResult; + @property getActions?: (record: T, activeElement: HTMLElement) => MenuOrMenuObject; @emitter expand: Emitter; #selectionSet = new Set(); @@ -245,12 +245,9 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement }); }; - #openActions = (evt: PointerEvent, menu: ContextMenuItem[]) => { - ContextMenu.open(menu, { - activeElement: evt.target as HTMLElement, - searchable: menu.length > 20, - maxHeight: '30em', - }); + #openActions = (evt: PointerEvent, record: T) => { + const activeElement = evt.target as HTMLElement; + ContextMenu.open(this.getActions!(record, activeElement), { activeElement }); }; #shouldRenderTd = (spanMemo: number[], index: number) => { @@ -297,10 +294,94 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement `, }; + #actionsColumn: Column = { + title: '', + width: this.#iconColWidth, + render: (record) => html` + this.#openActions(evt, record)} + > + `, + }; + #getDefaultStyle = (width?: string): StyleObject => { return width?.startsWith('0') ? { fontSize: '0' } : {}; }; + #columns?: Column[]; + #sidePart?: TemplateResult; + #headerPart?: TemplateResult; + willMount = () => { + this.memo( + () => { + this.#columns = this.expandedRowRender && this.columns ? [this.#expandedColumn, ...this.columns] : this.columns; + if (!this.#columns) return; + if (this.getActions) this.#columns.push(this.#actionsColumn); + + this.#headerPart = html` + + ${this.#columns.map(({ width = 'auto' }) => html` `)} + + ${this.headless + ? '' + : html` + + + ${this.#columns.map( + ({ title = '', width, style = this.#getDefaultStyle(width), tooltip }) => html` + + + ${title} + ${tooltip + ? html` + + + + ` + : ''} + + + `, + )} + + + `} + `; + + let sum = this.#columns + .filter(({ visibleWidth }) => visibleWidth !== 'auto') + .map(({ width }) => width || '15em') + .join(' + '); + + this.#sidePart = html` + ${this.#columns.map(({ visibleWidth, width }, index) => { + if (!visibleWidth) return ''; + + sum = `${sum} + ${width}`; + // `visibility: collapse;` 不完美 + return html` + + `; + })} + `; + }, + () => [this.columns, this.headless, this.getActions, this.expandedRowRender], + ); + }; + mounted = () => { this.memo( () => { @@ -311,28 +392,11 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement }; render = () => { - if (!this.columns) return html``; - const columns = this.expandedRowRender ? [this.#expandedColumn, ...this.columns] : this.columns; + const columns = this.#columns; + if (!columns) return html``; + const rowSpanMemo = columns.map(() => 0); return html` - ${this.selectable - ? html`` - : ''} - ${columns.map(({ visibleWidth }, index) => - // `visibility: collapse;` 不完美 - visibleWidth - ? html` - - ` - : '', - )} ${this.caption ? html` @@ -341,36 +405,7 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement ` : ''} - - ${columns.map( - ({ width = 'auto', getActions }) => - html``, - )} - - ${this.headless - ? '' - : html` - - - ${columns.map( - ({ title = '', width, style = this.#getDefaultStyle(width), tooltip }) => html` - - `, - )} - - - `} + ${this.#headerPart} ${this.data?.map( (record, _rowIndex, _data, colSpanMemo = [0]) => html` @@ -386,7 +421,6 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement { dataIndex, render, - getActions, width, style = this.#getDefaultStyle(width), getRowSpan, @@ -417,22 +451,9 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement ? html`` : render ? render(record) - : getActions - ? html` - - this.#openActions(evt, getActions(record, evt.target as HTMLElement))} - > - ` - : dataIndex - ? readProp(record, dataIndex) - : ''} + : dataIndex + ? readProp(record, dataIndex) + : ''} ` : '', @@ -451,6 +472,10 @@ export class DuoyunTableElement extends DuoyunScrollBoxElement )}
- - ${title} - ${tooltip - ? html` - - - - ` - : ''} - -
+ ${this.#sidePart} + ${this.selectable + ? html`` + : ''} ${!this.data ? html`
` : this.data.length === 0 diff --git a/packages/duoyun-ui/src/patterns/table.ts b/packages/duoyun-ui/src/patterns/table.ts index b2852fb1..283c3d1d 100644 --- a/packages/duoyun-ui/src/patterns/table.ts +++ b/packages/duoyun-ui/src/patterns/table.ts @@ -173,10 +173,11 @@ export class DyPatTableElement extends GemElement { // 如果不在 dy-pat-console 中,则需要提供 `locationStore` @property locationStore?: LocationStore; - @property rowKey?: string | string[]; - @property getRowStyle?: (record: T) => Partial; + @property rowKey?: DuoyunTableElement['rowKey']; + @property getRowStyle?: DuoyunTableElement['getRowStyle']; + @property expandedRowRender?: DuoyunTableElement['expandedRowRender']; + @property getActions?: DuoyunTableElement['getActions']; @property getSelectedActions?: (selections: any[]) => ContextMenuItem[]; - @property expandedRowRender?: (record: T) => undefined | string | TemplateResult; @emitter expand: Emitter; @@ -689,6 +690,7 @@ export class DyPatTableElement extends GemElement { part="table-wrap" exportparts="table,tr,td,th" .getRowStyle=${this.getRowStyle} + .getActions=${this.getActions} .expandedRowRender=${this.expandedRowRender} .data=${data} .columns=${this.#columns} diff --git a/packages/gem-examples/src/console/item-client.ts b/packages/gem-examples/src/console/item-client.ts index 26c31801..fec3e511 100644 --- a/packages/gem-examples/src/console/item-client.ts +++ b/packages/gem-examples/src/console/item-client.ts @@ -26,7 +26,13 @@ export class ConsolePageItemClientElement extends ConsolePageItemElement { } render = () => { return html` - + Add diff --git a/packages/gem-examples/src/console/item.ts b/packages/gem-examples/src/console/item.ts index 03e6ef60..949af4a8 100644 --- a/packages/gem-examples/src/console/item.ts +++ b/packages/gem-examples/src/console/item.ts @@ -1,7 +1,7 @@ import { html, GemElement } from '@mantou/gem/lib/element'; import { customElement } from '@mantou/gem/lib/decorators'; import { Time } from 'duoyun-ui/lib/time'; -import { ContextMenu } from 'duoyun-ui/elements/contextmenu'; +import { ContextMenu, ContextMenuItem } from 'duoyun-ui/elements/contextmenu'; import { FormItem, createForm } from 'duoyun-ui/patterns/form'; import { sleep } from 'duoyun-ui/lib/timer'; import { createPaginationStore } from 'duoyun-ui/helper/store'; @@ -72,12 +72,12 @@ export class ConsolePageItemElement extends GemElement { title: 'Address', dataIndex: ['address', 'street'], width: '15em', - visibleWidth: '58em', + visibleWidth: 'auto', }, { title: 'Company', width: '10em', - visibleWidth: '68em', + visibleWidth: 'auto', render: (r) => r.company.name, filterOptions: { field: ['company', 'name'], @@ -101,30 +101,28 @@ export class ConsolePageItemElement extends GemElement { { title: 'Updated', width: '8em', - visibleWidth: '76em', + visibleWidth: 'auto', render: (r) => new Time().relativeTimeFormat(new Time(r.updated)), filterOptions: { field: 'updated', type: 'date-time', }, }, + ]; + + getActions = (r: Item, activeElement: HTMLElement): ContextMenuItem[] => [ { - title: '', - getActions: (r, activeElement) => [ - { - text: 'Edit', - handle: () => this.onUpdate(r), - }, - { text: '---' }, - { - text: 'Delete', - danger: true, - handle: async () => { - await ContextMenu.confirm(`Confirm delete ${r.username}?`, { activeElement, danger: true }); - console.log('Delete: ', r); - }, - }, - ], + text: 'Edit', + handle: () => this.onUpdate(r), + }, + { text: '---' }, + { + text: 'Delete', + danger: true, + handle: async () => { + await ContextMenu.confirm(`Confirm delete ${r.username}?`, { activeElement, danger: true }); + console.log('Delete: ', r); + }, }, ]; @@ -249,6 +247,7 @@ export class ConsolePageItemElement extends GemElement { filterable .columns=${this.columns} .paginationStore=${this.state.pagination.store} + .getActions=${this.getActions} @fetch=${this.#onFetch} > Add