Skip to content

feat(grid): edit-config add blurOutside #3521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions examples/sites/demos/apis/grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -3563,9 +3563,11 @@ interface IEditConfig {
showStatus?: boolean
// 自定义编辑规则,返回true可以编辑返回false则禁止编辑
activeMethod?: ({row: IRow, column: IColumnConfig})=> boolean
// (3.19新增)当mode为'row'时,行编辑激活状态下默认会忽略activeMethod,配置为true使其生效
// (3.19.0新增)当mode为'row'时,行编辑激活状态下默认会忽略activeMethod,配置为true使其生效
activeStrictly?: boolean
}
//(3.25.0新增)自定义编辑态的退出逻辑。当返回true时,不会退出编辑态。
blurOutside?: ({ cell, event, $table }: { cell: HTMLElement, event: Event, $table: Component }) => boolean
}
`
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<template>
<div>
<tiny-grid
ref="grid"
:data="tableData"
seq-serial
:edit-config="{ trigger: 'click', mode: 'row', showStatus: true, blurOutside }"
>
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column
field="name"
title="名称"
width="500"
:show-icon="false"
:editor="{ component: 'input', autoselect: true }"
></tiny-grid-column>
<tiny-grid-column
field="area"
title="区域"
width="500"
:show-icon="false"
:editor="{ component: 'select', options }"
></tiny-grid-column>
<tiny-grid-column
field="address"
title="地址"
width="500"
:show-icon="false"
:editor="{ component: 'input', autoselect: true }"
></tiny-grid-column>
<tiny-grid-column
field="introduction"
title="公司简介"
width="500"
:show-icon="false"
:editor="{ component: 'input', autoselect: true }"
show-overflow="ellipsis"
></tiny-grid-column>
</tiny-grid>
</div>
</template>

<script setup>
import { TinyGrid, TinyGridColumn } from '@opentiny/vue'
import { ref } from 'vue'

const options = ref([
{ label: '华北区', value: '华北区' },
{ label: '华东区', value: '华东区' },
{ label: '华南区', value: '华南区' }
])
const tableData = ref([
{
id: '1',
name: 'GFD 科技 YX 公司',
area: '华东区',
address: '福州',
introduction: '公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
},
{
id: '2',
name: 'WWWW 科技 YX 公司',
area: '华南区',
address: '深圳福田区',
introduction: '公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
},
{
id: '3',
name: 'RFV 有限责任公司',
area: '华南区',
address: '中山市',
introduction: '公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
}
])

function blurOutside({ cell, event, $table }) {
const { getEventTargetNode, $el } = $table
const isClickRow = getEventTargetNode(event, $el, 'tiny-grid-body__row').flag
return isClickRow || isScrollBar(event, $el)
}
function isScrollBar(event, tableElm) {
const element = event.target

// 判断是否表格body
if (element !== tableElm.querySelector('.tiny-grid__body-wrapper')) {
return false
}

const rect = element.getBoundingClientRect()
const clickX = event.clientX
const clickY = event.clientY
// 检查垂直滚动条
if (element.scrollHeight > element.clientHeight) {
const scrollbarWidth = element.offsetWidth - element.clientWidth
if (clickX >= rect.right - scrollbarWidth && clickX <= rect.right) {
return true // 点击了垂直滚动条
}
}

// 检查水平滚动条
if (element.scrollWidth > element.clientWidth) {
const scrollbarHeight = element.offsetHeight - element.clientHeight
if (clickY >= rect.bottom - scrollbarHeight && clickY <= rect.bottom) {
return true // 点击了水平滚动条
}
}

return false
}
</script>

<style scoped>
.title {
font-size: 16px;
padding: 15px;
font-weight: bolder;
color: var(--tv-color-text, #191919);
}
</style>
28 changes: 28 additions & 0 deletions examples/sites/demos/pc/app/grid/edit/scrollbar-not-blur.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { test, expect } from '@playwright/test'

test('行编辑滚动不失焦', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())

await page.goto('grid-edit#scrollbar-not-blur')
page.setViewportSize({
width: 1400,
height: 1200
})

const demo = page.locator('#scrollbar-not-blur')
// 单元格编辑
await demo.getByRole('cell', { name: 'GFD 科技 YX 公司' }).first().click()
await expect(demo.locator('.tiny-grid-default-input').first()).toBeVisible()

// 点击滚动条
const bodyWrapper = demo.locator('.tiny-grid__body-wrapper')
const { x, y, height } = await bodyWrapper.boundingBox()
await page.mouse.move(x + 10, y + height - 3)
await page.waitForTimeout(200)
await page.mouse.down()
await page.waitForTimeout(200)
await page.mouse.move(x + 200, y + height - 3)
await page.waitForTimeout(200)
await page.mouse.up()
await expect(demo.locator('.tiny-grid-default-input').first()).toBeVisible()
})
129 changes: 129 additions & 0 deletions examples/sites/demos/pc/app/grid/edit/scrollbar-not-blur.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<template>
<div>
<tiny-grid
ref="grid"
:data="tableData"
seq-serial
:edit-config="{ trigger: 'click', mode: 'row', showStatus: true, blurOutside }"
>
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column
field="name"
title="名称"
width="500"
:show-icon="false"
:editor="{ component: 'input', autoselect: true }"
></tiny-grid-column>
<tiny-grid-column
field="area"
title="区域"
width="500"
:show-icon="false"
:editor="{ component: 'select', options }"
></tiny-grid-column>
<tiny-grid-column
field="address"
title="地址"
width="500"
:show-icon="false"
:editor="{ component: 'input', autoselect: true }"
></tiny-grid-column>
<tiny-grid-column
field="introduction"
title="公司简介"
width="500"
:show-icon="false"
:editor="{ component: 'input', autoselect: true }"
show-overflow="ellipsis"
></tiny-grid-column>
</tiny-grid>
</div>
</template>

<script>
import { TinyGrid, TinyGridColumn } from '@opentiny/vue'

export default {
components: {
TinyGrid,
TinyGridColumn
},
data() {
return {
options: [
{ label: '华北区', value: '华北区' },
{ label: '华东区', value: '华东区' },
{ label: '华南区', value: '华南区' }
],
tableData: [
{
id: '1',
name: 'GFD 科技 YX 公司',
area: '华东区',
address: '福州',
introduction: '公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
},
{
id: '2',
name: 'WWWW 科技 YX 公司',
area: '华南区',
address: '深圳福田区',
introduction: '公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
},
{
id: '3',
name: 'RFV 有限责任公司',
area: '华南区',
address: '中山市',
introduction: '公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
}
]
}
},
methods: {
blurOutside({ cell, event, $table }) {
const { getEventTargetNode, $el } = $table
const isClickRow = getEventTargetNode(event, $el, 'tiny-grid-body__row').flag
return isClickRow || this.isScrollBar(event, $el)
},
isScrollBar(event, tableElm) {
const element = event.target

// 判断是否表格body
if (element !== tableElm.querySelector('.tiny-grid__body-wrapper')) {
return false
}

const rect = element.getBoundingClientRect()
const clickX = event.clientX
const clickY = event.clientY
// 检查垂直滚动条
if (element.scrollHeight > element.clientHeight) {
const scrollbarWidth = element.offsetWidth - element.clientWidth
if (clickX >= rect.right - scrollbarWidth && clickX <= rect.right) {
return true // 点击了垂直滚动条
}
}

// 检查水平滚动条
if (element.scrollWidth > element.clientWidth) {
const scrollbarHeight = element.offsetHeight - element.clientHeight
if (clickY >= rect.bottom - scrollbarHeight && clickY <= rect.bottom) {
return true // 点击了水平滚动条
}
}

return false
}
}
}
</script>

<style scoped>
.title {
font-size: 16px;
padding: 15px;
font-weight: bolder;
color: var(--tv-color-text, #191919);
}
</style>
12 changes: 12 additions & 0 deletions examples/sites/demos/pc/app/grid/webdoc/grid-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ export default {
'<p>Table attribute setting<code>edit-config</code>Enable the editing mode, and then set <code>trigger</code> in the attribute object to modify the triggering mode. The options are as follows: <code>click trigger (click), double-click trigger (dblclick), and manual trigger (manual)</code>. The default value is <code>click trigger</code>. </p>\n'
},
codeFiles: ['edit/trigger-mode-for-editing.vue']
},
{
demoId: 'scrollbar-not-blur',
name: { 'zh-CN': '行编辑滚动不失焦', 'en-US': 'Scroll not blur' },
desc: {
'zh-CN': `
<p>配置 <code>edit-config</code> 的<code>blurOutside</code>自定义编辑态的退出逻辑。</p>
`,
'en-US':
'<p>Configure <code>blurOutside</code> of <code>edit-config</code> to customize the exit logic of the editing state. </p>\n'
},
codeFiles: ['edit/scrollbar-not-blur.vue']
}
],
apis: [{ name: 'grid-edit', 'type': 'component', 'props': [], 'events': [], 'slots': [] }]
Expand Down
1 change: 0 additions & 1 deletion packages/theme-saas/src/grid/table.less
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,6 @@
@apply w-full;
height: 100px;
@apply m-0;
background: url(../images/grid-nodata.svg) no-repeat center center;
@apply flex-shrink-0;
}

Expand Down
6 changes: 3 additions & 3 deletions packages/vue/src/grid/src/body/src/body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ function renderRowExpanded(args) {

function renderRowAfter({ $table, _vm, row, rowIndex, rows, id, used }) {
typeof $table.renderRowAfter === 'function' &&
$table.renderRowAfter.call($table, { rows, row, data: _vm.tableData, rowIndex, renderColumn, id, used }, h)
$table.renderRowAfter({ rows, row, data: _vm.tableData, rowIndex, renderColumn, id, used }, h)
}

function renderRow(args) {
Expand Down Expand Up @@ -829,7 +829,7 @@ export default defineComponent({
class={{
'tiny-grid__body-wrapper body__wrapper': true,
'is__scrollload': scrollLoad,
'no-data': isNoData
'no-data': isNoData && $table.isShapeTable
}}
style={{
height: bodyWrapperHeight ? `${bodyWrapperHeight}px` : undefined,
Expand Down Expand Up @@ -868,7 +868,7 @@ export default defineComponent({
isNoData ? (
<div ref="emptyBlock" class="tiny-grid__empty-block">
{$slots.empty
? $slots.empty.call(_vm, { $table }, h)
? $slots.empty({ $table }, h)
: $table.renderEmpty
? [$table.renderEmpty(h, $table)]
: [
Expand Down
2 changes: 1 addition & 1 deletion packages/vue/src/grid/src/cell/src/cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ export const Cell = {
const isTreeOrderedFalse = treeConfig && !treeOrdered
let indexValue = startIndex + seq
// tree-config为false的情况下,序号为1.1这种形式
if (isTreeOrderedFalse && level) {
if (isTreeOrderedFalse) {
indexValue = row[temporaryIndex]
}

Expand Down
5 changes: 4 additions & 1 deletion packages/vue/src/grid/src/mobile-first/column-link.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export default defineComponent({
vnode = h(
'div',
{
attrs: {
title: visibleButtons[0].name
},
'class': `w-5 h-5 sm:w-4 sm:w-4 ${
isDisabled(visibleButtons[0]) ? 'fill-color-icon-disabled' : 'fill-color-icon-secondary'
} `
Expand Down Expand Up @@ -70,7 +73,7 @@ export default defineComponent({
DropdownItem,
{
class: { [disabledClass || '']: isDisabled(buttonConfig) },
props: { itemData: buttonConfig.name, disabled: isDisabled(buttonConfig) }
props: { itemData: buttonConfig.name, disabled: isDisabled(buttonConfig), icon: buttonConfig.icon }
},
buttonConfig.name
)
Expand Down
Loading
Loading