feat: enhance the divider dialogbox treemenu function (#2758)

This commit is contained in:
ajaxzheng 2025-01-08 10:55:23 +08:00 committed by GitHub
parent 6e29e24dc3
commit 5f35800f62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
90 changed files with 2036 additions and 243 deletions

View File

@ -246,6 +246,36 @@ export default {
mode: ['pc'], mode: ['pc'],
pcDemo: 'dialog-width' pcDemo: 'dialog-width'
}, },
{
name: 'custom-style',
type: 'string',
defaultValue: ``,
desc: {
'zh-CN': '自定义实现双层抽屉',
'en-US': 'Custom Styles'
},
meta: {
stable: '3.21.0'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'double-dialog-height',
mfDemo: 'double-dialog-height'
},
{
name: 'no-animation',
type: 'string',
defaultValue: ``,
desc: {
'zh-CN': '是否开启动画',
'en-US': 'Indicates whether to enable animation'
},
meta: {
stable: '3.21.0'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'double-dialog-height',
mfDemo: 'double-dialog-height'
},
{ {
name: 'dialog-transition', name: 'dialog-transition',
type: 'string', type: 'string',

View File

@ -10,11 +10,13 @@ export default {
type: 'string', type: 'string',
defaultValue: "'solid'", defaultValue: "'solid'",
desc: { desc: {
'zh-CN': '设置分隔线的样式,该属性的可选值为 solid / dashed', 'zh-CN': '设置分隔线的样式,该属性的可选值为 solid / dashed / dotted',
'en-US': 'Set the style of the separator line, with optional values of solid/dashed for this property' 'en-US':
'Set the style of the separator line, with optional values of solid/dashed/dotted for this property'
}, },
mode: ['pc'], mode: ['pc', 'mobile-first'],
pcDemo: 'custom-style' pcDemo: 'custom-style',
mfDemo: 'type'
}, },
{ {
name: 'color', name: 'color',
@ -24,8 +26,9 @@ export default {
'zh-CN': '设置分隔线的颜色', 'zh-CN': '设置分隔线的颜色',
'en-US': 'Set the color of the divider.' 'en-US': 'Set the color of the divider.'
}, },
mode: ['pc'], mode: ['pc', 'mobile-first'],
pcDemo: 'custom-style' pcDemo: 'custom-style',
mfDemo: 'color'
}, },
{ {
name: 'content-background-color', name: 'content-background-color',
@ -58,8 +61,9 @@ export default {
'en-US': 'en-US':
'Set the position of the separator text, with optional values of left/center/right for this attribute' 'Set the position of the separator text, with optional values of left/center/right for this attribute'
}, },
mode: ['pc'], mode: ['pc', 'mobile-first'],
pcDemo: 'content-position' pcDemo: 'content-position',
mfDemo: 'content-position'
}, },
{ {
name: 'direction', name: 'direction',
@ -70,8 +74,83 @@ export default {
'en-US': 'en-US':
'Set the direction of the divider; the possible values for this property are "horizontal" or "vertical"' 'Set the direction of the divider; the possible values for this property are "horizontal" or "vertical"'
}, },
mode: ['pc', 'mobile-first'],
pcDemo: 'direction',
mfDemo: 'vertical'
},
{
name: 'margin',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '设置分割线上下左右的边距',
'en-US': 'Sets the margins of the split line'
},
meta: {
stable: '3.21.0'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'custom-style',
mfDemo: 'divider-margin'
},
{
name: 'offset',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '设置文本位置偏移量,需同时配合`text-position`属性使用,以确定偏移的方向',
'en-US':
'Sets the text position offset. This parameter must be used together with the `text-position` attribute to determine the offset direction'
},
meta: {
stable: '3.21.0'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'content-position',
mfDemo: 'offset'
},
{
name: 'height',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '设置整体的高度',
'en-US': 'Sets the overall height'
},
meta: {
stable: '3.21.0'
},
mode: ['pc'], mode: ['pc'],
pcDemo: 'direction' pcDemo: 'custom-style'
},
{
name: 'font-size',
type: 'string',
defaultValue: '14px',
desc: {
'zh-CN': '文本字体大小',
'en-US': 'Text font size'
},
meta: {
stable: '3.21.0'
},
mode: ['pc'],
pcDemo: 'custom-style'
},
{
name: 'status',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '分割线状态',
'en-US': 'Splitting Line Status'
},
meta: {
stable: '3.21.0'
},
mode: ['pc', 'mobile-first'],
pcDemo: 'status',
mfDemo: 'status'
} }
], ],
events: [], events: [],

View File

@ -219,10 +219,10 @@ export default {
}, },
{ {
name: 'validate-on-rule-change', name: 'validate-on-rule-change',
type: 'boolean', type: 'boolean | "deep"',
defaultValue: 'true', defaultValue: 'true',
desc: { desc: {
'zh-CN': '是否在 rules 属性改变后立即触发一次验证', 'zh-CN': '是否在 rules 属性改变后立即触发一次验证"deep"选项新增于3.21.0',
'en-US': 'Whether to trigger a verification immediately after the rules attribute is changed' 'en-US': 'Whether to trigger a verification immediately after the rules attribute is changed'
}, },
mode: ['pc', 'mobile', 'mobile-first'], mode: ['pc', 'mobile', 'mobile-first'],

View File

@ -0,0 +1,128 @@
<template>
<div>
<tiny-button @click="changModdeFn" type="primary">当前为 {{ flag }} 模式</tiny-button>
<tiny-button @click="changFn">双层抽屉 </tiny-button>
<tiny-dialog-box
:visible="boxVisibility"
right-slide
@update:visible="boxVisibility = $event"
title="父弹窗"
width="30%"
:custom-style="customStyle1"
>
<span>父弹窗内容</span>
<tiny-dialog-box
append-to-body
right-slide
:modal="false"
:close-on-click-modal="false"
no-animation
:visible="boxVisibility2"
:custom-style="customStyle2"
@close="dialogClose"
@update:visible="boxVisibility2 = $event"
title="子弹窗"
width="300px"
>
<span>子弹窗内容</span>
<template #footer>
<tiny-button type="primary" @click="boxVisibility2 = false"> 确定 </tiny-button>
</template>
</tiny-dialog-box>
<template #footer>
<tiny-button @click="boxVisibility = false">取消</tiny-button>
<tiny-button type="primary" @click="openDialog"> 子弹窗 </tiny-button>
</template>
</tiny-dialog-box>
</div>
</template>
<script>
import { Button, DialogBox } from '@opentiny/vue'
export default {
components: {
TinyButton: Button,
TinyDialogBox: DialogBox
},
data() {
return {
flag: 'auto',
boxVisibility: false,
customStyle1: {},
customStyle2: {},
boxVisibility2: false
}
},
methods: {
changFn() {
this.boxVisibility = true
},
changModdeFn() {
this.flag = this.flag === 'full' ? 'auto' : 'full'
},
openDialog() {
if (this.flag === 'full') {
this.customStyle1 = {
right: '8px',
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
transition: 'right 0.2s ease-in-out'
}
this.customStyle2 = {
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
opacity: 0
}
const rightW = document.body.offsetWidth * 0.3
this.customStyle1.right = -rightW + 80 + 'px'
this.customStyle2.right = rightW + 'px'
this.customStyle2.transition = 'right 200ms ease-in-out opacity 100ms linear'
this.boxVisibility2 = true
setTimeout(() => {
this.customStyle2.right = '85px'
}, 10)
setTimeout(() => {
this.customStyle2.opacity = 1
}, 100)
} else {
this.customStyle1 = {
right: '8px',
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
transition: 'right 0.2s ease-in-out'
}
this.customStyle2 = {
bottom: '8px',
top: 'unset',
height: 'auto',
borderRadius: '8px',
opacity: 0
}
const rightW = document.body.offsetWidth * 0.3
this.customStyle2.right = rightW + 46 + 'px'
this.customStyle2.transition = 'opacity 100ms linear'
this.boxVisibility2 = true
setTimeout(() => {
this.customStyle2.opacity = 1
}, 100)
}
},
dialogClose() {
setTimeout(() => {
this.customStyle1.right = '8px'
this.customStyle2.opacity = 0
}, 50)
}
}
}
</script>

View File

@ -82,6 +82,19 @@ export default {
'en-US': '<p>code>default</code>: content default slot. <code>footer</code>: bottom slot</p>' 'en-US': '<p>code>default</code>: content default slot. <code>footer</code>: bottom slot</p>'
}, },
codeFiles: ['dialog-slot.vue'] codeFiles: ['dialog-slot.vue']
},
{
demoId: 'double-dialog-height',
name: {
'zh-CN': '右侧双层弹框',
'en-US': 'Double-layer dialog box on the right'
},
desc: {
'zh-CN': '右侧弹窗分两种情况,父级弹框自动缩进,子级弹框高度撑满。父级弹框不缩进,子级弹框高度自适应。',
'en-US':
'There are two types of pop-up windows on the right. The parent pop-up boxes are automatically indented, and the child pop-up boxes are full. The parent dialog box is not indented, and the height of the child dialog box is adaptive.'
},
codeFiles: ['double-dialog-height.vue']
} }
] ]
} }

View File

@ -0,0 +1,19 @@
<template>
<div>
<p>基础使用</p>
<tiny-divider> </tiny-divider>
<br />
<p>带文本分割线</p>
<tiny-divider> 分割线 </tiny-divider>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,17 @@
<template>
<div>
<tiny-divider color="#55ccd9"></tiny-divider>
<br />
<tiny-divider color="#eb74df"> 分割线 </tiny-divider>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,17 @@
<template>
<div>
<tiny-divider> 分割线 </tiny-divider>
<tiny-divider content-position="left"> 分割线 </tiny-divider>
<tiny-divider content-position="right"> 分割线 </tiny-divider>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,19 @@
<template>
<div>
<p>
豫章故郡洪都新府星分翼轸地接衡庐襟三江而带五湖控蛮荆而引瓯越物华天宝龙光射牛斗之墟人杰地灵徐孺下陈蕃之榻
</p>
<tiny-divider margin="20px 0"> 分割线 </tiny-divider>
<p>雄州雾列俊采星驰台隍枕夷夏之交宾主尽东南之美都督阎公之雅望棨戟遥临宇文新州之懿范襜帷暂驻</p>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,17 @@
<template>
<div>
<tiny-divider content-position="left" offset="20%"> 分割线 </tiny-divider>
<tiny-divider content-position="left" offset="-14px"> 文本左偏移 </tiny-divider>
<tiny-divider content-position="right" offset="-14px"> 文本右偏移 </tiny-divider>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,19 @@
<template>
<div>
<tiny-divider> default </tiny-divider>
<tiny-divider status="success"> success </tiny-divider>
<tiny-divider status="error"> error </tiny-divider>
<tiny-divider status="warning"> warning </tiny-divider>
<tiny-divider status="info"> info </tiny-divider>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,17 @@
<template>
<div>
<tiny-divider> 分割线 </tiny-divider>
<tiny-divider border-style="dashed"> dash线 </tiny-divider>
<tiny-divider border-style="dotted"> dotted线 </tiny-divider>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,34 @@
<template>
<div>
<div>
指南
<tiny-divider direction="vertical" status="success"> </tiny-divider>
组件
<tiny-divider direction="vertical" status="success"> </tiny-divider>
主题
<tiny-divider direction="vertical" status="success"> </tiny-divider>
资源
</div>
<br />
<br />
<div class="divider-wrap">
Web 开发
<tiny-divider direction="vertical"> </tiny-divider>
卡片开发
<tiny-divider direction="vertical"> </tiny-divider>
移动开发
<tiny-divider direction="vertical"> </tiny-divider>
设计工具
</div>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -0,0 +1,7 @@
---
title: Divider 分割线
---
# Divider 分割线
<div>Divider 分割线组件,用于对不同内容的分隔。</div>

View File

@ -0,0 +1,7 @@
---
title: Divider split line
---
# Divider split line
<div>Divider Splitting line component, which is used to separate different contents.</div>

View File

