Skip to content
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

feat(steps): [steps] The stateless step style is added to the step bar, and the itemFooter slot is added. #2110

Merged
merged 2 commits into from
Sep 13, 2024
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
15 changes: 15 additions & 0 deletions examples/sites/demos/apis/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,21 @@ export default {
pcDemo: 'slot-item',
mfDemo: ''
},
{
name: 'itemFooter',
type: '',
defaultValue: '',
desc: {
'zh-CN': '步骤条数据项底部插槽,用于条形步骤条',
'en-US': 'Step bar data item bottom slot, used for bar steps'
},
meta: {
stable: '3.19.0'
},
mode: ['pc'],
pcDemo: 'slot-item-footer',
mfDemo: ''
},
{
name: 'prefix',
type: '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<div class="demo-steps-slot-item-footer">
<tiny-button class="mb10" @click="handleChange"
>点击{{ stepsData.length > 3 ? '减少' : '增加' }}一个节点</tiny-button
>
<tiny-steps :data="stepsData" line>
<template #itemFooter="{ slotScope: { links } }">
<div v-if="links">
<tiny-link class="item-link" v-for="link in links" :key="link.title">
<template v-if="link.icon" #icon>
<component :is="link.icon" class="tiny-svg-size"></component>
</template>
{{ link.title }}
</tiny-link>
</div>
</template>
</tiny-steps>
</div>
</template>
chenxi-20 marked this conversation as resolved.
Show resolved Hide resolved

<script setup>
import { ref } from 'vue'
import { Button as TinyButton, Steps as TinySteps, Link as TinyLink } from '@opentiny/vue'
import { IconFilletExternalLink } from '@opentiny/vue-icon'

const stepsData = ref([
{
name: '创建桶(超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题)',
status: 'done',
description:
'桶是存储对象的容器,用于存储您的数据,我这个描述要超出隐藏,我这个描述要超出隐藏,我这个描述要超出隐藏',
links: [{ title: '前往创建' }, { title: '前往创建' }]
},
{ name: '上传文件', status: 'doing', description: '上传您项目相关的文件至桶内存储' },
{
name: '购买资源包(可选)',
description: '资源包是预付费的优惠套餐,可降低存储成本。',
links: [{ title: '了解套餐详情', icon: IconFilletExternalLink() }]
}
])
const handleChange = () => {
if (stepsData.value.length > 3) {
stepsData.value.pop()
} else {
stepsData.value.push({
name: '域名解析',
description: '域名解析的内容'
})
}
}
</script>
Comment on lines +21 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use immutable update patterns in the handleChange method.

The script section looks good overall:

  • The imports are correct.
  • The stepsData ref is defined correctly with an array of step objects.
  • The handleChange method logic is correct. It checks the length of the stepsData array and adds or removes a step accordingly.

However, the handleChange method mutates the stepsData array directly. This is generally not recommended in Vue 3. It's better to use immutable update patterns.

Consider using the spread operator to create a new array instead of mutating the existing one:

const handleChange = () => {
  if (stepsData.value.length > 3) {
-   stepsData.value.pop()
+   stepsData.value = stepsData.value.slice(0, -1)
  } else {
-   stepsData.value.push({
+   stepsData.value = [...stepsData.value, {
      name: '域名解析',
      description: '域名解析的内容'
-   })
+   }]
  }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script setup>
import { ref } from 'vue'
import { Button as TinyButton, Steps as TinySteps, Link as TinyLink } from '@opentiny/vue'
import { IconFilletExternalLink } from '@opentiny/vue-icon'
const stepsData = ref([
{
name: '创建桶(超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题)',
status: 'done',
description:
'桶是存储对象的容器,用于存储您的数据,我这个描述要超出隐藏,我这个描述要超出隐藏,我这个描述要超出隐藏',
links: [{ title: '前往创建' }, { title: '前往创建' }]
},
{ name: '上传文件', status: 'doing', description: '上传您项目相关的文件至桶内存储' },
{
name: '购买资源包(可选)',
description: '资源包是预付费的优惠套餐,可降低存储成本。',
links: [{ title: '了解套餐详情', icon: IconFilletExternalLink() }]
}
])
const handleChange = () => {
if (stepsData.value.length > 3) {
stepsData.value.pop()
} else {
stepsData.value.push({
name: '域名解析',
description: '域名解析的内容'
})
}
}
</script>
<script setup>
import { ref } from 'vue'
import { Button as TinyButton, Steps as TinySteps, Link as TinyLink } from '@opentiny/vue'
import { IconFilletExternalLink } from '@opentiny/vue-icon'
const stepsData = ref([
{
name: '创建桶(超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题)',
status: 'done',
description:
'桶是存储对象的容器,用于存储您的数据,我这个描述要超出隐藏,我这个描述要超出隐藏,我这个描述要超出隐藏',
links: [{ title: '前往创建' }, { title: '前往创建' }]
},
{ name: '上传文件', status: 'doing', description: '上传您项目相关的文件至桶内存储' },
{
name: '购买资源包(可选)',
description: '资源包是预付费的优惠套餐,可降低存储成本。',
links: [{ title: '了解套餐详情', icon: IconFilletExternalLink() }]
}
])
const handleChange = () => {
if (stepsData.value.length > 3) {
stepsData.value = stepsData.value.slice(0, -1)
} else {
stepsData.value = [...stepsData.value, {
name: '域名解析',
description: '域名解析的内容'
}]
}
}
</script>


<style scoped>
.item-link + .item-link {
margin-left: 16px;
}
</style>
69 changes: 69 additions & 0 deletions examples/sites/demos/pc/app/steps/slot-item-footer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div class="demo-steps-slot-item-footer">
<tiny-button class="mb10" @click="handleChange"
>点击{{ stepsData.length > 3 ? '减少' : '增加' }}一个节点</tiny-button
>
<tiny-steps :data="stepsData" line>
<template #itemFooter="{ slotScope: { links } }">
<div v-if="links">
<tiny-link class="item-link" v-for="link in links" :key="link.title">
<template v-if="link.icon" #icon>
<component :is="link.icon" class="tiny-svg-size"></component>
</template>
{{ link.title }}
</tiny-link>
</div>
</template>
</tiny-steps>
</div>
</template>

<script>
import { Button, Steps, Link } from '@opentiny/vue'
import { IconFilletExternalLink } from '@opentiny/vue-icon'

export default {
components: {
TinyButton: Button,
TinySteps: Steps,
TinyLink: Link
},
data() {
return {
stepsData: [
{
name: '创建桶(超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题)',
status: 'done',
description:
'桶是存储对象的容器,用于存储您的数据,我这个描述要超出隐藏,我这个描述要超出隐藏,我这个描述要超出隐藏',
links: [{ title: '前往创建' }, { title: '前往创建' }]
},
{ name: '上传文件', status: 'doing', description: '上传您项目相关的文件至桶内存储' },
{
name: '购买资源包(可选)',
description: '资源包是预付费的优惠套餐,可降低存储成本。',
links: [{ title: '了解套餐详情', icon: IconFilletExternalLink() }]
}
]
}
},
methods: {
handleChange() {
if (this.stepsData.length > 3) {
this.stepsData.pop()
} else {
this.stepsData.push({
name: '域名解析',
description: '域名解析的内容'
})
}
}
}
}
</script>
Comment on lines +21 to +63
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script section looks good! Consider a minor refactor.

The component imports, components option, data property, and handleChange method are all implemented correctly.

As a minor refactor suggestion, consider using array spread syntax in the handleChange method when adding a new step to stepsData to make it more concise:

handleChange() {
  if (this.stepsData.length > 3) {
    this.stepsData.pop()
  } else {
-   this.stepsData.push({
+   this.stepsData = [...this.stepsData, {
      name: '域名解析',
      description: '域名解析的内容'
-   })
+   }]
  }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script>
import { Button, Steps, Link } from '@opentiny/vue'
import { IconFilletExternalLink } from '@opentiny/vue-icon'
export default {
components: {
TinyButton: Button,
TinySteps: Steps,
TinyLink: Link
},
data() {
return {
stepsData: [
{
name: '创建桶(超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题)',
status: 'done',
description:
'桶是存储对象的容器,用于存储您的数据,我这个描述要超出隐藏,我这个描述要超出隐藏,我这个描述要超出隐藏',
links: [{ title: '前往创建' }, { title: '前往创建' }]
},
{ name: '上传文件', status: 'doing', description: '上传您项目相关的文件至桶内存储' },
{
name: '购买资源包(可选)',
description: '资源包是预付费的优惠套餐,可降低存储成本。',
links: [{ title: '了解套餐详情', icon: IconFilletExternalLink() }]
}
]
}
},
methods: {
handleChange() {
if (this.stepsData.length > 3) {
this.stepsData.pop()
} else {
this.stepsData.push({
name: '域名解析',
description: '域名解析的内容'
})
}
}
}
}
</script>
<script>
import { Button, Steps, Link } from '@opentiny/vue'
import { IconFilletExternalLink } from '@opentiny/vue-icon'
export default {
components: {
TinyButton: Button,
TinySteps: Steps,
TinyLink: Link
},
data() {
return {
stepsData: [
{
name: '创建桶(超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题超长标题)',
status: 'done',
description:
'桶是存储对象的容器,用于存储您的数据,我这个描述要超出隐藏,我这个描述要超出隐藏,我这个描述要超出隐藏',
links: [{ title: '前往创建' }, { title: '前往创建' }]
},
{ name: '上传文件', status: 'doing', description: '上传您项目相关的文件至桶内存储' },
{
name: '购买资源包(可选)',
description: '资源包是预付费的优惠套餐,可降低存储成本。',
links: [{ title: '了解套餐详情', icon: IconFilletExternalLink() }]
}
]
}
},
methods: {
handleChange() {
if (this.stepsData.length > 3) {
this.stepsData.pop()
} else {
this.stepsData = [...this.stepsData, {
name: '域名解析',
description: '域名解析的内容'
}]
}
}
}
}
</script>


<style scoped>
.item-link + .item-link {
margin-left: 16px;
}
</style>
14 changes: 13 additions & 1 deletion examples/sites/demos/pc/app/steps/webdoc/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default {
{
demoId: 'slot-item',
name: {
'zh-CN': 'item插槽',
'zh-CN': 'item 插槽',
'en-US': 'item slot'
},
desc: {
Expand All @@ -118,6 +118,18 @@ export default {
},
codeFiles: ['slot-item.vue']
},
{
demoId: 'slot-item-footer',
name: {
'zh-CN': 'itemFooter 插槽',
'en-US': 'itemFooter slot'
},
desc: {
'zh-CN': '<p>通过插槽 <code>itemFooter</code> 自定义节点底部内容为链接按钮。</p>',
'en-US': '<p>Customize the bottom content of the node as a link button through slot<code>itemFooter</code>.</p>'
},
codeFiles: ['slot-item-footer.vue']
},
{
demoId: 'click',
name: {
Expand Down
38 changes: 32 additions & 6 deletions packages/theme/src/steps/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@
color: var(--ti-steps-text-color);

&.vertical {
.@{steps-prefix-cls}-block .description {
.@{steps-prefix-cls}-block .description,
.@{steps-prefix-cls}-block .line-footer {
margin-left: 0;
}
}
Expand Down Expand Up @@ -583,14 +584,30 @@
border-color: var(--ti-steps-disabled-border-color);
}

&.block-mini {
.title,
.description,
.line-footer {
max-width: 150px;
}
}

.title,
.description,
.line-footer {
max-width: 250px;
}

.title {
font-size: var(--ti-steps-font-size-base);
margin-left: var(--ti-steps-text-margin-left);
line-height: calc(var(--ti-steps-font-size-base) + 2px);
line-height: 1.5;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
word-break: break-all;
}

.line {
Expand Down Expand Up @@ -637,10 +654,15 @@
}
}

.description,
.line-footer {
line-height: 1.5;
margin-left: calc(var(--ti-steps-circle-width-height) + var(--ti-steps-text-margin-left));
}

.description {
font-size: 12px;
color: var(--ti-steps-description-text-color);
margin-left: calc(var(--ti-steps-circle-width-height) + var(--ti-steps-text-margin-left));
margin-top: var(--ti-steps-vertical-description-margin-top);
overflow: hidden;
display: -webkit-box;
Expand All @@ -657,10 +679,14 @@
}

&.not-vertical {
margin-top: 0;
margin-top: 10px;
}
}

.line-footer {
margin-top: 8px;
}

&.done .left-line {
border-top: 1px solid var(--ti-steps-line-active-bg-color);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/vue-directive/src/auto-tip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ const tooltipContent = hooks.ref('')

// 判断是否超出隐藏
const isEllipsis = (currentTarget) =>
currentTarget?.textContent && currentTarget.scrollWidth > currentTarget.offsetWidth
currentTarget?.textContent &&
(currentTarget.scrollWidth > currentTarget.clientWidth || currentTarget.scrollHeight > currentTarget.clientHeight)

const isAlwaysShowTip = (currentTarget) => Boolean(currentTarget?.boundingValue?.always)

Expand Down
21 changes: 11 additions & 10 deletions packages/vue/src/steps/package.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
{
"name": "@opentiny/vue-steps",
"type": "module",
"version": "3.18.0",
"description": "",
"license": "MIT",
"sideEffects": false,
"main": "lib/index.js",
"module": "index.ts",
"sideEffects": false,
"type": "module",
"devDependencies": {
"@opentiny-internal/vue-test-utils": "workspace:*",
"vitest": "^0.31.0"
},
"scripts": {
"build": "pnpm -w build:ui $npm_package_name",
"//postversion": "pnpm build"
},
"dependencies": {
"@opentiny/vue-renderless": "workspace:~",
"@opentiny/vue-common": "workspace:~",
"@opentiny/vue-directive": "workspace:~",
"@opentiny/vue-icon": "workspace:~",
"@opentiny/vue-popover": "workspace:~",
"@opentiny/vue-theme": "workspace:~",
"@opentiny/vue-icon": "workspace:~"
"@opentiny/vue-renderless": "workspace:~",
"@opentiny/vue-theme": "workspace:~"
},
"license": "MIT"
"devDependencies": {
"@opentiny-internal/vue-test-utils": "workspace:*",
"vitest": "^0.31.0"
}
}
13 changes: 10 additions & 3 deletions packages/vue/src/steps/src/pc/pc-line.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
node[statusField] === 'error' ? 'fault' : node[statusField],
{ 'active': index === active },
{ 'not-vertical': !vertical },
{ 'flex-non': index === data.length - 1 && !vertical }
{ 'flex-non': index === data.length - 1 && !vertical },
{ 'block-mini': data.length > 3 }
]"
@click="$emit('click', index, node, $event)"
>
Expand Down Expand Up @@ -73,7 +74,7 @@
</template>
</div>
<!-- title1 -->
<div v-if="!vertical" :title="node[nameField]" class="title">
<div v-if="!vertical" v-auto-tip="{ placement: 'bottom' }" class="title">
{{ node[nameField] }}
</div>
<div
Expand All @@ -88,7 +89,7 @@

<slot name="item" :slot-scope="node" :index="index">
<!-- title2 -->
<div :title="node[nameField]" :class="['title-vertical', { 'not-vertical': !vertical }]">
<div v-auto-tip="{ placement: 'bottom' }" :class="['title-vertical', { 'not-vertical': !vertical }]">
{{ node[nameField] }}
</div>
<!-- description -->
Expand All @@ -99,9 +100,13 @@
node[statusField] === 'error' ? 'fault' : node[statusField],
{ 'not-vertical': !vertical }
]"
v-auto-tip="{ placement: 'bottom' }"
>
{{ node[descriptionField] }}
</div>
<div class="line-footer" v-if="slots.itemFooter">
<slot class="line-footer" name="itemFooter" :slot-scope="node" :index="index"></slot>
</div>
</slot>
</div>
<!-- hidden right step section -->
Expand Down Expand Up @@ -138,12 +143,14 @@
import { renderless, api } from '@opentiny/vue-renderless/steps/vue'
import { props, setup, defineComponent } from '@opentiny/vue-common'
import { IconFinish, IconWarn } from '@opentiny/vue-icon'
import { AutoTip } from '@opentiny/vue-directive'

export default defineComponent({
components: {
IconFinish: IconFinish(),
IconWarn: IconWarn()
},
directives: { AutoTip }, // 新规范
emits: ['click'],
props: [...props, 'vertical', 'nameField', 'statusField', 'data', 'active', 'visibleNum', 'descriptionField', 'size'],
setup(props: any, context: any) {
Expand Down
Loading