@h-ai/ui 0.1.0-alpha.47 → 0.1.0-alpha.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/components/compounds/Pagination.svelte +12 -6
- package/dist/components/compounds/Popover.svelte +104 -18
- package/dist/components/compounds/Tooltip.svelte +127 -6
- package/dist/components/scenes/crud/CrudPage.svelte +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/internal/browser-safety.d.ts +3 -2
- package/dist/internal/browser-safety.js +26 -5
- package/dist/types.d.ts +9 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ components/
|
|
|
174
174
|
| `Modal` | 模态框 | `open`, `title`, `size`, `radius`, `bodyOverflow`, `showClose` |
|
|
175
175
|
| `Drawer` | 抽屉 | `open`, `position`, `title`, `size`, `width`, `resizable`, `widthStorageKey` |
|
|
176
176
|
| `Confirm` | 确认框 | `open`, `title`, `message`, `variant`, `onconfirm` |
|
|
177
|
-
| `Popover` | 弹出层 | `open`, `position`, `trigger`, `offset`
|
|
177
|
+
| `Popover` | 弹出层 | `open`, `position`, `trigger`, `offset`, `portal` |
|
|
178
178
|
|
|
179
179
|
#### 数据展示
|
|
180
180
|
|
|
@@ -51,23 +51,28 @@
|
|
|
51
51
|
: 'w-[2.5rem] shrink-0 [&>.fieldset]:m-0 [&>.fieldset]:min-w-0',
|
|
52
52
|
)
|
|
53
53
|
|
|
54
|
+
let jumperValue = $state(String(page))
|
|
55
|
+
|
|
54
56
|
function goToPage(p: number) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
const clamped = Math.max(1, Math.min(totalPages, Math.round(p)))
|
|
58
|
+
if (clamped !== page) {
|
|
59
|
+
page = clamped
|
|
60
|
+
jumperValue = String(clamped)
|
|
61
|
+
onchange?.(clamped)
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
let jumperValue = $state('')
|
|
62
|
-
|
|
63
65
|
function handleJump() {
|
|
64
66
|
const p = Number.parseInt(jumperValue, 10)
|
|
65
67
|
if (!Number.isNaN(p)) {
|
|
66
68
|
goToPage(p)
|
|
67
|
-
jumperValue = ''
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
function handleJumperBlur() {
|
|
73
|
+
handleJump()
|
|
74
|
+
}
|
|
75
|
+
|
|
71
76
|
function handlePageSizeChange(value: string) {
|
|
72
77
|
const next = Number.parseInt(value, 10)
|
|
73
78
|
if (!Number.isNaN(next) && next !== pageSize) {
|
|
@@ -150,6 +155,7 @@
|
|
|
150
155
|
max={totalPages}
|
|
151
156
|
bind:value={jumperValue}
|
|
152
157
|
onkeydown={(e: KeyboardEvent & { currentTarget: HTMLInputElement }) => e.key === 'Enter' && handleJump()}
|
|
158
|
+
onblur={handleJumperBlur}
|
|
153
159
|
/>
|
|
154
160
|
</div>
|
|
155
161
|
<span class={tableTextClass}>{labels.page ?? uiM('pagination_page')}</span>
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
-->
|
|
10
10
|
<script lang='ts'>
|
|
11
11
|
import type { DataAttributes, PopoverProps } from '../../types.js'
|
|
12
|
-
import {
|
|
12
|
+
import { tick } from 'svelte'
|
|
13
|
+
import { cn, getDataAttributes, portal as portalAction } from '../../utils.js'
|
|
13
14
|
|
|
14
15
|
let {
|
|
15
16
|
open = $bindable(false),
|
|
16
17
|
position = 'bottom',
|
|
17
18
|
trigger = 'click',
|
|
18
19
|
offset = 8,
|
|
20
|
+
portal = false,
|
|
19
21
|
class: className = '',
|
|
20
22
|
onopen,
|
|
21
23
|
onclose,
|
|
@@ -27,6 +29,10 @@
|
|
|
27
29
|
const dataAttributes = $derived(getDataAttributes(restProps))
|
|
28
30
|
let triggerElement = $state<HTMLDivElement | null>(null)
|
|
29
31
|
let popoverElement = $state<HTMLDivElement | null>(null)
|
|
32
|
+
/** portal 模式下的 fixed 坐标;每次滚动或缩放后重新计算。 */
|
|
33
|
+
let portalStyle = $state('')
|
|
34
|
+
/** 首帧布局收敛后的二次定位帧,关闭时必须取消避免过期写入。 */
|
|
35
|
+
let portalPositionFrame: number | undefined = $state()
|
|
30
36
|
|
|
31
37
|
const positionClass = $derived({
|
|
32
38
|
top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
|
|
@@ -42,6 +48,51 @@
|
|
|
42
48
|
right: 'right-full top-1/2 -translate-y-1/2 border-r-base-100 border-t-transparent border-l-transparent border-b-transparent',
|
|
43
49
|
}[position])
|
|
44
50
|
|
|
51
|
+
/** portal 模式下不再依赖相对容器,改用视口坐标并在边缘自动收敛。 */
|
|
52
|
+
function updatePortalPosition(): void {
|
|
53
|
+
if (!triggerElement || !popoverElement) {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const triggerRect = triggerElement.getBoundingClientRect()
|
|
58
|
+
const popoverRect = popoverElement.getBoundingClientRect()
|
|
59
|
+
const viewportPadding = 8
|
|
60
|
+
const gap = offset
|
|
61
|
+
const maxLeft = Math.max(viewportPadding, window.innerWidth - popoverRect.width - viewportPadding)
|
|
62
|
+
const maxTop = Math.max(viewportPadding, window.innerHeight - popoverRect.height - viewportPadding)
|
|
63
|
+
let left = triggerRect.left + (triggerRect.width - popoverRect.width) / 2
|
|
64
|
+
let top = triggerRect.bottom + gap
|
|
65
|
+
|
|
66
|
+
if (position === 'top') {
|
|
67
|
+
top = triggerRect.top - popoverRect.height - gap
|
|
68
|
+
}
|
|
69
|
+
else if (position === 'left') {
|
|
70
|
+
left = triggerRect.left - popoverRect.width - gap
|
|
71
|
+
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
|
|
72
|
+
}
|
|
73
|
+
else if (position === 'right') {
|
|
74
|
+
left = triggerRect.right + gap
|
|
75
|
+
top = triggerRect.top + (triggerRect.height - popoverRect.height) / 2
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
portalStyle = `position:fixed;left:${Math.round(Math.min(Math.max(viewportPadding, left), maxLeft))}px;top:${Math.round(Math.min(Math.max(viewportPadding, top), maxTop))}px;z-index:1200;`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 等 portal 挂载完成后复测,避免首帧按未完成内容高度定位。 */
|
|
82
|
+
async function scheduleSettledPortalPosition(): Promise<void> {
|
|
83
|
+
await tick()
|
|
84
|
+
if (!open || !portal) {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
portalPositionFrame = window.requestAnimationFrame(() => {
|
|
89
|
+
portalPositionFrame = undefined
|
|
90
|
+
if (open && portal) {
|
|
91
|
+
updatePortalPosition()
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
45
96
|
function handleOpen() {
|
|
46
97
|
open = true
|
|
47
98
|
onopen?.()
|
|
@@ -95,6 +146,27 @@
|
|
|
95
146
|
document.removeEventListener('click', handleClickOutside)
|
|
96
147
|
}
|
|
97
148
|
})
|
|
149
|
+
|
|
150
|
+
// portal 内容脱离原始相对定位容器后,以 fixed 坐标跟随触发器滚动和缩放。
|
|
151
|
+
$effect(() => {
|
|
152
|
+
if (!open || !portal) {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
updatePortalPosition()
|
|
157
|
+
void scheduleSettledPortalPosition()
|
|
158
|
+
const listenerOptions = { passive: true, capture: true } as const
|
|
159
|
+
window.addEventListener('scroll', updatePortalPosition, listenerOptions)
|
|
160
|
+
window.addEventListener('resize', updatePortalPosition)
|
|
161
|
+
return () => {
|
|
162
|
+
if (portalPositionFrame !== undefined) {
|
|
163
|
+
window.cancelAnimationFrame(portalPositionFrame)
|
|
164
|
+
portalPositionFrame = undefined
|
|
165
|
+
}
|
|
166
|
+
window.removeEventListener('scroll', updatePortalPosition, listenerOptions)
|
|
167
|
+
window.removeEventListener('resize', updatePortalPosition)
|
|
168
|
+
}
|
|
169
|
+
})
|
|
98
170
|
</script>
|
|
99
171
|
|
|
100
172
|
<div {...dataAttributes}
|
|
@@ -117,23 +189,37 @@
|
|
|
117
189
|
{/if}
|
|
118
190
|
</div>
|
|
119
191
|
|
|
120
|
-
<!--
|
|
192
|
+
<!-- 弹出内容;portal 模式用于逃逸滚动容器的 overflow 裁剪。 -->
|
|
121
193
|
{#if open}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
194
|
+
{#if portal}
|
|
195
|
+
<div
|
|
196
|
+
bind:this={popoverElement}
|
|
197
|
+
use:portalAction
|
|
198
|
+
class='fixed z-50 rounded-box border border-base-200 bg-base-100 p-4 shadow-lg'
|
|
199
|
+
style={portalStyle}
|
|
200
|
+
role='dialog'
|
|
201
|
+
>
|
|
202
|
+
{#if children}
|
|
203
|
+
{@render children()}
|
|
204
|
+
{/if}
|
|
205
|
+
</div>
|
|
206
|
+
{:else}
|
|
207
|
+
<div
|
|
208
|
+
bind:this={popoverElement}
|
|
209
|
+
class={cn(
|
|
210
|
+
'absolute z-50 bg-base-100 rounded-box shadow-lg border border-base-200 p-4',
|
|
211
|
+
positionClass,
|
|
212
|
+
)}
|
|
213
|
+
style='--offset: {offset}px'
|
|
214
|
+
role='dialog'
|
|
215
|
+
>
|
|
216
|
+
<!-- 箭头 -->
|
|
217
|
+
<div class={cn('absolute w-0 h-0 border-8', arrowClass)}></div>
|
|
218
|
+
|
|
219
|
+
{#if children}
|
|
220
|
+
{@render children()}
|
|
221
|
+
{/if}
|
|
222
|
+
</div>
|
|
223
|
+
{/if}
|
|
138
224
|
{/if}
|
|
139
225
|
</div>
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
=============================================================================
|
|
3
3
|
@h-ai/ui - Tooltip 组件
|
|
4
4
|
=============================================================================
|
|
5
|
-
|
|
5
|
+
提示框组件,支持两种渲染模式:
|
|
6
|
+
- 默认:DaisyUI CSS 伪元素(::before),轻量无 JS 开销
|
|
7
|
+
- portal:通过 document.body 渲染,不受父容器 overflow 裁剪
|
|
6
8
|
|
|
7
9
|
使用 Svelte 5 Runes ($props, $derived)
|
|
8
10
|
=============================================================================
|
|
@@ -14,12 +16,17 @@
|
|
|
14
16
|
const {
|
|
15
17
|
content,
|
|
16
18
|
position = 'top',
|
|
19
|
+
delay,
|
|
17
20
|
class: className = '',
|
|
18
21
|
children,
|
|
22
|
+
portal = false,
|
|
19
23
|
...restProps
|
|
20
24
|
}: TooltipProps & DataAttributes = $props()
|
|
21
25
|
|
|
22
26
|
const dataAttributes = $derived(getDataAttributes(restProps))
|
|
27
|
+
|
|
28
|
+
// ─── CSS 模式(默认) ───
|
|
29
|
+
|
|
23
30
|
const positionMap = {
|
|
24
31
|
top: 'tooltip-top',
|
|
25
32
|
right: 'tooltip-right',
|
|
@@ -34,10 +41,124 @@
|
|
|
34
41
|
className,
|
|
35
42
|
),
|
|
36
43
|
)
|
|
44
|
+
|
|
45
|
+
// ─── Portal 模式 ───
|
|
46
|
+
|
|
47
|
+
let triggerEl = $state<HTMLDivElement>()
|
|
48
|
+
let portalEl: HTMLDivElement | null = null
|
|
49
|
+
|
|
50
|
+
function getClass(opacity: string): string {
|
|
51
|
+
return cn(
|
|
52
|
+
'pointer-events-none fixed z-[9999] rounded-md bg-base-content px-2 py-1 text-xs text-base-100 shadow-md',
|
|
53
|
+
className,
|
|
54
|
+
opacity,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function ensurePortal(): HTMLDivElement {
|
|
59
|
+
if (!portalEl && typeof document !== 'undefined') {
|
|
60
|
+
portalEl = document.createElement('div')
|
|
61
|
+
portalEl.setAttribute('role', 'tooltip')
|
|
62
|
+
portalEl.className = getClass('opacity-0')
|
|
63
|
+
document.body.appendChild(portalEl)
|
|
64
|
+
}
|
|
65
|
+
return portalEl!
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function updatePosition(): string {
|
|
69
|
+
if (!triggerEl)
|
|
70
|
+
return ''
|
|
71
|
+
|
|
72
|
+
const rect = triggerEl.getBoundingClientRect()
|
|
73
|
+
const gap = 6
|
|
74
|
+
let top: number
|
|
75
|
+
let left: number
|
|
76
|
+
let transform: string
|
|
77
|
+
|
|
78
|
+
switch (position) {
|
|
79
|
+
case 'top':
|
|
80
|
+
top = rect.top - gap
|
|
81
|
+
left = rect.left + rect.width / 2
|
|
82
|
+
transform = 'translate(-50%,-100%)'
|
|
83
|
+
break
|
|
84
|
+
case 'bottom':
|
|
85
|
+
top = rect.bottom + gap
|
|
86
|
+
left = rect.left + rect.width / 2
|
|
87
|
+
transform = 'translateX(-50%)'
|
|
88
|
+
break
|
|
89
|
+
case 'left':
|
|
90
|
+
top = rect.top + rect.height / 2
|
|
91
|
+
left = rect.left - gap
|
|
92
|
+
transform = 'translate(-100%,-50%)'
|
|
93
|
+
break
|
|
94
|
+
case 'right':
|
|
95
|
+
top = rect.top + rect.height / 2
|
|
96
|
+
left = rect.right + gap
|
|
97
|
+
transform = 'translateY(-50%)'
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return `top:${top}px;left:${left}px;transform:${transform};`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let showTimer: ReturnType<typeof setTimeout> | undefined
|
|
105
|
+
|
|
106
|
+
function show() {
|
|
107
|
+
if (delay) {
|
|
108
|
+
showTimer = setTimeout(doShow, delay)
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
doShow()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function doShow() {
|
|
116
|
+
const el = ensurePortal()
|
|
117
|
+
el.className = getClass('opacity-100')
|
|
118
|
+
el.style.cssText = updatePosition()
|
|
119
|
+
el.textContent = content
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function hide() {
|
|
123
|
+
if (showTimer) {
|
|
124
|
+
clearTimeout(showTimer)
|
|
125
|
+
showTimer = undefined
|
|
126
|
+
}
|
|
127
|
+
if (portalEl) {
|
|
128
|
+
portalEl.className = getClass('opacity-0')
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
$effect(() => {
|
|
133
|
+
return () => {
|
|
134
|
+
if (showTimer)
|
|
135
|
+
clearTimeout(showTimer)
|
|
136
|
+
if (portalEl?.parentNode) {
|
|
137
|
+
portalEl.parentNode.removeChild(portalEl)
|
|
138
|
+
portalEl = null
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
})
|
|
37
142
|
</script>
|
|
38
143
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
144
|
+
{#if portal}
|
|
145
|
+
<div
|
|
146
|
+
{...dataAttributes}
|
|
147
|
+
bind:this={triggerEl}
|
|
148
|
+
class='inline-flex'
|
|
149
|
+
onmouseenter={show}
|
|
150
|
+
onmouseleave={hide}
|
|
151
|
+
onfocusin={show}
|
|
152
|
+
onfocusout={hide}
|
|
153
|
+
>
|
|
154
|
+
{#if children}
|
|
155
|
+
{@render children()}
|
|
156
|
+
{/if}
|
|
157
|
+
</div>
|
|
158
|
+
{:else}
|
|
159
|
+
<div {...dataAttributes} class={tooltipClass} data-tip={content}>
|
|
160
|
+
{#if children}
|
|
161
|
+
{@render children()}
|
|
162
|
+
{/if}
|
|
163
|
+
</div>
|
|
164
|
+
{/if}
|
|
@@ -655,7 +655,7 @@
|
|
|
655
655
|
{/if}
|
|
656
656
|
|
|
657
657
|
<!-- 搜索 + 过滤栏 -->
|
|
658
|
-
{#if searchable || filterFields.length > 0}
|
|
658
|
+
{#if searchable || filterFields.length > 0 || toolbarLeading || toolbarActions}
|
|
659
659
|
{#if toolbarStyle === 'toolbar'}
|
|
660
660
|
<TableToolbar
|
|
661
661
|
{searchable}
|
|
@@ -699,7 +699,7 @@
|
|
|
699
699
|
<Card padding='none' class={listCardClass}>
|
|
700
700
|
{#if card}
|
|
701
701
|
<!-- 卡片视图使用独立滚动容器,分页栏始终保留在容器外。 -->
|
|
702
|
-
<div class='min-h-0 flex-1 overflow-auto'>
|
|
702
|
+
<div class='min-h-0 flex-1 overflow-auto pt-2'>
|
|
703
703
|
{#if data.items.length > 0}
|
|
704
704
|
<div class='grid min-h-full grid-cols-[repeat(auto-fill,minmax(min(100%,18rem),1fr))] content-start gap-3'>
|
|
705
705
|
{#each data.items as item (String(item[keyField]))}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
export * from './components/index.js';
|
|
9
9
|
// ─── i18n 国际化(Paraglide 辅助工具) ───
|
|
10
10
|
export * from './i18n.svelte.js';
|
|
11
|
+
// ─── 浏览器能力安全辅助(剪贴板、localStorage 等降级方案) ───
|
|
12
|
+
export * from './internal/browser-safety.js';
|
|
11
13
|
// ─── Message 轻量消息提示(类似 ElementUI Message) ───
|
|
12
14
|
export * from './message.svelte.js';
|
|
13
15
|
// ─── MessageBox 命令式弹框(类似 ElementUI MessageBox) ───
|
|
@@ -20,7 +20,8 @@ export declare function writeStoredValue(key: string, value: string): boolean;
|
|
|
20
20
|
/**
|
|
21
21
|
* 安全写入剪贴板。
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* 优先使用现代 Clipboard API;在不安全上下文(HTTP)、旧 WebView
|
|
24
|
+
* 或浏览器策略禁用时回退到 `document.execCommand('copy')`,
|
|
25
|
+
* 确保尽可能多的环境下复制操作可用。
|
|
25
26
|
*/
|
|
26
27
|
export declare function writeTextToClipboard(text: string): Promise<boolean>;
|
|
@@ -41,16 +41,37 @@ export function writeStoredValue(key, value) {
|
|
|
41
41
|
/**
|
|
42
42
|
* 安全写入剪贴板。
|
|
43
43
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* 优先使用现代 Clipboard API;在不安全上下文(HTTP)、旧 WebView
|
|
45
|
+
* 或浏览器策略禁用时回退到 `document.execCommand('copy')`,
|
|
46
|
+
* 确保尽可能多的环境下复制操作可用。
|
|
46
47
|
*/
|
|
47
48
|
export async function writeTextToClipboard(text) {
|
|
48
|
-
if (typeof navigator === 'undefined'
|
|
49
|
+
if (typeof navigator === 'undefined') {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// 现代 Clipboard API(仅安全上下文可用)
|
|
53
|
+
if (navigator.clipboard?.writeText) {
|
|
54
|
+
try {
|
|
55
|
+
await navigator.clipboard.writeText(text);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Clipboard API 失败时继续尝试 fallback
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// fallback:textarea + execCommand(兼容 HTTP、旧浏览器)
|
|
63
|
+
if (typeof document === 'undefined') {
|
|
49
64
|
return false;
|
|
50
65
|
}
|
|
51
66
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
const textarea = document.createElement('textarea');
|
|
68
|
+
textarea.value = text;
|
|
69
|
+
textarea.style.cssText = 'position:fixed;left:-9999px;top:-9999px;opacity:0';
|
|
70
|
+
document.body.appendChild(textarea);
|
|
71
|
+
textarea.select();
|
|
72
|
+
const ok = document.execCommand('copy');
|
|
73
|
+
document.body.removeChild(textarea);
|
|
74
|
+
return ok;
|
|
54
75
|
}
|
|
55
76
|
catch {
|
|
56
77
|
return false;
|
package/dist/types.d.ts
CHANGED
|
@@ -516,6 +516,11 @@ export interface TooltipProps extends DataAttributes {
|
|
|
516
516
|
class?: string;
|
|
517
517
|
/** 触发元素插槽 */
|
|
518
518
|
children?: Snippet;
|
|
519
|
+
/**
|
|
520
|
+
* 启用 portal 模式:提示内容通过 document.body 渲染,
|
|
521
|
+
* 不受父容器 overflow 裁剪。适用于表格、滚动容器等场景。
|
|
522
|
+
*/
|
|
523
|
+
portal?: boolean;
|
|
519
524
|
}
|
|
520
525
|
/**
|
|
521
526
|
* 加载属性
|
|
@@ -727,8 +732,8 @@ export interface DropdownItem {
|
|
|
727
732
|
export interface DropdownProps extends DataAttributes {
|
|
728
733
|
/** 菜单项 */
|
|
729
734
|
items: DropdownItem[];
|
|
730
|
-
/**
|
|
731
|
-
trigger?: 'click' | 'hover';
|
|
735
|
+
/** 触发方式;`manual` 由外部完全控制 open 状态。 */
|
|
736
|
+
trigger?: 'click' | 'hover' | 'manual';
|
|
732
737
|
/** 位置 */
|
|
733
738
|
position?: Position;
|
|
734
739
|
/** 对齐 */
|
|
@@ -977,6 +982,8 @@ export interface PopoverProps extends DataAttributes {
|
|
|
977
982
|
trigger?: 'click' | 'hover';
|
|
978
983
|
/** 偏移量 */
|
|
979
984
|
offset?: number;
|
|
985
|
+
/** 是否将内容挂到 body,以逃逸祖先 overflow 和堆叠上下文。 */
|
|
986
|
+
portal?: boolean;
|
|
980
987
|
/** 自定义类名 */
|
|
981
988
|
class?: string;
|
|
982
989
|
/** 打开事件 */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.49",
|
|
5
5
|
"description": "Hai Framework UI component library for Svelte 5 Runes.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"svelte": "./dist/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"mermaid": "^11.12.0",
|
|
53
53
|
"shiki": "^4.0.2",
|
|
54
54
|
"tailwind-merge": "^3.5.0",
|
|
55
|
-
"@h-ai/core": "0.1.0-alpha.
|
|
55
|
+
"@h-ai/core": "0.1.0-alpha.49"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@sveltejs/package": "^2.5.7",
|