@ -0,0 +1,111 @@
export default {
column: '2',
owner: '',
demos: [
{
demoId: 'basic-usage',
name: {
'zh-CN': '基本用法',
'en-US': 'Basic Usage'
},
desc: {
'zh-CN': '带文本的分割线',
'en-US': 'Splitting line with text'
},
codeFiles: ['basic-usage.vue']
},
{
demoId: 'divider-margin',
name: {
'zh-CN': '边距用法',
'en-US': 'Margin Usage'
},
desc: {
'zh-CN': '通过<code>margin</code>属性可以设置分割线上下左右的边距。',
'en-US': 'You can use the <code>margin</code> attribute to set the margins of the split line.'
},
codeFiles: ['divider-margin.vue']
},
{
demoId: 'color',
name: {
'zh-CN': '颜色用法',
'en-US': 'Color Usage'
},
desc: {
'zh-CN': '通过<code>color</code>属性可以自定义分割线颜色。',
'en-US': 'You can use the <code>color</code> attribute to customize the color of the split line.'
},
codeFiles: ['color.vue']
},
{
demoId: 'offset',
name: {
'zh-CN': '偏移量用法',
'en-US': 'Offset Usage'
},
desc: {
'zh-CN':
'通过<code>offset</code>属性可以设置文本位置偏移量,需同时配合<code>content-position</code>属性使用,以确定偏移的方向。',
'en-US':
'<code>offset</code> can be used to set the text position offset. It must be used together with the <code>content-position</code> attribute to determine the offset direction.'
},
codeFiles: ['offset.vue']
},
{
demoId: 'type',
name: {
'zh-CN': '分割线类型',
'en-US': 'Type Usage'
},
desc: {
'zh-CN':
'通过<code>border-style</code>属性可以设置分割线类型,可选值有<code>solid</code><code>dashed</code><code>dotted</code>,默认为<code>solid</code>。',
'en-US':
'You can set the split line type through the <code>border-style</code> attribute. The options are <code>solid</code><code>dashed</code><code>dotted</code>. The default value is <code>solid</code>.'
},
codeFiles: ['type.vue']
},
{
demoId: 'content-position',
name: {
'zh-CN': '文本位置用法',
'en-US': 'Text Position'
},
desc: {
'zh-CN':
'通过<code>content-position</code>属性可以设置分割线文本位置,可选值有<code>center</code><code>left</code><code>right</code>三种,默认为<code>center</code>。',
'en-US':
'You can use the <code>content-position</code> attribute to set the text position of the split line. The options are as follows: <code>center</code><code>left</code><code>right</code>. The default value is <code>center</code>.'
},
codeFiles: ['content-position.vue']
},
{
demoId: 'status',
name: {
'zh-CN': '状态',
'en-US': 'Status'
},
desc: {
'zh-CN':
'通过<code>status</code>属性可以设置分割线状态,可选值有<code>default</code><code>success</code><code>warning</code><code>error</code><code>info</code>五种状态。',
'en-US':
'You can use the <code>status</code> attribute to set the split line status. The options are <code>default</code><code>success</code><code>warning</code><code>error</code><code>info</code>.'
},
codeFiles: ['status.vue']
},
{
demoId: 'vertical',
name: {
'zh-CN': '垂直方向',
'en-US': 'Vertical direction'
},
desc: {
'zh-CN': '通过<code>direction</code>属性设置为<code>vertical</code>,垂直分割线,不支持默认插槽。',
'en-US':
'Set the <code>direction</code> attribute to <code>vertical</code>, which indicates a vertical split line. The default slot is not supported.'
},
codeFiles: ['vertical.vue']
}
]
}

View File

@ -125,7 +125,8 @@ export const cmpMenus = [
{ name: 'Amount', nameCn: '金额', key: 'amount' }, { name: 'Amount', nameCn: '金额', key: 'amount' },
{ name: 'currency', nameCn: '币种', key: 'currency' }, { name: 'currency', nameCn: '币种', key: 'currency' },
{ name: 'CalendarView', nameCn: '日历', key: 'calendar-view' }, { name: 'CalendarView', nameCn: '日历', key: 'calendar-view' },
{ name: 'FloatingButton', nameCn: '悬浮按钮', key: 'floating-button' } { name: 'FloatingButton', nameCn: '悬浮按钮', key: 'floating-button' },
{ name: 'Divider', nameCn: '分割线', key: 'divider' }
] ]
} }
] ]

View File

@ -58,7 +58,7 @@ const pickerOptions = {
} }
const yearPickerOptions = { const yearPickerOptions = {
disabledDate(year) { disabledDate(year) {
return year > 2025 || year < 2022 return year.getTime() > new Date(2025, 0, 1, 0).getTime() || year.getTime() < new Date(2022, 0, 1, 0).getTime()
} }
} }
</script> </script>

View File

@ -62,7 +62,9 @@ export default {
}, },
yearPickerOptions: { yearPickerOptions: {
disabledDate(year) { disabledDate(year) {
return year > 2025 || year < 2022 return (
year.getTime() > new Date(2025, 0, 1, 0).getTime() || year.getTime() < new Date(2022, 0, 1, 0).getTime()
)
} }
} }
} }

View File

@ -0,0 +1,129 @@
<template>
<div>
<tiny-button @click="changModdeFn" type="primary">当前为 {{ flag }} 模式</tiny-button>
<tiny-button @click="changFn">双层抽屉 </tiny-button>
<tiny-dialog-box
:visible="boxVisibility"
right-slide
@update:visible="boxVisibility = $event"
title="父弹窗"
width="30%"
:custom-style="customStyle1"
>
<span>父弹窗内容</span>
<tiny-dialog-box
append-to-body
right-slide
:modal="false"
:close-on-click-modal="false"
no-animation
:visible="boxVisibility2"
:custom-style="customStyle2"
@close="dialogClose"
@update:visible="boxVisibility2 = $event"
title="子弹窗"
width="300px"
>
<span>子弹窗内容</span>
<template #footer>
<tiny-button type="primary" @click="boxVisibility2 = false"> 确定 </tiny-button>
</template>
</tiny-dialog-box>
<template #footer>
<tiny-button @click="boxVisibility = false">取消</tiny-button>
<tiny-button type="primary" @click="openDialog"> 子弹窗 </tiny-button>
</template>
</tiny-dialog-box>
</div>
</template>
<script setup lang="jsx">
import { ref, nextTick } from 'vue'
import { TinyButton, TinyDialogBox } from '@opentiny/vue'
const boxVisibility = ref(false)
const boxVisibility2 = ref(false)
const flag = ref('auto')
const customStyle1 = ref({})
const customStyle2 = ref({})
function changFn() {
boxVisibility.value = true
}
function changModdeFn() {
flag.value = flag.value === 'full' ? 'auto' : 'full'
}
function openDialog() {
if (flag.value === 'full') {
customStyle1.value = {
right: '8px',
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
transition: 'right 0.2s ease-in-out'
}
customStyle2.value = {
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
opacity: 0
}
const rightW = document.body.offsetWidth * 0.3
customStyle1.value.right = -rightW + 80 + 'px'
customStyle2.value.right = rightW + 'px'
customStyle2.value.transition = 'right 200ms ease-in-out opacity 100ms linear'
boxVisibility2.value = true
nextTick(() => {
customStyle2.value.right = '85px'
}, 10)
nextTick(() => {
customStyle2.value.opacity = 1
}, 100)
} else {
customStyle1.value = {
right: '8px',
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
transition: 'right 0.2s ease-in-out'
}
customStyle2.value = {
bottom: '8px',
top: 'unset',
height: 'auto',
borderRadius: '8px',
opacity: 0
}
const rightW = document.body.offsetWidth * 0.3
customStyle2.value.right = rightW + 46 + 'px'
customStyle2.value.transition = 'opacity 100ms linear'
boxVisibility2.value = true
setTimeout(() => {
customStyle2.value.opacity = 1
}, 100)
}
}
function dialogClose() {
if (flag.value === 'full') {
nextTick(() => {
customStyle1.value.right = '8px'
customStyle2.value.opacity = 0
}, 50)
} else {
customStyle2.value.opacity = 0
}
}
</script>

View File

@ -0,0 +1,128 @@
<template>
<div>
<tiny-button @click="changModdeFn" type="primary">当前为 {{ flag }} 模式</tiny-button>
<tiny-button @click="changFn">双层抽屉 </tiny-button>
<tiny-dialog-box
:visible="boxVisibility"
right-slide
@update:visible="boxVisibility = $event"
title="父弹窗"
width="30%"
:custom-style="customStyle1"
>
<span>父弹窗内容</span>
<tiny-dialog-box
append-to-body
right-slide
:modal="false"
:close-on-click-modal="false"
no-animation
:visible="boxVisibility2"
:custom-style="customStyle2"
@close="dialogClose"
@update:visible="boxVisibility2 = $event"
title="子弹窗"
width="300px"
>
<span>子弹窗内容</span>
<template #footer>
<tiny-button type="primary" @click="boxVisibility2 = false"> 确定 </tiny-button>
</template>
</tiny-dialog-box>
<template #footer>
<tiny-button @click="boxVisibility = false">取消</tiny-button>
<tiny-button type="primary" @click="openDialog"> 子弹窗 </tiny-button>
</template>
</tiny-dialog-box>
</div>
</template>
<script>
import { Button, DialogBox } from '@opentiny/vue'
export default {
components: {
TinyButton: Button,
TinyDialogBox: DialogBox
},
data() {
return {
flag: 'auto',
boxVisibility: false,
customStyle1: {},
customStyle2: {},
boxVisibility2: false
}
},
methods: {
changFn() {
this.boxVisibility = true
},
changModdeFn() {
this.flag = this.flag === 'full' ? 'auto' : 'full'
},
openDialog() {
if (this.flag === 'full') {
this.customStyle1 = {
right: '8px',
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
transition: 'right 0.2s ease-in-out'
}
this.customStyle2 = {
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
opacity: 0
}
const rightW = document.body.offsetWidth * 0.3
this.customStyle1.right = -rightW + 80 + 'px'
this.customStyle2.right = rightW + 'px'
this.customStyle2.transition = 'right 200ms ease-in-out opacity 100ms linear'
this.boxVisibility2 = true
setTimeout(() => {
this.customStyle2.right = '85px'
}, 10)
setTimeout(() => {
this.customStyle2.opacity = 1
}, 100)
} else {
this.customStyle1 = {
right: '8px',
bottom: '8px',
top: '8px',
height: 'auto',
borderRadius: '8px',
transition: 'right 0.2s ease-in-out'
}
this.customStyle2 = {
bottom: '8px',
top: 'unset',
height: 'auto',
borderRadius: '8px',
opacity: 0
}
const rightW = document.body.offsetWidth * 0.3
this.customStyle2.right = rightW + 46 + 'px'
this.customStyle2.transition = 'opacity 100ms linear'
this.boxVisibility2 = true
setTimeout(() => {
this.customStyle2.opacity = 1
}, 100)
}
},
dialogClose() {
setTimeout(() => {
this.customStyle1.right = '8px'
this.customStyle2.opacity = 0
}, 50)
}
}
}
</script>

View File

