diff --git a/docs/demos/sender/actions-config-basic.vue b/docs/demos/sender/actions-config-basic.vue index 231e368e6..0407d6ce8 100644 --- a/docs/demos/sender/actions-config-basic.vue +++ b/docs/demos/sender/actions-config-basic.vue @@ -49,10 +49,3 @@ const handleSubmit = (text: string) => { color: #606266; } - - diff --git a/docs/src/components/sender.md b/docs/src/components/sender.md index e78fb9d9c..cf98caf50 100644 --- a/docs/src/components/sender.md +++ b/docs/src/components/sender.md @@ -724,11 +724,16 @@ Sender 组件提供了丰富的 CSS 变量用于自定义样式。 | -------------------------------- | ------------ | | `--tr-sender-button-size` | 按钮尺寸 | | `--tr-sender-button-size-submit` | 提交按钮尺寸 | +| `--tr-sender-tooltip-gap` | 按钮 Tooltip 与触发按钮的间距 | :::tip 尺寸变体 所有变量都支持通过 `size` 属性自动切换。当 `size="small"` 时,组件会使用对应的 `-small` 变体(如 `--tr-sender-font-size-small`)。 ::: +:::tip Tooltip 间距 +Sender 按钮的 Tooltip 间距可通过 `--tr-sender-tooltip-gap` 全局自定义,默认值为 `8px`。由于 Tooltip 弹层通常挂载在全局层级,建议在 `:root` 或全局主题作用域下设置该变量。 +::: + :::tip 使用示例 ```css /* 自定义背景色 */ @@ -742,6 +747,11 @@ Sender 组件提供了丰富的 CSS 变量用于自定义样式。 --tr-sender-button-size: 40px; --tr-sender-button-size-submit: 44px; } + +/* 全局调整 Sender 按钮 Tooltip 间距 */ +:root { + --tr-sender-tooltip-gap: 10px; +} ``` ::: @@ -789,4 +799,4 @@ Sender 组件提供了丰富的 CSS 变量用于自定义样式。 |-------|----------| | startSpeech | 使用 `VoiceButton.start()` | | stopSpeech | 使用 `VoiceButton.stop()` | -| activateTemplateFirstField | 自动处理,无需调用 | \ No newline at end of file +| activateTemplateFirstField | 自动处理,无需调用 | diff --git a/packages/components/src/sender-actions/action-button/index.vue b/packages/components/src/sender-actions/action-button/index.vue index 4e1eca529..94673b216 100644 --- a/packages/components/src/sender-actions/action-button/index.vue +++ b/packages/components/src/sender-actions/action-button/index.vue @@ -86,3 +86,11 @@ const sizeStyle = computed(() => { } } + + diff --git a/packages/components/src/sender-actions/clear-button/index.vue b/packages/components/src/sender-actions/clear-button/index.vue index a5394a63c..a4c41585b 100644 --- a/packages/components/src/sender-actions/clear-button/index.vue +++ b/packages/components/src/sender-actions/clear-button/index.vue @@ -3,7 +3,6 @@ import { computed } from 'vue' import { useSenderContext } from '../../sender/context' import { IconClear } from '@opentiny/tiny-robot-svgs' import ActionButton from '../action-button/index.vue' -import { normalizeTooltipContent } from '../utils/tooltip' // 从 Context 读取状态和配置 const { hasContent, clearable, clear, loading, defaultActions } = useSenderContext() @@ -18,7 +17,7 @@ const isDisabled = computed(() => { return false }) -const tooltipRenderFn = computed(() => normalizeTooltipContent(defaultActions.value?.clear?.tooltip)) +const tooltip = computed(() => defaultActions.value?.clear?.tooltip) const tooltipPlacement = computed(() => defaultActions.value?.clear?.tooltipPlacement ?? 'top') @@ -46,7 +45,7 @@ const handleClick = () => { v-if="show" :icon="IconClear" :disabled="isDisabled" - :tooltip="tooltipRenderFn" + :tooltip="tooltip" :tooltip-placement="tooltipPlacement" @click="handleClick" /> diff --git a/packages/components/src/sender-actions/styles/tooltip.less b/packages/components/src/sender-actions/styles/tooltip.less new file mode 100644 index 000000000..5eadb012a --- /dev/null +++ b/packages/components/src/sender-actions/styles/tooltip.less @@ -0,0 +1,59 @@ +.tr-sender-tooltip-gap-mixin() { + &[data-popper-placement^='top'], + &[x-placement^='top'], + &[data-placement^='top'] { + padding-bottom: var(--tr-sender-tooltip-gap, 8px); + } + + &[data-popper-placement^='bottom'], + &[x-placement^='bottom'], + &[data-placement^='bottom'] { + padding-top: var(--tr-sender-tooltip-gap, 8px); + } + + &[data-popper-placement^='left'], + &[x-placement^='left'], + &[data-placement^='left'] { + padding-right: var(--tr-sender-tooltip-gap, 8px); + } + + &[data-popper-placement^='right'], + &[x-placement^='right'], + &[data-placement^='right'] { + padding-left: var(--tr-sender-tooltip-gap, 8px); + } +} + +.tr-sender-tooltip-shell-mixin() { + padding: 0; + background: transparent; + border-radius: 0; + box-shadow: none; +} + +.tr-sender-tooltip-inner-base-mixin() { + display: block; + min-width: 10px; + max-width: 450px; + word-wrap: break-word; + white-space: pre-wrap; +} + +.tr-sender-tooltip-light-inner-mixin() { + .tr-sender-tooltip-inner { + .tr-sender-tooltip-inner-base-mixin(); + padding: var(--tv-Tooltip-padding-y) var(--tv-Tooltip-padding-x); + color: var(--tv-Tooltip-popper-light-text-color); + font-size: var(--tv-Tooltip-popper-font-size); + line-height: var(--tv-Tooltip-popper-font-line-height); + background: var(--tv-Tooltip-popper-light-bg-color); + border-radius: var(--tv-Tooltip-popper-border-radius); + box-shadow: var(--tv-Tooltip-box-shadow); + } +} + +.tr-sender-tooltip-light-popper-mixin() { + .tr-sender-tooltip-shell-mixin(); + .tr-sender-tooltip-gap-mixin(); + .tr-sender-tooltip-light-inner-mixin(); +} diff --git a/packages/components/src/sender-actions/submit-button/index.vue b/packages/components/src/sender-actions/submit-button/index.vue index 22a346df1..4124a8aa8 100644 --- a/packages/components/src/sender-actions/submit-button/index.vue +++ b/packages/components/src/sender-actions/submit-button/index.vue @@ -148,15 +148,10 @@ const handleClick = () => { diff --git a/packages/components/src/sender-actions/utils/tooltip.ts b/packages/components/src/sender-actions/utils/tooltip.ts index 6325a2993..0e35b346d 100644 --- a/packages/components/src/sender-actions/utils/tooltip.ts +++ b/packages/components/src/sender-actions/utils/tooltip.ts @@ -1,5 +1,34 @@ +import { h, isVNode, type VNode } from 'vue' import type { TooltipContent } from '../types/tooltip' +const TOOLTIP_INNER_CLASS = 'tr-sender-tooltip-inner' + +function hasTooltipInnerClass(value: unknown): boolean { + if (!value) return false + + if (typeof value === 'string') { + return value.split(/\s+/).includes(TOOLTIP_INNER_CLASS) + } + + if (Array.isArray(value)) { + return value.some(hasTooltipInnerClass) + } + + if (typeof value === 'object') { + return Boolean((value as Record)[TOOLTIP_INNER_CLASS]) + } + + return false +} + +function renderTooltipInner(content: string | VNode) { + if (isVNode(content) && hasTooltipInnerClass(content.props?.class)) { + return content + } + + return h('div', { class: TOOLTIP_INNER_CLASS }, content) +} + /** * 将 TooltipContent 转换为 TinyTooltip 的 render-content 函数 */ @@ -7,8 +36,8 @@ export function normalizeTooltipContent(tooltip: TooltipContent | undefined) { if (!tooltip) return undefined if (typeof tooltip === 'string') { - return () => tooltip + return () => renderTooltipInner(tooltip) } - return tooltip + return () => renderTooltipInner(tooltip()) }