@ -164,6 +164,19 @@ export default {
}, },
codeFiles: ['right-dialog.vue'] codeFiles: ['right-dialog.vue']
}, },
{
demoId: 'double-dialog-height',
name: {
'zh-CN': '右侧双层弹框',
'en-US': 'Double-layer dialog box on the right'
},
desc: {
'zh-CN': '右侧弹窗分两种情况,父级弹框自动缩进,子级弹框高度撑满。父级弹框不缩进,子级弹框高度自适应。',
'en-US':
'There are two types of pop-up windows on the right. The parent pop-up boxes are automatically indented, and the child pop-up boxes are full. The parent dialog box is not indented, and the height of the child dialog box is adaptive.'
},
codeFiles: ['double-dialog-height.vue']
},
{ {
demoId: 'hidden-header', demoId: 'hidden-header',
name: { name: {

View File

@ -1,7 +0,0 @@
import { test, expect } from '@playwright/test'
test('Divider 基础用法', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('divider#basic-usage')
await page.locator('.tiny-divider').click()
})

View File

@ -0,0 +1,14 @@
<template>
<div>
<p>文案在左侧</p>
<tiny-divider content-position="left" offset="20%">左侧且偏移20%</tiny-divider>
<p>文案在中间</p>
<tiny-divider content-position="center">中间</tiny-divider>
<p>文案在右侧</p>
<tiny-divider content-position="right" offset="80px">右侧且偏移80px</tiny-divider>
</div>
</template>
<script setup>
import { TinyDivider } from '@opentiny/vue'
</script>

View File

@ -0,0 +1,20 @@
<template>
<div>
<p>文案在左侧</p>
<tiny-divider content-position="left" offset="20%">左侧且偏移20%</tiny-divider>
<p>文案在中间</p>
<tiny-divider content-position="center">中间</tiny-divider>
<p>文案在右侧</p>
<tiny-divider content-position="right" offset="80px">右侧且偏移80px</tiny-divider>
</div>
</template>
<script>
import { TinyDivider } from '@opentiny/vue'
export default {
components: {
TinyDivider
}
}
</script>

View File

@ -1,12 +0,0 @@
<template>
<p>文案在左侧</p>
<tiny-divider content-position="left">文案</tiny-divider>
<p>文案在中间</p>
<tiny-divider content-position="center">文案</tiny-divider>
<p>文案在右侧</p>
<tiny-divider content-position="right">文案</tiny-divider>
</template>
<script setup>
import { TinyDivider } from '@opentiny/vue'
</script>

View File

@ -1,20 +0,0 @@
<template>
<div>
<p>文案在左侧</p>
<tiny-divider content-position="left">文案</tiny-divider>
<p>文案在中间</p>
<tiny-divider content-position="center">文案</tiny-divider>
<p>文案在右侧</p>
<tiny-divider content-position="right">文案</tiny-divider>
</div>
</template>
<script>
import { TinyDivider } from '@opentiny/vue'
export default {
components: {
TinyDivider
}
}
</script>

View File

@ -0,0 +1,14 @@
<template>
<p>自定义分隔线颜色</p>
<tiny-divider color="#1476ff"></tiny-divider>
<p>自定义文案的颜色及字体大小</p>
<tiny-divider content-color="#1476ff">字体颜色</tiny-divider>
<tiny-divider content-color="#ffffff" content-background-color="#1476ff">文案背景</tiny-divider>
<tiny-divider font-size="16px">字体大小</tiny-divider>
<p>自定义分割线整体内容高度及边距</p>
<tiny-divider height="40px" margin="8px 0">高度及边距</tiny-divider>
</template>
<script setup>
import { TinyDivider } from '@opentiny/vue'
</script>

View File

@ -0,0 +1,22 @@
<template>
<div>
<p>自定义分隔线颜色</p>
<tiny-divider color="#1476ff"></tiny-divider>
<p>自定义文案的颜色及字体大小</p>
<tiny-divider content-color="#1476ff">字体颜色</tiny-divider>
<tiny-divider content-color="#ffffff" content-background-color="#1476ff">文案背景</tiny-divider>
<tiny-divider font-size="16px">字体大小</tiny-divider>
<p>自定义分割线整体内容高度及边距</p>
<tiny-divider height="40px" margin="8px 0">高度及边距</tiny-divider>
</div>
</template>
<script>
import { TinyDivider } from '@opentiny/vue'
export default {
components: {
TinyDivider
}
}
</script>

View File

@ -1,14 +0,0 @@
<template>
<p>自定义分隔线颜色</p>
<tiny-divider color="#1476ff"></tiny-divider>
<p>自定义分隔线的样式</p>
<tiny-divider border-style="dashed"></tiny-divider>
<p>自定义文案的颜色</p>
<tiny-divider content-color="#1476ff">文案</tiny-divider>
<p>文案的背景颜色</p>
<tiny-divider content-color="#ffffff" content-background-color="#1476ff">文案</tiny-divider>
</template>
<script setup>
import { TinyDivider } from '@opentiny/vue'
</script>

View File

@ -1,22 +0,0 @@
<template>
<div>
<p>自定义分隔线颜色</p>
<tiny-divider color="#1476ff"></tiny-divider>
<p>自定义分隔线的样式</p>
<tiny-divider border-style="dashed"></tiny-divider>
<p>自定义文案的颜色</p>
<tiny-divider content-color="#1476ff">文案</tiny-divider>
<p>文案的背景颜色</p>
<tiny-divider content-color="#ffffff" content-background-color="#1476ff">文案</tiny-divider>
</div>
</template>
<script>
import { TinyDivider } from '@opentiny/vue'
export default {
components: {
TinyDivider
}
}
</script>

View File

@ -1,9 +1,11 @@
<template> <template>
<span>分隔线</span> <div>
<tiny-divider direction="vertical"></tiny-divider> <span>分隔线</span>
<span>分隔线</span> <tiny-divider direction="vertical"></tiny-divider>
<tiny-divider direction="vertical"></tiny-divider> <span>分隔线</span>
<span>分隔线</span> <tiny-divider direction="vertical"></tiny-divider>
<span>分隔线</span>
</div>
</template> </template>
<script setup> <script setup>

View File

@ -0,0 +1,11 @@
<template>
<div>
<tiny-divider>solid线</tiny-divider>
<tiny-divider border-style="dashed">dashed线</tiny-divider>
<tiny-divider border-style="dotted">dotted线</tiny-divider>
</div>
</template>
<script setup>
import { TinyDivider } from '@opentiny/vue'
</script>

View File

@ -0,0 +1,17 @@
<template>
<div>
<tiny-divider>solid线</tiny-divider>
<tiny-divider border-style="dashed">dashed线</tiny-divider>
<tiny-divider border-style="dotted">dotted线</tiny-divider>
</div>
</template>
<script>
import { TinyDivider } from '@opentiny/vue'
export default {
components: {
TinyDivider
}
}
</script>

View File

@ -0,0 +1,13 @@
<template>
<div>
<tiny-divider> default </tiny-divider>
<tiny-divider status="success"> success </tiny-divider>
<tiny-divider status="error"> error </tiny-divider>
<tiny-divider status="warning"> warning </tiny-divider>
<tiny-divider status="info"> info </tiny-divider>
</div>
</template>
<script setup>
import { TinyDivider } from '@opentiny/vue'
</script>

View File

@ -0,0 +1,19 @@
<template>
<div>
<tiny-divider> default </tiny-divider>
<tiny-divider status="success"> success </tiny-divider>
<tiny-divider status="error"> error </tiny-divider>
<tiny-divider status="warning"> warning </tiny-divider>
<tiny-divider status="info"> info </tiny-divider>
</div>
</template>
<script>
import { Divider } from '@opentiny/vue'
export default {
components: {
TinyDivider: Divider
}
}
</script>

View File

@ -12,7 +12,7 @@ export default {
'zh-CN': '通过引用组件标签即可。', 'zh-CN': '通过引用组件标签即可。',
'en-US': 'You can refer to component label.' 'en-US': 'You can refer to component label.'
}, },
codeFiles: ['base.vue'] codeFiles: ['basic-usage.vue']
}, },
{ {
demoId: 'direction', demoId: 'direction',
@ -21,8 +21,9 @@ export default {
'en-US': 'Vertical divider line' 'en-US': 'Vertical divider line'
}, },
desc: { desc: {
'zh-CN': '<p>通过 <code>direction</code> 属性可以设置分隔线的方向。</p>', 'zh-CN': '<p>通过设置<code>vertical</code>属性为<code>true</code>可以设置分隔线的方向。</p>',
'en-US': '<p>The direction of the separator line can be set through the<code>direction</code>attribute.</p>' 'en-US':
'<p>You can set the direction of the divider by setting the <code>vertical</code> property to <code>true</code>.</p>'
}, },
codeFiles: ['direction.vue'] codeFiles: ['direction.vue']
}, },
@ -33,11 +34,11 @@ export default {
'en-US': 'The position of the separator copy' 'en-US': 'The position of the separator copy'
}, },
desc: { desc: {
'zh-CN': '<p>通过 <code>content-position</code> 属性 可以设置分隔线文案的位置。</p>', 'zh-CN': '通过<code>text-position</code> 属性设置分隔线文案的位置,<code>offset</code>属性设置左右偏移量。',
'en-US': 'en-US':
'<p>The position of the separator text can be set through the<code>content position</code>attribute.</p>' 'The <code>text-position</code> attribute is used to set the position of the partition line copywriting, and the <code>offset</code> attribute is used to set the left and right offsets.'
}, },
codeFiles: ['contentPosition.vue'] codeFiles: ['content-position.vue']
}, },
{ {
demoId: 'custom-style', demoId: 'custom-style',
@ -47,11 +48,37 @@ export default {
}, },
desc: { desc: {
'zh-CN': 'zh-CN':
'<p>通过<code>color</code><code>border-style</code><code>content-color</code>属性,自定义分割线样式。</p>', '通过<code>line-color</code>属性设置分割线颜色,<code>content-background-color</code>属性设置文案背景颜色,<code>content-color</code>属性设置文字颜色,<code>height</code>属性设置分割线整体内容高度,<code>margin</code>属性设置外边距值。',
'en-US': 'en-US':
'<p>Customize the split line style through the<code>color</code><code>border style</code><code>content color</code>attribute.</p>' 'The <code>line-color</code> attribute sets the color of the split line, the <code>content-background-color</code> attribute sets the background color of the copywriting, the <code>content-color</code> attribute sets the text color, and the <code>height</code> Property Sets the overall content height of the split line. The <code>margin</code> property sets the outer margin value.'
}, },
codeFiles: ['customStyle.vue'] codeFiles: ['custom-style.vue']
},
{
demoId: 'divider-type',
name: {
'zh-CN': '分隔线类型',
'en-US': 'Separator Line Type'
},
desc: {
'zh-CN':
'通过<code>type</code>设置分割线显示类型,默认值为<code>solid</code>,可选值为<code>dashed</code><code>dotted</code>。',
'en-US':
'Set the display type of the split line through <code>type</code>. The default value is <code>solid</code>. The options are <code>dashed</code><code>dotted</code>.'
},
codeFiles: ['divider-type.vue']
},
{
demoId: 'status',
name: {
'zh-CN': '分隔线状态',
'en-US': 'Separator Status'
},
desc: {
'zh-CN': '通过<code>status</code>属性设置分割线显示状态颜色。',
'en-US': 'Use the <code>status</code> attribute to set the color of the display status of the splitter line.'
},
codeFiles: ['status.vue']
} }
] ]
} }

View File

@ -10,7 +10,7 @@ import { getAlias, pathFromWorkspaceRoot } from '../../config/vite'
import { external } from '../../shared/config' import { external } from '../../shared/config'
import type { Module } from '../../shared/module-utils' import type { Module } from '../../shared/module-utils'
import { getAllIcons, getAllModules, getByName } from '../../shared/module-utils' import { getAllIcons, getAllModules, getByName } from '../../shared/module-utils'
import { logGreen, kebabCase, capitalizeKebabCase, getPatchVersion, isValidVersion } from '../../shared/utils' import { capitalizeKebabCase, getPatchVersion, isValidVersion, kebabCase, logGreen } from '../../shared/utils'
import generatePackageJsonPlugin from './rollup/generate-package-json' import generatePackageJsonPlugin from './rollup/generate-package-json'
import inlineChunksPlugin from './rollup/inline-chunks' import inlineChunksPlugin from './rollup/inline-chunks'
import replaceModuleNamePlugin from './rollup/replace-module-name' import replaceModuleNamePlugin from './rollup/replace-module-name'
@ -257,11 +257,7 @@ export const getBaseConfig = ({ vueVersion, dtsInclude, dts, buildTarget, isRunt
extensions: ['.js', '.ts', '.tsx', '.vue'], extensions: ['.js', '.ts', '.tsx', '.vue'],
alias: { alias: {
...getAlias(vueVersion, '', design), ...getAlias(vueVersion, '', design),
'@tiptap/vue': `${ '@tiptap/vue': `${vueVersion === '2' ? '@tiptap/vue-2' : '@tiptap/vue-3'}`,
vueVersion === '2'
? path.resolve(pathFromPackages(''), 'vue/src/rich-text-editor/node_modules/@tiptap/vue-2')
: path.resolve(pathFromPackages(''), 'vue/src/rich-text-editor/node_modules/@tiptap/vue-3')
}`,
'@vue/babel-helper-vue-jsx-merge-props': 'node_modules/@vue/babel-helper-vue-jsx-merge-props/dist/helper.js' '@vue/babel-helper-vue-jsx-merge-props': 'node_modules/@vue/babel-helper-vue-jsx-merge-props/dist/helper.js'
} }
}, },

View File

@ -1,3 +1,5 @@
import rich_text_editor_pkg from '../../../../packages/vue/src/rich-text-editor/package.json'
const EXTENERAL = [ const EXTENERAL = [
'vue', 'vue',
'axios', 'axios',
@ -9,7 +11,8 @@ const EXTENERAL = [
'streamsaver', 'streamsaver',
'shepherd.js', 'shepherd.js',
'./label-wrap', './label-wrap',
'./tall-storage.vue' './tall-storage.vue',
...Object.keys(rich_text_editor_pkg.peerDependencies)
] ]
const external = (deps) => { const external = (deps) => {
return EXTENERAL.includes(deps) || /^@opentiny[\\/]|@originjs|echarts|cropperjs|@better-scroll/.test(deps) return EXTENERAL.includes(deps) || /^@opentiny[\\/]|@originjs|echarts|cropperjs|@better-scroll/.test(deps)

View File

@ -0,0 +1,496 @@
/**
* Copyright (c) 2022 - present TinyVue Authors.
* Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import * as hooks from 'vue'
import { camelize, capitalize, hyphenate } from '@opentiny/mobile-utils/string'
import { bindFilter, emitter, getElementCssClass, getElementStatusClass } from '../utils'
const Teleport = hooks.Teleport
export { emitter, bindFilter, getElementCssClass, getElementStatusClass, Teleport }
export const defineAsyncComponent = hooks.defineAsyncComponent
export const markRaw = hooks.markRaw
export const renderComponent = ({
view = undefined as any,
component = undefined as any,
props,
context: { attrs, slots },
extend = {}
}) => {
return () => hooks.h((view && view.value) || component, { ref: 'modeTemplate', ...props, ...attrs, ...extend }, slots)
}
export const rootConfig = (context) => {
const instance = hooks.getCurrentInstance()
context && setInstanceEmitter(instance)
return instance?.appContext.config.globalProperties
}
export const getComponentName = () => {
// 此处组件最多为两层组件,所以对多获取到父级组件即可
const instance = hooks.getCurrentInstance()
let componentName = instance?.type?.name
if (!componentName) {
componentName = instance?.parent?.type?.name
}
return componentName || ''
}
export const appContext = () =>
hooks.getCurrentInstance()?.appContext || {
component: () => {
// do nothing
}
}
export const appProperties = () => {
const instance = hooks.getCurrentInstance()
return instance?.appContext.config.globalProperties || {}
}
export const useRouter = (instance = hooks.getCurrentInstance()) => {
const router = instance?.appContext.config.globalProperties.$router
const route = router && router.currentRoute.value
return { route, router }
}
const setInstanceEmitter = (instance) => {
const $emitter = emitter()
if (typeof instance.$emitter === 'undefined') Object.defineProperty(instance, '$emitter', { get: () => $emitter })
}
const emitEvent = (vm) => {
const broadcast = (vm, componentName, eventName, params) => {
const children = (vm.subTree && vm.subTree.children) || vm.children
Array.isArray(children) &&
children.forEach((child) => {
const name = child.type && child.type.componentName
const component = child.component
if (name === componentName) {
component.emit(eventName, params)
component.$emitter && component.$emitter.emit(eventName, params)
} else {
broadcast(child, componentName, eventName, params)
}
})
}
return {
dispatch(componentName, eventName, params) {
let parent = vm.parent || vm.root
let name = parent.type && parent.type.componentName
while (parent && (!name || name !== componentName)) {
parent = parent.parent
if (parent) name = parent.type && parent.type.componentName
}
if (parent) {
parent.emit(...[eventName].concat(params))
// fix: VUE3下事件参数为数组VUE2下事件参数不是数组这里修改为和VUE2兼容
parent.$emitter && parent.$emitter.emit(...[eventName].concat(params))
}
},
broadcast(componentName, eventName, params) {
broadcast(vm, componentName, eventName, params)
}
}
}
const getRealParent = (vm) => {
if (vm && vm.parent)
return vm.parent.type.name === 'AsyncComponentWrapper' && vm.parent.parent ? vm.parent.parent : vm.parent
}
const parent = (vm) => (handler) => {
let parent = getRealParent(vm)
let level = 0
const parentObject = (parent) => {
return {
level,
vm: createVm({}, parent),
el: parent.vnode.el,
options: parent.type
}
}
if (typeof handler !== 'function') return parent ? parentObject(parent) : {}
level++
while (parent) {
if (handler(parentObject(parent))) break
parent = getRealParent(parent)
level++
}
}
const children = (vm) => (handler) => {
if (typeof handler !== 'function') return generateChildren(vm.subTree)
let layer = 1
const broadcast = (subTree) => {
if (subTree) {
const children = subTree.children || subTree.dynamicChildren
const level = layer++
if (Array.isArray(children)) {
if (
children.some((child) => {
return (
child.component &&
handler({
level,
vm: createVm({}, child.component),
el: child.el,
options: child.type,
isLevel1: true
})
)
})
)
return
children.forEach((child) => broadcast(child))
}
}
}
broadcast(vm.subTree)
}
const regEventPrefix = /^on[A-Z]/
const generateListeners = (attrs) => {
const $attrs = {}
const $listeners = {}
for (const name in attrs) {
const event = attrs[name]
if (regEventPrefix.test(name) && typeof event === 'function') {
$listeners[hyphenate(name.substr(2))] = event
continue
}
$attrs[name] = event
}
return { $attrs, $listeners }
}
const generateChildren = (subTree) => {
const children: any = []
children.refs = {}
if (subTree) {
const arr = subTree.dynamicChildren || subTree.children
if (Array.isArray(arr)) {
arr.forEach((child) => {
if (child.component) {
const vm = createVm({}, child.component)
children.push(vm)
child.props.ref && (children.refs[child.props.ref] = vm)
}
})
} else if (subTree.component) {
children.push(createVm({}, subTree.component))
}
}
return children
}
const defineProperties = (vm, instance, property, filter) => {
for (const name in instance[property]) {
if (typeof filter === 'function' && filter(name)) continue
Object.defineProperty(vm, name, {
configurable: true,
enumerable: true,
get: () => instance[property][name],
set: (value) => (instance[property][name] = value)
})
}
return vm
}
const filter = (name) => name.indexOf('_') === 0
const defineInstanceVm = (vm, instance) => {
defineProperties(vm, instance, 'setupState', null)
defineProperties(vm, instance, 'props', filter)
defineProperties(vm, instance, 'ctx', filter)
return vm
}
const createVm = (vm, instance, context = null) => {
const { $attrs, $listeners } = generateListeners(instance.attrs)
let $emitter = instance.$emitter
if (!$emitter) {
setInstanceEmitter(instance)
$emitter = instance.$emitter
}
const emit = (...args) => {
instance.emit(...args)
$emitter.emit.apply(vm, args)
}
const $set = (target, propertyName, value) => (target[propertyName] = value)
context || defineInstanceVm(vm, instance)
Object.defineProperties(vm, {
$attrs: { get: () => $attrs },
$children: { get: () => generateChildren(instance.subTree) },
$constants: { get: () => instance.props._constants },
$emit: { get: () => emit },
$el: { get: () => instance.vnode.el },
$listeners: { get: () => $listeners },
$mode: { get: () => instance._tiny_mode },
$nextTick: { get: () => hooks.nextTick },
$off: { get: () => $emitter.off },
$on: { get: () => $emitter.on },
$once: { get: () => $emitter.once },
$options: { get: () => ({ componentName: instance.type.componentName }) },
$parent: {
get: () => instance.parent && createVm({}, getRealParent(instance))
},
$refs: { get: () => instance.refs },
$renderless: { get: () => instance.props.tiny_renderless },
$scopedSlots: { get: () => instance.slots },
$set: { get: () => $set },
$slots: { get: () => instance.slots },
$template: { get: () => instance.props.tiny_template }
})
return vm
}
const onBeforeMount = (instance, refs) => {
for (let name in instance.refs) {
if (Object.prototype.hasOwnProperty.call(instance.refs, name)) {
refs[name] = instance.refs[name]
}
}
}
export const tools = (context, mode) => {
const instance = hooks.getCurrentInstance() as any
const root = instance?.appContext.config.globalProperties
const { route, router } = useRouter(instance)
const i18n = instance?.proxy?.$root?.$i18n
const { dispatch, broadcast } = emitEvent(instance)
const parentHandler = parent(instance)
const childrenHandler = children(instance)
const vm = createVm({}, instance, context)
const emit = context.emit
const refs = {}
const grandParent = typeof instance.props.tiny_template === 'undefined' && getRealParent(instance)
const parentVm = grandParent ? createVm({}, grandParent) : instance.parent ? createVm({}, instance.parent) : null
const setParentAttribute = ({ name, value }) => {
const ctx = grandParent ? grandParent.ctx : instance?.parent?.ctx
ctx[name] = value
// 当前的parentVm也保存一下。
parentVm[name] = value
}
const defineInstanceProperties = (props) => {
Object.defineProperties(vm, props)
Object.defineProperties(instance?.ctx, props)
}
const defineParentInstanceProperties = (props) => {
parentVm && Object.defineProperties(parentVm, props)
}
hooks.onBeforeMount(() => defineInstanceVm(vm, instance))
hooks.onMounted(() => onBeforeMount(instance, refs))
return {
framework: 'vue3',
vm,
emit,
emitter,
route,
router,
dispatch,
broadcast,
parentHandler,
childrenHandler,
i18n,
refs,
slots: instance?.slots,
scopedSlots: instance?.slots,
attrs: context.attrs,
parent: parentVm,
nextTick: hooks.nextTick,
constants: instance?.props._constants,
mode,
isPCMode: mode === 'pc',
isMobileMode: mode === 'mobile',
service: root?.$service,
getService: () => root?.$getService(vm),
setParentAttribute,
defineInstanceProperties,
defineParentInstanceProperties
}
}
const mapping = (content, before, after) => {
if (typeof content[before] !== 'undefined') {
const fn = content[before]
content[after] = (el, binding, vnode) => {
vnode.context = binding.instance
fn(el, binding, vnode)
}
delete content[before]
}
}
export const directive = (directives) => {
for (const name in directives) {
const content = directives[name]
mapping(content, 'bind', 'beforeMount')
mapping(content, 'update', 'updated')
mapping(content, 'unbind', 'unmounted')
}
return directives
}
export const parseVnode = (vnode) => vnode
const { Text, Comment } = hooks
export const isEmptyVnode = (vnode) => !vnode || !vnode.type || [Text, Comment].includes(vnode.type)
const parseProps = (propsData) => {
const props = {}
for (const name in propsData) {
if (name === 'class' || name === 'style') {
props[name] = propsData[name]
} else if (name === 'on' || name === 'nativeOn') {
const events = propsData[name]
for (const eventName in events) props[`on${capitalize(camelize(eventName))}`] = events[eventName]
} else if (name === 'attrs' || name === 'props' || name === 'domProps') {
const attrs = propsData[name]
for (const key in attrs) props[key] = attrs[key]
} else {
props[name] = propsData[name]
}
}
return props
}
const customResolveComponent = (component) => {
let type = component
let customElement = false
if (typeof component === 'string' && typeof document !== 'undefined') {
const el = document.createElement(component)
const svgTagNames = ['SVG', 'CIRCLE', 'PATH']
if ((el instanceof HTMLUnknownElement && !svgTagNames.includes(el.nodeName)) || component.includes('-')) {
component = component.toLowerCase()
customElement = true
if (component === 'transition') type = hooks.Transition
else if (component === 'transition-group') type = hooks.TransitionGroup
else type = hooks.resolveComponent(component)
} else {
type = component
}
}
return { type, component, customElement }
}
type CreateElement = (component: any, propsData?: any, childData?: any) => ReturnType<typeof hooks.h>
export const h: CreateElement = (component, propsData, childData) => {
let props = {}
let children = childData
const ret = customResolveComponent(component)
const customElement = ret.customElement
const type = ret.type
component = ret.component
if (propsData && typeof propsData === 'object' && !Array.isArray(propsData)) {
props = parseProps(propsData)
propsData.scopedSlots && (children = propsData.scopedSlots)
} else if (typeof propsData === 'string' || Array.isArray(propsData)) {
childData = propsData
}
if (typeof childData === 'string' || Array.isArray(childData))
children = typeof component !== 'string' || customElement ? () => childData : childData
return hooks.h(type, props, children)
}
export const createComponentFn = (design) => {
return ({ component, propsData, el }) => {
const comp = Object.assign(component, { provide: { [design.configKey]: design.configInstance } })
const vnode = hooks.createVNode(comp, propsData)
hooks.render(vnode, el)
return createVm({}, vnode.component)
}
}
export const defineComponent = hooks.defineComponent
export default hooks
export const isVue2 = false
export const isVue3 = true
export const isVnode = hooks.isVNode
export const KeepAlive = hooks.KeepAlive
export type {
PropType,
ExtractPropTypes,
DefineComponent,
ComponentPublicInstance,
SetupContext,
ComputedRef
} from 'vue'

View File

@ -918,7 +918,8 @@
"type": "component", "type": "component",
"exclude": false, "exclude": false,
"mode": [ "mode": [
"pc" "pc",
"mobile-first"
] ]
}, },
"DividerPc": { "DividerPc": {
@ -926,6 +927,11 @@
"type": "template", "type": "template",
"exclude": false "exclude": false
}, },
"DividerMobileFirst": {
"path": "vue/src/divider/src/mobile-first.vue",
"type": "template",
"exclude": false
},
"Drawer": { "Drawer": {
"path": "vue/src/drawer/index.ts", "path": "vue/src/drawer/index.ts",
"type": "component", "type": "component",

View File

@ -56,6 +56,10 @@ export const computedStyle =
} }
} }
if (props.customStyle) {
style = Object.assign(style, props.customStyle)
}
return style return style
} }
@ -111,9 +115,9 @@ export const watchVisible =
nextTick(() => state.key++) nextTick(() => state.key++)
} }
if (props.rightSlide) { if (props.rightSlide && state.current !== 'default') {
const dialogBoxDom = el.querySelector(constants.DIALOG_BOX_CLASS) || el const selector = `[data-tag=${constants.DIALOG_BOX_DATA_TAG}]`
dialogBoxDom.style.left = '' props.rightSlide && (el.querySelector(selector).style.left = '')
} }
} }
} }

View File

@ -0,0 +1,57 @@
import type { IDividerStyle, IDividerRenderlessParams } from '@/types'
export const computedRootStyle =
({ state, props }: Pick<IDividerRenderlessParams, 'state' | 'props'>) =>
(): IDividerStyle => {
return {
height: props.height || state.height,
margin: props.margin || state.margin,
fontSize: props.fontSize
}
}
export const computedLineStyle =
({ props }: Pick<IDividerRenderlessParams, 'props'>) =>
(): IDividerStyle => {
const lineStyle = {
borderTopStyle: props.borderStyle
}
if (props.color) {
Object.assign(lineStyle, { borderTopColor: props.color })
}
return lineStyle
}
export const computedTextStyle =
({ props }: Pick<IDividerRenderlessParams, 'props'>) =>
(): IDividerStyle => {
const textStyle = {
left: 'unset',
right: 'unset'
}
const defaultOffset = '5%'
if (props.contentPosition === 'left') {
textStyle.left = props.offset || defaultOffset
} else if (props.contentPosition === 'right') {
textStyle.right = props.offset || defaultOffset
}
return textStyle
}
export const setDividerHeight =
({ state, props, vm, nextTick }: Pick<IDividerRenderlessParams, 'state' | 'props' | 'vm' | 'nextTick'>) =>
(): void => {
const verticalHeight = '12px'
if (props.direction === 'vertical') {
state.height = props.height || verticalHeight
} else {
nextTick(() => {
const offsetHeight = vm.$refs.text && vm.$refs.text.offsetHeight
state.height = props.height || offsetHeight ? offsetHeight + 'px' : 'auto'
})
}
}

View File

@ -1,5 +1,42 @@
export const api = [] import type {
IDividerApi,
IDividerProps,
IDividerRenderlessParamUtils,
IDividerState,
ISharedRenderlessParamHooks
} from '@/types'
import { computedLineStyle, computedTextStyle, computedRootStyle, setDividerHeight } from './index'
export const api = ['state']
export const renderless = (
props: IDividerProps,
{ reactive, onMounted, computed }: ISharedRenderlessParamHooks,
{ vm, nextTick }: IDividerRenderlessParamUtils
) => {
const defaultMargin: string = '16px 0'
const verticalMargin: string = '0 8px'
const api = {} as IDividerApi
const state: IDividerState = reactive({
height: 'auto',
margin: props.direction === 'vertical' ? verticalMargin : defaultMargin,
lineStyle: computed(() => api.computedLineStyle()),
textStyle: computed(() => api.computedTextStyle()),
rootStyle: computed(() => api.computedRootStyle())
})
Object.assign(api, {
state,
computedLineStyle: computedLineStyle({ props }),
computedTextStyle: computedTextStyle({ props }),
computedRootStyle: computedRootStyle({ props, state }),
setDividerHeight: setDividerHeight({ props, state, vm, nextTick })
})
onMounted(() => {
api.setDividerHeight()
})
export const renderless = () => {
return api return api
} }

View File

@ -177,7 +177,7 @@ export const initAria =
} }
} }
const toggleFocus = export const toggleFocus =
({ state, value }) => ({ state, value }) =>
() => { () => {
state.focusing = value state.focusing = value
@ -194,9 +194,9 @@ export const initEvent =
state.dropdownElm?.addEventListener('keydown', api.handleItemKeyDown, true) state.dropdownElm?.addEventListener('keydown', api.handleItemKeyDown, true)
if (!props.splitButton || !props.singleButton) { if (!props.splitButton || !props.singleButton) {
on(state.triggerElm, 'focus', toggleFocus({ state, value: true })) on(state.triggerElm, 'focus', api.toggleFocusOnTrue)
on(state.triggerElm, 'blur', toggleFocus({ state, value: false })) on(state.triggerElm, 'blur', api.toggleFocusOnFalse)
on(state.triggerElm, 'click', toggleFocus({ state, value: false })) on(state.triggerElm, 'click', api.toggleFocusOnFalse)
} }
if (state.trigger === 'hover') { if (state.trigger === 'hover') {
@ -270,9 +270,9 @@ export const beforeDistory =
() => { () => {
if (state.triggerElm) { if (state.triggerElm) {
off(state.triggerElm, 'keydown', api.handleTriggerKeyDown) off(state.triggerElm, 'keydown', api.handleTriggerKeyDown)
off(state.triggerElm, 'focus', toggleFocus({ state, value: true })) off(state.triggerElm, 'focus', api.toggleFocusOnTrue)
off(state.triggerElm, 'blur', toggleFocus({ state, value: false })) off(state.triggerElm, 'blur', api.toggleFocusOnFalse)
off(state.triggerElm, 'click', toggleFocus({ state, value: false })) off(state.triggerElm, 'click', api.toggleFocusOnFalse)
off(state.triggerElm, 'mouseenter', api.show) off(state.triggerElm, 'mouseenter', api.show)
off(state.triggerElm, 'mouseleave', api.hide) off(state.triggerElm, 'mouseleave', api.hide)
off(state.triggerElm, 'click', api.handleClick) off(state.triggerElm, 'click', api.handleClick)

View File

@ -36,14 +36,15 @@ import {
initDomOperation, initDomOperation,
mounted, mounted,
beforeDistory, beforeDistory,
clickOutside clickOutside,
toggleFocus
} from './index' } from './index'
export const api = ['state', 'handleMainButtonClick', 'hide', 'show', 'initDomOperation', 'handleClick', 'clickOutside'] export const api = ['state', 'handleMainButtonClick', 'hide', 'show', 'initDomOperation', 'handleClick', 'clickOutside']
export const renderless = ( export const renderless = (
props: IDropdownProps, props: IDropdownProps,
{ reactive, watch, provide, onMounted, computed }: ISharedRenderlessParamHooks, { reactive, watch, provide, onMounted, computed, onBeforeUnmount }: ISharedRenderlessParamHooks,
{ emit, parent, broadcast, vm, nextTick, mode, designConfig }: IDropdownRenderlessParamUtils { emit, parent, broadcast, vm, nextTick, mode, designConfig }: IDropdownRenderlessParamUtils
): IDropdownApi => { ): IDropdownApi => {
const api = {} as IDropdownApi const api = {} as IDropdownApi
@ -85,13 +86,16 @@ export const renderless = (
triggerElmFocus: triggerElmFocus(state), triggerElmFocus: triggerElmFocus(state),
initDomOperation: initDomOperation({ api, state, vm }), initDomOperation: initDomOperation({ api, state, vm }),
beforeDistory: beforeDistory({ vm, api, state }), beforeDistory: beforeDistory({ vm, api, state }),
clickOutside: clickOutside({ props, api }) clickOutside: clickOutside({ props, api }),
toggleFocusOnTrue: toggleFocus({ state, value: true }),
toggleFocusOnFalse: toggleFocus({ state, value: false })
}) })
watch(() => state.visible, api.watchVisible) watch(() => state.visible, api.watchVisible)
watch(() => state.focusing, api.watchFocusing) watch(() => state.focusing, api.watchFocusing)
onMounted(api.mounted) onMounted(api.mounted)
onBeforeUnmount(api.beforeDistory)
return api return api
} }

View File

@ -1511,7 +1511,7 @@ export const downloadFileSingleInner =
export const getDownloadFileInfo = export const getDownloadFileInfo =
({ api, state, props, service }: Pick<IFileUploadRenderlessParams, 'api' | 'state' | 'props' | 'service'>) => ({ api, state, props, service }: Pick<IFileUploadRenderlessParams, 'api' | 'state' | 'props' | 'service'>) =>
({ docId }: { docId: string }) => { ({ docId, docVersion }: { docId: string; docVersion: string }) => {
return service.getDocumentInfoUrl().then((url) => { return service.getDocumentInfoUrl().then((url) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
service service

View File

@ -116,7 +116,7 @@ export const renderless = (
onBeforeUnmount(unbindDialogEvent) onBeforeUnmount(unbindDialogEvent)
watch(() => props.rules, api.watchRules) watch(() => props.rules, api.watchRules, { deep: props.validateOnRuleChange === 'deep' })
return api return api
} }

View File

@ -1,14 +1,14 @@
import { import {
Active,
closeTablePanel,
eventClick,
eventImg,
handleChange, handleChange,
setLink, setLink,
tableMouseMove,
tableChoose,
toggleTablePanel,
closeTablePanel,
shouldShow, shouldShow,
eventImg, tableChoose,
eventClick, tableMouseMove,
Active toggleTablePanel
} from './index' } from './index'
import type { ISharedRenderlessParamHooks } from '@/types' import type { ISharedRenderlessParamHooks } from '@/types'
@ -261,11 +261,11 @@ export const renderless = (
onDestroy() { onDestroy() {
// The editor is being destroyed. // The editor is being destroyed.
emit('destroy') emit('destroy')
}, }
...props.options
} }
let options = Object.assign(defaultOptions, props.options) let options = { ...defaultOptions, ...props.options }
options.extensions = [...new Set([...defaultOptions.extensions, ...(props.options.extensions || [])])]
const state = reactive({ const state = reactive({
editor: new Editor(options), editor: new Editor(options),

View File

@ -572,6 +572,11 @@ export const handleFocus =
if (!state.willFocusRun) return // 立即触发了blur,则不执行focus了 if (!state.willFocusRun) return // 立即触发了blur,则不执行focus了
if (!state.softFocus) { if (!state.softFocus) {
// tiny 新增 shape条件: 防止过滤器模式且filterable时 面板无法关闭的bug
if (props.shape === 'filter') {
return
}
if (props.automaticDropdown || props.filterable || props.searchable) { if (props.automaticDropdown || props.filterable || props.searchable) {
state.visible = true state.visible = true
state.softFocus = true state.softFocus = true

View File

@ -27,6 +27,12 @@ export const initData =
} }
} }
export const getTree =
({ vm }) =>
() => {
return vm.$refs.tree
}
export const setMenuKey = export const setMenuKey =
(api: ITreeMenuApi) => (api: ITreeMenuApi) =>
({ newData, menuData }: { newData: ITreeMenuNewData[]; menuData: ITreeMenuData[] }) => { ({ newData, menuData }: { newData: ITreeMenuNewData[]; menuData: ITreeMenuData[] }) => {

View File

@ -35,7 +35,8 @@ import {
setCurrentNode, setCurrentNode,
getCurrentNode, getCurrentNode,
handleToggleMenu, handleToggleMenu,
computedTreeStyle computedTreeStyle,
getTree
} from './index' } from './index'
import type { import type {
ISharedRenderlessParamUtils, ISharedRenderlessParamUtils,
@ -70,7 +71,8 @@ export const api = [
'getCurrentKey', 'getCurrentKey',
'setCurrentNode', 'setCurrentNode',
'getCurrentNode', 'getCurrentNode',
'handleToggleMenu' 'handleToggleMenu',
'getTree'
] ]
export const renderless = ( export const renderless = (
@ -121,6 +123,7 @@ export const renderless = (
getCurrentKey: getCurrentKey({ vm }), getCurrentKey: getCurrentKey({ vm }),
setCurrentNode: setCurrentNode({ vm }), setCurrentNode: setCurrentNode({ vm }),
getCurrentNode: getCurrentNode({ vm }), getCurrentNode: getCurrentNode({ vm }),
getTree: getTree({ vm }),
handleToggleMenu: handleToggleMenu({ state, vm }), handleToggleMenu: handleToggleMenu({ state, vm }),
computedTreeStyle: computedTreeStyle({ props }) computedTreeStyle: computedTreeStyle({ props })
}) })

View File

@ -28,7 +28,7 @@ export const getIsDisabled =
({ props }) => ({ props }) =>
(year) => { (year) => {
return props.selectionMode.startsWith('year') && typeof props.disabledDate === 'function' return props.selectionMode.startsWith('year') && typeof props.disabledDate === 'function'
? props.disabledDate(year) ? props.disabledDate(new Date(year, 0, 1, 0))
: false : false
} }
@ -87,7 +87,7 @@ export const getRows =
cell.text = year cell.text = year
cell.type = isToday ? DATEPICKER.Today : DATEPICKER.Normal cell.type = isToday ? DATEPICKER.Today : DATEPICKER.Normal
if (props.selectionMode.startsWith('year')) { if (props.selectionMode.startsWith('year')) {
cell.disabled = typeof disabledDate === 'function' && disabledDate(year) cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(year, 0, 1, 0))
} }
if (selectionMode === DATEPICKER.YearRange) { if (selectionMode === DATEPICKER.YearRange) {

View File

@ -0,0 +1,41 @@
import type { ExtractPropTypes } from 'vue'
import type { dividerProps } from '@/divider/src'
import type { computedLineStyle, computedTextStyle, computedRootStyle, setDividerHeight } from '../src/divider'
import type { ISharedRenderlessFunctionParams, ISharedRenderlessParamUtils } from './shared.type'
export interface IDividerState {
height: string
margin: string
lineStyle: IDividerStyle
textStyle: IDividerStyle
rootStyle: IDividerStyle
}
export type IDividerProps = ExtractPropTypes<typeof dividerProps>
export interface IDividerApi {
state: IDividerState
computedLineStyle: ReturnType<typeof computedLineStyle>
computedTextStyle: ReturnType<typeof computedTextStyle>
computedRootStyle: ReturnType<typeof computedRootStyle>
setDividerHeight: ReturnType<typeof setDividerHeight>
}
export type IDividerRenderlessParams = ISharedRenderlessFunctionParams<never> & {
api: IDividerApi
state: IDividerState
props: IDividerProps
}
export type IDividerRenderlessParamUtils = ISharedRenderlessParamUtils<never>
export interface IDividerStyle {
margin?: string | number
height?: string | number
fontSize?: string | number
right?: string | number
left?: string | number
borderLeftStyle?: string | number
borderTopStyle?: string | number
borderTopColor?: string | number
}

View File

@ -50,6 +50,8 @@ export interface IDropdownApi {
initDomOperation: () => void initDomOperation: () => void
beforeDistory: () => void beforeDistory: () => void
clickOutside: () => void clickOutside: () => void
toggleFocusOnTrue: () => void
toggleFocusOnFalse: () => void
} }
export type IDropdownRenderlessParams = ISharedRenderlessFunctionParams<null> & { export type IDropdownRenderlessParams = ISharedRenderlessFunctionParams<null> & {

View File

@ -82,6 +82,7 @@ export * from './dropdown-item.type'
export * from './dropdown-menu.type' export * from './dropdown-menu.type'
export * from './dynamic-scroller.type' export * from './dynamic-scroller.type'
export * from './dynamic-scroller-item.type' export * from './dynamic-scroller-item.type'
export * from './divider.type'
export * from './espace.type' export * from './espace.type'
export * from './exception.type' export * from './exception.type'
export * from './fall-menu.type' export * from './fall-menu.type'

View File

@ -23,7 +23,10 @@ import type {
setCurrentKey, setCurrentKey,
getCurrentKey, getCurrentKey,
setCurrentNode, setCurrentNode,
getCurrentNode getCurrentNode,
handleToggleMenu,
computedTreeStyle,
getTree
} from '../src/tree-menu' } from '../src/tree-menu'
import type { ISharedRenderlessParamUtils } from './shared.type' import type { ISharedRenderlessParamUtils } from './shared.type'
@ -51,6 +54,7 @@ export interface ITreeMenuApi {
currentChange: ReturnType<typeof currentChange> currentChange: ReturnType<typeof currentChange>
watchFilterText: ReturnType<typeof watchFilterText> watchFilterText: ReturnType<typeof watchFilterText>
getTitle: ReturnType<typeof getTitle> getTitle: ReturnType<typeof getTitle>
getTree: ReturnType<typeof getTree>
setMenuKey: ReturnType<typeof setMenuKey> setMenuKey: ReturnType<typeof setMenuKey>
initData: ReturnType<typeof initData> initData: ReturnType<typeof initData>
collapseChange: ReturnType<typeof collapseChange> collapseChange: ReturnType<typeof collapseChange>
@ -60,6 +64,8 @@ export interface ITreeMenuApi {
getCurrentKey: ReturnType<typeof getCurrentKey> getCurrentKey: ReturnType<typeof getCurrentKey>
setCurrentNode: ReturnType<typeof setCurrentNode> setCurrentNode: ReturnType<typeof setCurrentNode>
getCurrentNode: ReturnType<typeof getCurrentNode> getCurrentNode: ReturnType<typeof getCurrentNode>
handleToggleMenu: ReturnType<typeof handleToggleMenu>
computedTreeStyle: ReturnType<typeof computedTreeStyle>
} }
export interface ITreeMenuData { export interface ITreeMenuData {

View File

@ -152,6 +152,7 @@
.@{modal-prefix-cls}__body { .@{modal-prefix-cls}__body {
white-space: normal; white-space: normal;
word-break: break-word; word-break: break-word;
overflow: auto;
} }
} }

View File

@ -0,0 +1,71 @@
@import '../custom.less';
@divider-prefix-cls: ~'@{css-prefix}divider';
.@{divider-prefix-cls} {
@apply h-auto;
@apply flex;
@apply items-center;
@apply justify-center;
@apply relative;
@apply w-full;
@apply overflow-hidden;
& &--default,
&&--default {
@apply border-color-border;
}
& &--info,
&&--info {
@apply border-color-brand;
}
& &--error,
&&--error {
@apply border-color-error;
}
& &--warning,
&&--warning {
@apply border-color-warning;
}
& &--success,
&&--success {
@apply border-color-success;
}
&--horizontal {
@apply mt-24 mr-0 mb-24 ml-0;
}
&--vertical {
@apply inline-block;
@apply w-1;
@apply align-middle;
@apply mt-0 mr-8 mb-0 ml-8;
height: 1em;
@apply border-l border-l-color-border;
}
&__text {
@apply absolute;
@apply font-medium;
@apply pt-0 pr-14 pb-0 pl-14;
@apply text-color-text-primary;
@apply bg-color-bg-1;
}
.@{divider-prefix-cls}-line {
@apply h-px;
@apply w-full;
@apply border-t;
}
.@{divider-prefix-cls}-text {
@apply absolute;
@apply pt-0 pr-14 pb-0 pl-14;
@apply bg-white;
}
}

View File

@ -5,6 +5,8 @@
@custom-prefix-cls: ~'@{css-prefix}custom'; @custom-prefix-cls: ~'@{css-prefix}custom';
.@{modal-prefix-cls} { .@{modal-prefix-cls} {
overflow-wrap: break-word;
&__wrapper { &__wrapper {
@apply hidden; @apply hidden;
@apply fixed; @apply fixed;
@ -141,8 +143,7 @@
&.type__confirm { &.type__confirm {
.@{modal-prefix-cls}__body { .@{modal-prefix-cls}__body {
@apply whitespace-normal; @apply whitespace-normal;
word-wrap: break-word; word-break: break-word;
@apply break-all;
@apply overflow-auto; @apply overflow-auto;
} }
} }

View File

@ -97,6 +97,7 @@
@apply whitespace-pre-wrap; @apply whitespace-pre-wrap;
white-space-collapse: preserve-breaks; white-space-collapse: preserve-breaks;
word-wrap: break-word; word-wrap: break-word;
word-break: break-word;
& > * { & > * {
@apply cursor-text; @apply cursor-text;

View File

@ -91,6 +91,12 @@
> span > .tiny-svg { > span > .tiny-svg {
@apply ~'-mt-0.5'; @apply ~'-mt-0.5';
} }
&:first-child {
> .tiny-svg,
> span > .tiny-svg {
margin-top: 1px
}
}
} }
& &__loading { & &__loading {

View File

@ -6,14 +6,43 @@
.@{divider-prefix-cls} { .@{divider-prefix-cls} {
.inject-Divider-vars(); .inject-Divider-vars();
position: relative; position: relative;
height: auto;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
overflow: hidden;
&&--horizontal { & &--default,
display: block; &&--default {
margin: 24px 0; border-color: var(--tv-Divider-default-color);
border-top: 1px solid var(--tv-Divider-dividing-line);
} }
&&--vertical { & &--success,
&&--success {
border-color: var(--tv-Divider-success-color);
}
& &--warning,
&&--warning {
border-color: var(--tv-Divider-warning-color);
}
& &--info,
&&--info {
border-color: var(--tv-Divider-info-color);
}
& &--error,
&&--error {
border-color: var(--tv-Divider-error-color);
}
&--horizontal {
margin: 24px 0;
}
&--vertical {
display: inline-block; display: inline-block;
width: 1px; width: 1px;
height: 1em; height: 1em;
@ -24,24 +53,22 @@
&__text { &__text {
position: absolute; position: absolute;
font-size: 14px;
font-weight: 500; font-weight: 500;
padding: 0 14px;
color: var(--tv-Divider-text-color); color: var(--tv-Divider-text-color);
background-color: var(--tv-Divider-text-bg-color); background-color: var(--tv-Divider-text-bg-color);
line-height: normal;
}
&.is-left { .@{divider-prefix-cls}-line {
left: 20px; height: 1px;
transform: translateY(-50%); width: 100%;
} border-top-width: var(--tv-Divider-border-width);
}
&.is-center { .@{divider-prefix-cls}-text {
left: 50%; position: absolute;
transform: translate(-50%) translateY(-50%); padding: 0 14px;
} background-color: var(--tv-Divider-text-bg-color);
&.is-right {
right: 20px;
transform: translateY(-50%);
}
} }
} }

View File

@ -5,4 +5,16 @@
--tv-Divider-text-bg-color: var(--tv-color-border-divider); --tv-Divider-text-bg-color: var(--tv-color-border-divider);
// 文案文本色 // 文案文本色
--tv-Divider-text-color: var(--tv-color-text); --tv-Divider-text-color: var(--tv-color-text);
// 成功颜色
--tv-Divider-success-color: var(--tv-color-success-border);
// 默认颜色
--tv-Divider-default-color: var(--tv-color-border-divider-short);
// 告警颜色
--tv-Divider-warning-color: var(--tv-color-warn-border);
// 信息颜色
--tv-Divider-info-color: var(--tv-color-info-border);
// 错误颜色
--tv-Divider-error-color: var(--tv-color-error-border);
// 宽度
--tv-Divider-border-width: var(--tv-border-width);
} }

View File

@ -333,7 +333,6 @@ export class Hasher extends BufferedBlockAlgorithm {
if (messageUpdate) { if (messageUpdate) {
this._append(messageUpdate) this._append(messageUpdate)
} }
// @ts-ignore // @ts-ignore
const hash = this._doFinalize() const hash = this._doFinalize()

View File

@ -15,7 +15,7 @@ function editNodes(el: HTMLElement, nodes: HTMLElement[], query: string) {
// 2、处理收集后的节点字符串搜索性能优于正则替换 // 2、处理收集后的节点字符串搜索性能优于正则替换
nodes.forEach((node) => { nodes.forEach((node) => {
const content = node.textContent as string const content = node.textContent as string
const start = content.toLowerCase().indexOf(query.toLowerCase()) const start = content.indexOf(query)
const startText = content.substring(0, start) const startText = content.substring(0, start)
const endText = content.substring(start + query.length) const endText = content.substring(start + query.length)
@ -58,7 +58,7 @@ function edit(el: HTMLElement, query: string) {
} }
const content = node.textContent || '' const content = node.textContent || ''
return content.toLowerCase().includes(query.toLowerCase()) return content.includes(query)
}) })
editNodes(el, matchNodes, query) editNodes(el, matchNodes, query)

View File

@ -194,7 +194,7 @@ export default {
hour: 'hour', hour: 'hour',
minute: 'minute', minute: 'minute',
second: 'second', second: 'second',
to: '', to: '-',
yearMonth: '{month} {year}', yearMonth: '{month} {year}',
yearMonthDay: '{month} {day}, {year}' yearMonthDay: '{month} {day}, {year}'
}, },

View File

@ -21,6 +21,7 @@ export const $constants = {
DIALOG_BOX_CLASS: 'div.tiny-dialog-box', DIALOG_BOX_CLASS: 'div.tiny-dialog-box',
PC_SCROLL_LOCK_CLASS: 'dialog-box__scroll-lock', PC_SCROLL_LOCK_CLASS: 'dialog-box__scroll-lock',
MOBILE_SCROLL_LOCK_CLASS: 'mobile-dialog-box__scroll-lock', MOBILE_SCROLL_LOCK_CLASS: 'mobile-dialog-box__scroll-lock',
DIALOG_BOX_DATA_TAG: 'tiny-dialog-box',
Mode: 'pc', Mode: 'pc',
SCROLL_LOCK_CLASS(mode) { SCROLL_LOCK_CLASS(mode) {
return mode === this.Mode ? this.PC_SCROLL_LOCK_CLASS : this.MOBILE_SCROLL_LOCK_CLASS return mode === this.Mode ? this.PC_SCROLL_LOCK_CLASS : this.MOBILE_SCROLL_LOCK_CLASS
@ -122,6 +123,14 @@ export const dialogBoxProps = {
dialogTransition: { dialogTransition: {
type: String, type: String,
default: '' default: ''
},
noAnimation: {
type: Boolean,
default: false
},
customStyle: {
type: Object,
default: () => ({})
} }
} }

View File

@ -1,7 +1,12 @@
<template> <template>
<div> <div>
<div v-if="state.current !== 'default'"> <div v-if="state.current !== 'default'">
<transition :name="state.animationName" @after-enter="afterEnter" @after-leave="afterLeave"> <transition
:duration="noAnimation ? 0 : undefined"
:name="state.animationName"
@after-enter="afterEnter"
@after-leave="afterLeave"
>
<div <div
v-show="visible" v-show="visible"
:class="['fixed inset-0 m-0 flex items-center', dialogClass]" :class="['fixed inset-0 m-0 flex items-center', dialogClass]"
@ -26,7 +31,7 @@
<div <div
v-if="showHeader" v-if="showHeader"
data-tag="tiny-dialog-box__header" data-tag="tiny-dialog-box__header"
class="px-6 py-4 h-12 leading-4 bg-color-bg-1 flex justify-between items-center border-b border-b-color-bg-3" class="px-6 py-4 leading-5.5 bg-color-bg-1 flex justify-between items-center border-b border-b-color-bg-3"
@mousedown="handleDrag" @mousedown="handleDrag"
> >
<slot name="title"> <slot name="title">
@ -171,7 +176,9 @@ export default defineComponent({
'destroyOnClose', 'destroyOnClose',
'dialogClass', 'dialogClass',
'beforeClose', 'beforeClose',
'maxHeight' 'maxHeight',
'customStyle',
'noAnimation'
], ],
model: { model: {
prop: 'visible', prop: 'visible',

View File

@ -10,7 +10,12 @@
* *
--> -->
<template> <template>
<transition :name="state.animationName" @after-enter="afterEnter" @after-leave="afterLeave"> <transition
:duration="noAnimation ? 0 : undefined"
:name="state.animationName"
@after-enter="afterEnter"
@after-leave="afterLeave"
>
<div <div
v-show="visible" v-show="visible"
:class="['tiny-dialog-box__wrapper', dialogClass]" :class="['tiny-dialog-box__wrapper', dialogClass]"
@ -32,6 +37,8 @@
]" ]"
:style="state.style" :style="state.style"
class="tiny-dialog-box" class="tiny-dialog-box"
data-tag="tiny-dialog-box"
:data-dialog-box-draggable="draggable"
:key="state.key" :key="state.key"
> >
<div v-if="showHeader" ref="header" class="tiny-dialog-box__header" @mousedown="handleDrag"> <div v-if="showHeader" ref="header" class="tiny-dialog-box__header" @mousedown="handleDrag">
@ -134,7 +141,9 @@ export default defineComponent({
'dialogClass', 'dialogClass',
'beforeClose', 'beforeClose',
'maxHeight', 'maxHeight',
'dialogTransition' 'dialogTransition',
'customStyle',
'noAnimation'
], ],
model: { model: {
prop: 'visible', prop: 'visible',

View File

@ -3,44 +3,58 @@ import type { PropType } from '@opentiny/vue-common'
import template from 'virtual-template?pc' import template from 'virtual-template?pc'
export type DirectionType = 'horizontal' | 'vertical' export type DirectionType = 'horizontal' | 'vertical'
export type BorderStyleType = 'dashed' | 'solid' export type BorderStyleType = 'dashed' | 'solid' | 'dotted'
export type ContentPositionType = 'left' | 'right' | 'center' export type ContentPositionType = 'left' | 'right' | 'center'
const $constants = {} export const $constants = {}
export const dividerProps = {
...$props,
_constants: {
type: Object,
default: () => $constants
},
direction: {
type: String as PropType<DirectionType>,
default: 'horizontal'
},
color: {
type: String,
default: ''
},
borderStyle: {
type: String as PropType<BorderStyleType>,
default: 'solid'
},
contentPosition: {
type: String as PropType<ContentPositionType>,
default: 'center'
},
contentColor: {
type: String,
default: ''
},
contentBackgroundColor: {
type: String,
default: ''
},
fontSize: {
type: String,
default: () => '14px'
},
margin: String,
offset: String,
height: String,
status: {
type: String,
values: ['default', 'success', 'warning', 'error', 'info'],
default: () => 'default'
}
}
export default defineComponent({ export default defineComponent({
name: $prefix + 'Divider', name: $prefix + 'Divider',
props: { props: dividerProps,
...$props,
_constants: {
type: Object,
default: () => $constants
},
direction: {
type: String as PropType<DirectionType>,
default: 'horizontal'
},
color: {
type: String,
default: ''
},
borderStyle: {
type: String as PropType<BorderStyleType>,
default: 'solid'
},
contentPosition: {
type: String as PropType<ContentPositionType>,
default: 'center'
},
contentColor: {
type: String,
default: ''
},
contentBackgroundColor: {
type: String,
default: ''
}
},
setup(props, context) { setup(props, context) {
return $setup({ props, context, template }) return $setup({ props, context, template })
} }

View File

@ -0,0 +1,47 @@
<template>
<div
data-tag="aui-divider"
:style="state.rootStyle"
:class="
m(
'overflow-hidden relative',
vertical ? 'inline-block w-px border-l' : 'h-auto flex items-center justify-center w-full',
gcls('status-' + status)
)
"
>
<div
v-if="!vertical"
class="h-px w-full border-t"
:style="state.lineStyle"
:class="[gcls('status-' + status)]"
></div>
<span v-if="!vertical" ref="text" class="absolute py-0 px-3.5 bg-white" :style="state.textStyle">
<slot></slot>
</span>
</div>
</template>
<script lang="ts">
import { renderless, api } from '@opentiny/vue-renderless/divider/vue'
import { setup, props, defineComponent } from '@opentiny/vue-common'
import { classes } from './token'
export default defineComponent({
props: [
...props,
'type',
'vertical',
'lineColor',
'textPosition',
'status',
'margin',
'offset',
'fontSize',
'height'
],
setup(props, context): any {
return setup({ props, context, renderless, api, classes })
}
})
</script>

View File

@ -1,18 +1,24 @@
<template> <template>
<div <div
:class="['tiny-divider', 'tiny-divider--' + direction]" :class="['tiny-divider', 'tiny-divider--' + direction, direction === 'vertical' ? 'tiny-divider--' + status : '']"
:style="{ :style="state.rootStyle"
'border-top-style': direction === 'horizontal' ? borderStyle : '',
'border-left-style': direction === 'vertical' ? borderStyle : '',
'border-color': color
}"
> >
<div
v-if="direction !== 'vertical'"
:style="{
'border-color': color,
...state.lineStyle
}"
:class="['tiny-divider-line', 'tiny-divider--' + status]"
></div>
<div <div
v-if="slots.default" v-if="slots.default"
:class="['tiny-divider__text', 'is-' + contentPosition]" ref="text"
:class="['tiny-divider__text']"
:style="{ :style="{
color: contentColor, color: contentColor,
'background-color': contentBackgroundColor 'background-color': contentBackgroundColor,
...state.textStyle
}" }"
> >
<slot></slot> <slot></slot>
@ -25,7 +31,20 @@ import { renderless, api } from '@opentiny/vue-renderless/divider/vue'
import { props, setup, defineComponent } from '@opentiny/vue-common' import { props, setup, defineComponent } from '@opentiny/vue-common'
export default defineComponent({ export default defineComponent({
props: [...props, 'direction', 'color', 'borderStyle', 'contentPosition', 'contentColor', 'contentBackgroundColor'], props: [
...props,
'direction',
'color',
'borderStyle',
'contentPosition',
'contentColor',
'contentBackgroundColor',
'status',
'margin',
'offset',
'fontSize',
'height'
],
setup(props, context) { setup(props, context) {
return setup({ props, context, renderless, api }) return setup({ props, context, renderless, api })
} }

View File

@ -0,0 +1,7 @@
export const classes = {
'status-default': 'border-color-border',
'status-info': 'border-color-brand',
'status-warning': 'border-color-warning',
'status-success': 'border-color-success',
'status-error': 'border-color-error'
}

View File

@ -33,7 +33,8 @@ export const formProps = {
size: String, size: String,
disabled: Boolean, disabled: Boolean,
validateOnRuleChange: { validateOnRuleChange: {
type: Boolean, type: [Boolean, String],
validator: (value: string | boolean) => typeof value === 'boolean' || ['deep'].includes(value),
default: true default: true
}, },
hideRequiredAsterisk: { hideRequiredAsterisk: {

View File

@ -213,7 +213,9 @@ function defaultEditRender(h, renderOpts, params, context) {
let editorModel = component.model || {} let editorModel = component.model || {}
let modelProps = typeof component === 'string' ? 'value' : editorModel.prop || 'modelValue' let modelProps = typeof component === 'string' ? 'value' : editorModel.prop || 'modelValue'
const key = row[$table.rowId]
let options = { let options = {
key,
class: isTag ? `tiny-grid-default-${component}` : '', class: isTag ? `tiny-grid-default-${component}` : '',
attrs: { attrs: {
formatOpt, formatOpt,

View File

@ -340,7 +340,7 @@ const Methods = {
return new Promise((resolve) => { return new Promise((resolve) => {
this.loadTableData(datas) this.loadTableData(datas)
resolve() resolve()
}).then(this.recalculate) })
}, },
reloadRow(row, record, field) { reloadRow(row, record, field) {
let { tableData, tableSourceData } = this let { tableData, tableSourceData } = this
@ -1627,14 +1627,10 @@ const Methods = {
}, },
// 处理x轴方向虚拟滚动列数据加载 // 处理x轴方向虚拟滚动列数据加载
updateScrollXData() { updateScrollXData() {
let { scrollXLoad, scrollXStore, tableColumn, treeConfig, visibleColumn, visibleColumnChanged, columnStore } = this let { scrollXStore } = this
let { lastStartIndex = -1, renderSize, startIndex } = scrollXStore
let args = { lastStartIndex, renderSize, scrollXLoad, startIndex, tableColumn, columnStore }
Object.assign(args, { treeConfig, visibleColumn, visibleColumnChanged })
// 获取需要渲染的列数和最后一次渲染列的index值 // 获取需要渲染的列数和最后一次渲染列的index值
let ret = sliceVisibleColumn(args) let ret = sliceVisibleColumn(this)
if (ret.sliced) { if (ret.sliced) {
// 更新DOM样式保证表格滚动时的对齐初始化表格时也需要计算x轴方向滚动条占位符的尺寸 // 更新DOM样式保证表格滚动时的对齐初始化表格时也需要计算x轴方向滚动条占位符的尺寸
@ -2076,6 +2072,7 @@ const Methods = {
lastScrollLeft = Math.min(lastScrollLeft, maxScrollLeft) lastScrollLeft = Math.min(lastScrollLeft, maxScrollLeft)
fastdom.mutate(() => { fastdom.mutate(() => {
this.restoreScollFlag = true
this.scrollTo(lastScrollLeft, lastScrollTop) this.scrollTo(lastScrollLeft, lastScrollTop)
scrollXLoad && this.triggerScrollXEvent() scrollXLoad && this.triggerScrollXEvent()

View File

@ -204,9 +204,10 @@ const getTreeShowKey = ({ scrollYLoad, treeConfig }) => {
return treeShowKey return treeShowKey
} }
const sliceVisibleColumn = (args) => { const sliceVisibleColumn = (tableVm) => {
const { lastStartIndex, renderSize, scrollXLoad, startIndex, tableColumn, columnStore } = args const { treeConfig, visibleColumn, tableColumn, columnStore, visibleColumnChanged, scrollXLoad, scrollXStore } =
const { treeConfig, visibleColumn, visibleColumnChanged } = args tableVm
const { lastStartIndex = -1, renderSize, startIndex } = scrollXStore
const { leftList, rightList } = columnStore const { leftList, rightList } = columnStore
let tableColumn2 = tableColumn let tableColumn2 = tableColumn
@ -215,7 +216,8 @@ const sliceVisibleColumn = (args) => {
let sliced = false let sliced = false
if (scrollXLoad && treeConfig) { if (scrollXLoad && treeConfig) {
if (visibleColumnChanged || !~lastStartIndex || lastStartIndex !== startIndex) { if (visibleColumnChanged || !~lastStartIndex || lastStartIndex !== startIndex || tableVm.restoreScollFlag) {
tableVm.restoreScollFlag = false
tableColumn2 = visibleColumn.slice(startIndex, startIndex + renderSize) tableColumn2 = visibleColumn.slice(startIndex, startIndex + renderSize)
lastStartIndex2 = startIndex lastStartIndex2 = startIndex
visibleColumnChanged2 = false visibleColumnChanged2 = false

View File

@ -85,7 +85,7 @@
'w-full border-0 sm:border px-0 sm:px-3 sm:border-solid sm:border-color-border sm:hover:border-color-border-hover ' + 'w-full border-0 sm:border px-0 sm:px-3 sm:border-solid sm:border-color-border sm:hover:border-color-border-hover ' +
'sm:focus:border-color-brand-focus sm:disabled:border-color-border-separator ' + 'sm:focus:border-color-brand-focus sm:disabled:border-color-border-separator ' +
'placeholder:text-color-text-placeholder placeholder:text-sm sm:disabled:placeholder:text-color-text-disabled text-sm text-color-text-primary ' + 'placeholder:text-color-text-placeholder placeholder:text-sm sm:disabled:placeholder:text-color-text-disabled text-sm text-color-text-primary ' +
'bg-color-bg-1 disabled:cursor-not-allowed disabled:text-color-text-disabled sm:disabled:text-color-text-secondary ' + 'bg-color-bg-1 disabled:cursor-not-allowed disabled:text-color-text-disabled sm:disabled:text-color-text-disabled ' +
'sm:disabled:bg-color-bg-6 py-0 outline-0 transition-colors duration-200 ease-in-out ', 'sm:disabled:bg-color-bg-6 py-0 outline-0 transition-colors duration-200 ease-in-out ',
state.inputSizeMf === 'medium' state.inputSizeMf === 'medium'
? `h-8 leading-8 ${m('sm:text-sm')} placeholder:text-sm` ? `h-8 leading-8 ${m('sm:text-sm')} placeholder:text-sm`
@ -323,7 +323,7 @@
ref="textarea" ref="textarea"
v-bind="a($attrs, ['type', 'class', 'style', '^on[A-Z]'])" v-bind="a($attrs, ['type', 'class', 'style', '^on[A-Z]'])"
:tabindex="tabindex" :tabindex="tabindex"
class="block w-full border-0 sm:border-solid sm:border-color-border sm:hover:border-color-border-hover sm:focus:border-color-brand-focus sm:disabled:border-color-border-separator outline-0 rounded placeholder:text-color-text-placeholder placeholder:text-sm sm:disabled:placeholder:text-color-text-disabled text-sm text-color-text-primary bg-color-bg-1 disabled:cursor-not-allowed disabled:text-color-text-disabled sm:disabled:text-color-text-secondary sm:disabled:bg-color-bg-6" class="block w-full border-0 sm:border-solid sm:border-color-border sm:hover:border-color-border-hover sm:focus:border-color-brand-focus sm:disabled:border-color-border-separator outline-0 rounded placeholder:text-color-text-placeholder placeholder:text-sm sm:disabled:placeholder:text-color-text-disabled text-sm text-color-text-primary bg-color-bg-1 disabled:cursor-not-allowed disabled:text-color-text-disabled sm:disabled:text-color-text-disabled sm:disabled:bg-color-bg-6"
:class="[ :class="[
readonly ? 'sm:border-0 px-0 py-0' : 'sm:border px-3 ', readonly ? 'sm:border-0 px-0 py-0' : 'sm:border px-3 ',
state.isDisplayOnly ? 'hidden' : '', state.isDisplayOnly ? 'hidden' : '',

View File

@ -412,7 +412,7 @@ export default defineComponent({
? h( ? h(
'div', 'div',
{ {
class: 'flex-auto flex mx-6 mt-4 mb-5 sm:my-6 leading-5 overflow-auto' class: 'flex-auto flex mx-6 mt-4 mb-5 sm:my-6 leading-5 overflow-auto break-normal'
}, },
[ [
status ? h('div', { class: 'hidden sm:block mr-8' }) : null, status ? h('div', { class: 'hidden sm:block mr-8' }) : null,

View File

@ -134,8 +134,9 @@ export const useRule = (props: RuleProps) => {
} }
} }
} }
return { name: field, label: field }
} }
let fieldData = fieldMap?.[field] ?? findFieldData(field, fields) let fieldData = fieldMap?.[field] ? fieldMap[field] : findFieldData(field, fields)
const inputType = fieldData.inputType ?? getInputType(field, operator) const inputType = fieldData.inputType ?? getInputType(field, operator)
const operators = getOperators(field) const operators = getOperators(field)
const operatorObject = getOption(operators, operator) const operatorObject = getOption(operators, operator)

View File

@ -18,6 +18,10 @@
"@opentiny/vue-icon": "workspace:~" "@opentiny/vue-icon": "workspace:~"
}, },
"devDependencies": { "devDependencies": {
"@opentiny-internal/vue-test-utils": "workspace:*",
"vitest": "catalog:"
},
"peerDependencies": {
"@tiptap/core": "~2.1.16", "@tiptap/core": "~2.1.16",
"@tiptap/extension-code-block-lowlight": "~2.1.16", "@tiptap/extension-code-block-lowlight": "~2.1.16",
"@tiptap/extension-collaboration": "~2.1.16", "@tiptap/extension-collaboration": "~2.1.16",
@ -43,8 +47,6 @@
"@tiptap/vue-2": "~2.1.16", "@tiptap/vue-2": "~2.1.16",
"@tiptap/vue-3": "~2.1.16", "@tiptap/vue-3": "~2.1.16",
"highlight.js": "^11.8.0", "highlight.js": "^11.8.0",
"lowlight": "^2.9.0", "lowlight": "^2.9.0"
"@opentiny-internal/vue-test-utils": "workspace:*",
"vitest": "catalog:"
} }
} }

View File

@ -57,7 +57,7 @@
v-if="~['fail'].indexOf(file.status)" v-if="~['fail'].indexOf(file.status)"
class="relative w-full h-full bg-black/50 rounded" class="relative w-full h-full bg-black/50 rounded"
> >
<icon-cue-l-o class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 fill-color-error" /> <icon-cue-l class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 fill-color-error" />
</div> </div>
</div> </div>
<div <div

View File

@ -123,18 +123,18 @@ export default defineComponent({
{state.currentBreakpoint !== 'default' && {state.currentBreakpoint !== 'default' &&
!displayOnly && !displayOnly &&
(promptTip && tipMessage ? ( (promptTip && tipMessage ? (
<div class="hidden sm:inline-flex sm:items-center"> <div class="hidden sm:inline-flex sm:items-center w-full">
{uploadTrigger()} {uploadTrigger()}
<tiny-tooltip effect="light" content={tipMessage} placement="right" popper-options={popperConfig}> <tiny-tooltip effect="light" content={tipMessage} placement="right" popper-options={popperConfig}>
<tiny-icon-help-circle custom-class="ml-2 cursor-pointer fill-color-icon-tertiary"></tiny-icon-help-circle> <tiny-icon-help-circle custom-class="ml-2 cursor-pointer fill-color-icon-tertiary"></tiny-icon-help-circle>
</tiny-tooltip> </tiny-tooltip>
</div> </div>
) : listType === 'text' ? ( ) : listType === 'text' ? (
<div class="hidden sm:inline-flex sm:items-center"> <div class="hidden sm:inline-flex sm:items-center w-full">
{uploadTrigger()} {uploadTrigger()}
<div <div
title={tipMessage} title={typeof tipMessage === 'string' ? tipMessage : ''}
class="hidden sm:block text-xs leading-4 overflow-hidden text-ellipsis whitespace-nowrap text-color-text-placeholder ml-2 cursor-pointer"> class="hidden sm:block text-xs leading-4 overflow-hidden whitespace-nowrap text-ellipsis text-color-text-placeholder ml-2 cursor-pointer">
{tipMessage} {tipMessage}
</div> </div>
</div> </div>