@ithinkdt/ui 4.0.0-5
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 -0
- package/auto-imports.d.ts +10 -0
- package/auto-imports.js +19 -0
- package/package.json +61 -0
- package/src/design/index.d.ts +53 -0
- package/src/design/index.js +1 -0
- package/src/design/layout.js +113 -0
- package/src/directives/index.d.ts +37 -0
- package/src/directives/index.js +2 -0
- package/src/directives/spin.js +175 -0
- package/src/directives/tooltip.js +125 -0
- package/src/index.d.ts +5 -0
- package/src/index.js +37 -0
- package/src/use-style.js +8 -0
- package/src/utils.js +19 -0
- package/tests/index.spec.ts +9 -0
- package/tests/tsconfig.json +9 -0
- package/tsconfig.json +14 -0
- package/unocss.d.ts +3 -0
- package/unocss.js +16 -0
- package/vitest.config.ts +7 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @ithinkdt/ui
|
package/auto-imports.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const UI = [
|
|
2
|
+
{
|
|
3
|
+
from: '@ithinkdt/ui',
|
|
4
|
+
imports: ['vSpin', 'vTooltip'],
|
|
5
|
+
},
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
export default UI
|
|
9
|
+
|
|
10
|
+
export const UIDirectives = [
|
|
11
|
+
{
|
|
12
|
+
type: 'directive',
|
|
13
|
+
resolve(name) {
|
|
14
|
+
return ['Spin', 'Tooltip'].includes(name)
|
|
15
|
+
? { name: `v${name}`, from: '@ithinkdt/ui' }
|
|
16
|
+
: undefined
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
]
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ithinkdt/ui",
|
|
3
|
+
"version": "4.0.0-5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "iThinkDT UI",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"vue",
|
|
9
|
+
"ithinkdt"
|
|
10
|
+
],
|
|
11
|
+
"module": "./src/index.js",
|
|
12
|
+
"types": "./src/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./src/index.d.ts",
|
|
16
|
+
"default": "./src/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": {
|
|
19
|
+
"default": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"./auto-imports": {
|
|
22
|
+
"types": "./auto-imports.d.ts",
|
|
23
|
+
"default": "./auto-imports.js"
|
|
24
|
+
},
|
|
25
|
+
"./unocss": {
|
|
26
|
+
"types": "./unocss.d.ts",
|
|
27
|
+
"default": "./unocss.js"
|
|
28
|
+
},
|
|
29
|
+
"./design": {
|
|
30
|
+
"types": "./design/index.d.ts",
|
|
31
|
+
"default": "./design/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"registry": "https://registry.npmjs.org/",
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@unocss/preset-uno": ">=0.62.3",
|
|
41
|
+
"@vueuse/core": "^11.0.3",
|
|
42
|
+
"@ithinkdt/common": "^4.0.0-5"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@ithinkdt/core": ">=4.0",
|
|
46
|
+
"vue": ">=3.4",
|
|
47
|
+
"vue-router": ">=4.4",
|
|
48
|
+
"ithinkdt-ui": ">=1.4"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"ithinkdt-ui": "^1.4.5",
|
|
52
|
+
"typescript": "~5.5.4",
|
|
53
|
+
"vue": "^3.4.38",
|
|
54
|
+
"vue-router": "^4.4.3",
|
|
55
|
+
"@ithinkdt/core": "^4.0.0-5"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"format": "prettier --write \"./{src,tests}/**/*.{js,vue,ts,jsx,tsx}\"",
|
|
59
|
+
"release": "pnpm publish --no-git-checks"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
declare const AppLayout: (
|
|
2
|
+
props: import('@ithinkdt/common').PublicProps & {
|
|
3
|
+
/**
|
|
4
|
+
* 布局模式
|
|
5
|
+
* - sider-header_content_footer 左右布局,侧栏占据整个高度
|
|
6
|
+
* - header_sider-content_footer 上下布局,头部占据所有宽度
|
|
7
|
+
*/
|
|
8
|
+
layout?: 'sider-header_content_footer' | 'header_sider-content_footer'
|
|
9
|
+
},
|
|
10
|
+
context: {
|
|
11
|
+
slots: {
|
|
12
|
+
default?: () => import('vue').VNode
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
) => import('vue').VNode
|
|
16
|
+
|
|
17
|
+
declare const AppHeader: (
|
|
18
|
+
props: import('@ithinkdt/common').PublicProps & {},
|
|
19
|
+
context: {
|
|
20
|
+
slots: {
|
|
21
|
+
default?: () => import('vue').VNode
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
) => import('vue').VNode
|
|
25
|
+
|
|
26
|
+
declare const AppContent: (
|
|
27
|
+
props: import('@ithinkdt/common').PublicProps & {},
|
|
28
|
+
context: {
|
|
29
|
+
slots: {
|
|
30
|
+
default?: () => import('vue').VNode
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
) => import('vue').VNode
|
|
34
|
+
|
|
35
|
+
declare const AppSider: (
|
|
36
|
+
props: import('@ithinkdt/common').PublicProps & {},
|
|
37
|
+
context: {
|
|
38
|
+
slots: {
|
|
39
|
+
default?: () => import('vue').VNode
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
) => import('vue').VNode
|
|
43
|
+
|
|
44
|
+
declare const AppFooter: (
|
|
45
|
+
props: import('@ithinkdt/common').PublicProps & {},
|
|
46
|
+
context: {
|
|
47
|
+
slots: {
|
|
48
|
+
default?: () => import('vue').VNode
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
) => import('vue').VNode
|
|
52
|
+
|
|
53
|
+
export { AppLayout, AppHeader, AppContent, AppSider, AppFooter }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AppLayout, AppHeader, AppContent, AppSider, AppFooter } from './layout.js'
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { defineComponent, h } from 'vue'
|
|
2
|
+
import { c, cB, cM } from 'ithinkdt-ui'
|
|
3
|
+
|
|
4
|
+
import useStyle from '../use-style.js'
|
|
5
|
+
|
|
6
|
+
const clsPrefix = 'app'
|
|
7
|
+
export const AppLayout = /* @__PURE__ */ defineComponent({
|
|
8
|
+
name: 'AppLayout',
|
|
9
|
+
props: {
|
|
10
|
+
layout: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: 'sider-header_content_footer',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
setup(props, { slots }) {
|
|
16
|
+
useStyle('-layout', style, ref(clsPrefix), false)
|
|
17
|
+
|
|
18
|
+
return () => {
|
|
19
|
+
const { layout } = props
|
|
20
|
+
|
|
21
|
+
return h(
|
|
22
|
+
'div',
|
|
23
|
+
{
|
|
24
|
+
class: {
|
|
25
|
+
[`${clsPrefix}-layout`]: true,
|
|
26
|
+
[`${clsPrefix}-layout--full-header`]: layout === 'header_sider-content_footer',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
slots.default?.(),
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export const AppHeader = /* @__PURE__ */ defineComponent({
|
|
36
|
+
name: 'AppHeader',
|
|
37
|
+
props: {},
|
|
38
|
+
setup(props, { slots }) {
|
|
39
|
+
return () => h('div', { class: `${clsPrefix}-header` }, slots.default?.())
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
export const AppSider = /* @__PURE__ */ defineComponent({
|
|
43
|
+
name: 'AppSider',
|
|
44
|
+
props: {},
|
|
45
|
+
setup(props, { slots }) {
|
|
46
|
+
return () => h('div', { class: `${clsPrefix}-sider` }, slots.default?.())
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
export const AppContent = /* @__PURE__ */ defineComponent({
|
|
50
|
+
name: 'AppContent',
|
|
51
|
+
props: {},
|
|
52
|
+
setup(props, { slots }) {
|
|
53
|
+
return () => h('div', { class: `${clsPrefix}-content` }, slots.default?.())
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
export const AppFooter = /* @__PURE__ */ defineComponent({
|
|
57
|
+
name: 'AppFooter',
|
|
58
|
+
props: {},
|
|
59
|
+
setup(props, { slots }) {
|
|
60
|
+
return () => h('div', { class: `${clsPrefix}-footer` }, slots.default?.())
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const style = /* @__PURE__ */ c([
|
|
65
|
+
cB(
|
|
66
|
+
'layout',
|
|
67
|
+
{
|
|
68
|
+
display: 'grid',
|
|
69
|
+
gridTemplateAreas: `
|
|
70
|
+
'sider header'
|
|
71
|
+
'sider content'
|
|
72
|
+
'sider footer'
|
|
73
|
+
`,
|
|
74
|
+
gridTemplateRows: 'auto 1fr auto',
|
|
75
|
+
gridTemplateColumns: 'auto 1fr',
|
|
76
|
+
},
|
|
77
|
+
[
|
|
78
|
+
cM('full-header', {
|
|
79
|
+
gridTemplateAreas: `
|
|
80
|
+
'header header'
|
|
81
|
+
'sider content'
|
|
82
|
+
'sider footer'
|
|
83
|
+
`,
|
|
84
|
+
}),
|
|
85
|
+
],
|
|
86
|
+
),
|
|
87
|
+
cB('header', {
|
|
88
|
+
gridArea: 'header',
|
|
89
|
+
height: '52px',
|
|
90
|
+
boxShadow: '0 1px 4px rgb(0 21 41 / 0.08)',
|
|
91
|
+
}),
|
|
92
|
+
cB(
|
|
93
|
+
'sider',
|
|
94
|
+
{
|
|
95
|
+
gridArea: 'sider',
|
|
96
|
+
width: '210px',
|
|
97
|
+
boxShadow:
|
|
98
|
+
'1px 2px 2px -2px rgb(0 0 0 / 0.04), 3px 6px 6px 0 rgb(0 0 0 / 0.03)',
|
|
99
|
+
},
|
|
100
|
+
[
|
|
101
|
+
cM('collapsed', {
|
|
102
|
+
width: '48px',
|
|
103
|
+
}),
|
|
104
|
+
],
|
|
105
|
+
),
|
|
106
|
+
cB('content', {
|
|
107
|
+
gridArea: 'content',
|
|
108
|
+
}),
|
|
109
|
+
cB('footer', {
|
|
110
|
+
gridArea: 'footer',
|
|
111
|
+
lineHeight: '24px',
|
|
112
|
+
}),
|
|
113
|
+
])
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { VNode, Directive } from 'vue'
|
|
2
|
+
|
|
3
|
+
declare const SpinDirectiveProvider: () => VNode
|
|
4
|
+
|
|
5
|
+
type SpinDirectiveProps = void | undefined | string | boolean
|
|
6
|
+
|
|
7
|
+
type SpinDirective = Directive<
|
|
8
|
+
HTMLElement & {
|
|
9
|
+
readonly dataset: DOMStringMap & {
|
|
10
|
+
spinTip?: string
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
SpinDirectiveProps
|
|
14
|
+
>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* directive 加载效果
|
|
18
|
+
*
|
|
19
|
+
* 将在容器最后 append 元素,如果容器的 position 是 static,将修改为 relative。如出现样式问题请注意排查
|
|
20
|
+
*/
|
|
21
|
+
declare const vSpin: SpinDirective
|
|
22
|
+
|
|
23
|
+
declare const TooltipDirectiveProvider: () => VNode
|
|
24
|
+
|
|
25
|
+
type TooltipDirectiveOptions = void | undefined | string
|
|
26
|
+
|
|
27
|
+
type TooltipDirective = Directive<HTMLElement, TooltipDirectiveOptions>
|
|
28
|
+
/**
|
|
29
|
+
* directive 弹出提示
|
|
30
|
+
*
|
|
31
|
+
* 支持的修饰符
|
|
32
|
+
* - auto 当内容溢出来容器时自动展示,否则隐藏
|
|
33
|
+
* - left-end、left、left-start、top-start、top、top-end、right-start、right、right-end、bottom-end、bottom、bottom-start 弹出内容的位置
|
|
34
|
+
*/
|
|
35
|
+
declare const vTooltip: TooltipDirective
|
|
36
|
+
|
|
37
|
+
export { SpinDirectiveProvider, SpinDirective, vSpin, TooltipDirectiveProvider, TooltipDirective, vTooltip }
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { defineComponent, ref, nextTick, h, Fragment } from 'vue'
|
|
2
|
+
import { watchDebounced } from '@vueuse/core'
|
|
3
|
+
import { useElementIntersectionRect } from '@ithinkdt/common/composables'
|
|
4
|
+
import { string2dom } from '@ithinkdt/common/dom'
|
|
5
|
+
|
|
6
|
+
import { c, cB, cE, cM } from 'ithinkdt-ui'
|
|
7
|
+
|
|
8
|
+
import useStyle from '../use-style.js'
|
|
9
|
+
|
|
10
|
+
let clsPrefix
|
|
11
|
+
|
|
12
|
+
const style = /* @__PURE__ */ c([
|
|
13
|
+
cB('spin-host', [
|
|
14
|
+
cM('relative', {
|
|
15
|
+
position: 'relative',
|
|
16
|
+
}),
|
|
17
|
+
]),
|
|
18
|
+
cB(
|
|
19
|
+
'spin-directive',
|
|
20
|
+
{
|
|
21
|
+
zIndex: '999999',
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
color: 'var(--color-primary)',
|
|
24
|
+
display: 'flex',
|
|
25
|
+
flexDirection: 'column',
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
gap: '4px',
|
|
29
|
+
willChange: 'background-color',
|
|
30
|
+
backgroundColor: `rgb(255 255 255 / 0)`,
|
|
31
|
+
transition: 'background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
32
|
+
},
|
|
33
|
+
[
|
|
34
|
+
cE('tip', [c('&:empty', { display: 'none' })]),
|
|
35
|
+
cE('icon', {
|
|
36
|
+
willChange: 'opacity',
|
|
37
|
+
opacity: '0',
|
|
38
|
+
transition: 'opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
39
|
+
}),
|
|
40
|
+
cM(
|
|
41
|
+
'spining',
|
|
42
|
+
{
|
|
43
|
+
backgroundColor: `rgb(255 255 255 / 0.5)`,
|
|
44
|
+
},
|
|
45
|
+
[cE('icon', { opacity: '1' })],
|
|
46
|
+
),
|
|
47
|
+
],
|
|
48
|
+
),
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
export const SpinDirectiveProvider = /* @__PURE__ */defineComponent({
|
|
52
|
+
name: 'SpinDirectiveProvider',
|
|
53
|
+
setup() {
|
|
54
|
+
clsPrefix = useStyle('-spin-directive', style)
|
|
55
|
+
return () => h(Fragment)
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const Spin = /* @__PURE__ */ Symbol('spin-dir')
|
|
60
|
+
|
|
61
|
+
const init = (el, value) => {
|
|
62
|
+
if (el[Spin]) return
|
|
63
|
+
|
|
64
|
+
const loading = ref(!!value)
|
|
65
|
+
|
|
66
|
+
const spinEl = string2dom(`<div class="${clsPrefix.value}-spin-directive">
|
|
67
|
+
<svg class="${clsPrefix.value}-spin-directive__icon" width="32" height="32" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
68
|
+
<g>
|
|
69
|
+
<animateTransform attributeName="transform" type="rotate" values="0 100 100;270 100 100" begin="0s" dur="1.6s" fill="freeze" repeatCount="indefinite"></animateTransform>
|
|
70
|
+
<circle fill="none" stroke="currentColor" stroke-width="16" stroke-linecap="round" cx="100" cy="100" r="92" stroke-dasharray="567" stroke-dashoffset="1848">
|
|
71
|
+
<animateTransform attributeName="transform" type="rotate" values="0 100 100;135 100 100;450 100 100" begin="0s" dur="1.6s" fill="freeze" repeatCount="indefinite"></animateTransform>
|
|
72
|
+
<animate attributeName="stroke-dashoffset" values="567;142;567" begin="0s" dur="1.6s" fill="freeze" repeatCount="indefinite"></animate>
|
|
73
|
+
</circle>
|
|
74
|
+
</g>
|
|
75
|
+
</svg>
|
|
76
|
+
<div class="${clsPrefix.value}-spin-directive__tip"></div>
|
|
77
|
+
</div>`)
|
|
78
|
+
|
|
79
|
+
let rect
|
|
80
|
+
const { stop: stopEl } = useElementIntersectionRect(el, (_rect) => {
|
|
81
|
+
rect = _rect
|
|
82
|
+
Object.assign(spinEl.style, {
|
|
83
|
+
left: 0,
|
|
84
|
+
top: 0,
|
|
85
|
+
width: rect.width + 'px',
|
|
86
|
+
height: rect.height + 'px',
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
let timer
|
|
91
|
+
const stopSpin = watchDebounced(
|
|
92
|
+
loading,
|
|
93
|
+
(loading) => {
|
|
94
|
+
if (loading) {
|
|
95
|
+
if (timer === undefined) {
|
|
96
|
+
clearTimeout(timer)
|
|
97
|
+
timer = undefined
|
|
98
|
+
}
|
|
99
|
+
el.append(spinEl)
|
|
100
|
+
requestAnimationFrame(() => {
|
|
101
|
+
spinEl.classList.add(`${clsPrefix.value}-spin-directive--spining`)
|
|
102
|
+
})
|
|
103
|
+
} else {
|
|
104
|
+
spinEl.classList.remove(`${clsPrefix.value}-spin-directive--spining`)
|
|
105
|
+
timer = setTimeout(() => {
|
|
106
|
+
spinEl.remove()
|
|
107
|
+
}, 300)
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{ immediate: true, debounce: 30 },
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
el[Spin] = {
|
|
114
|
+
loading,
|
|
115
|
+
spinEl,
|
|
116
|
+
updateTip: () => {
|
|
117
|
+
const tipEl = spinEl.querySelector(`.${clsPrefix.value}-spin-directive__tip`)
|
|
118
|
+
tipEl.textContent = el.dataset.spinTip
|
|
119
|
+
},
|
|
120
|
+
stop: () => {
|
|
121
|
+
stopEl()
|
|
122
|
+
stopSpin()
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const obs = async (el) => {
|
|
128
|
+
if (el[Spin].observer) return
|
|
129
|
+
// 创建 MutationObserver 对象
|
|
130
|
+
await nextTick()
|
|
131
|
+
const observer = (el[Spin].observer = new MutationObserver((mutationsList) => {
|
|
132
|
+
for (const mutation of mutationsList) {
|
|
133
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'data-spin-tip') {
|
|
134
|
+
el[Spin].updateTip()
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}))
|
|
139
|
+
|
|
140
|
+
// 开始观察目标元素
|
|
141
|
+
observer.observe(el, { attributes: true })
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const checkStyle = (el) => {
|
|
145
|
+
const style = window.getComputedStyle(el)
|
|
146
|
+
if (style.getPropertyValue('position') === 'static') {
|
|
147
|
+
el.classList.add(`${clsPrefix.value}-spin-host--relative`)
|
|
148
|
+
} else {
|
|
149
|
+
el.classList.remove(`${clsPrefix.value}-spin-host--relative`)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const vSpin = {
|
|
154
|
+
beforeMount(el, { value }) {
|
|
155
|
+
init(el, value)
|
|
156
|
+
el.classList.add(`${clsPrefix.value}-spin-host`)
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
mounted(el) {
|
|
160
|
+
el[Spin].updateTip()
|
|
161
|
+
checkStyle(el)
|
|
162
|
+
obs(el)
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
updated(el, { value }) {
|
|
166
|
+
el[Spin].loading.value = !!value
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
beforeUnmount(el) {
|
|
170
|
+
el[Spin].loading.value = false
|
|
171
|
+
el[Spin].stop()
|
|
172
|
+
el[Spin].observer?.disconnect()
|
|
173
|
+
delete el[Spin]
|
|
174
|
+
},
|
|
175
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { defineComponent, h, shallowRef, ref, reactive, isVNode } from 'vue'
|
|
2
|
+
import { useEventListener } from '@vueuse/core'
|
|
3
|
+
import { NTooltip } from 'ithinkdt-ui'
|
|
4
|
+
import { debounce } from '@ithinkdt/common/fn'
|
|
5
|
+
|
|
6
|
+
const Tooltip = /* @__PURE__ */ Symbol('tooltip-dir')
|
|
7
|
+
const current = /* @__PURE__ */ shallowRef()
|
|
8
|
+
|
|
9
|
+
export const TooltipDirectiveProvider = /* @__PURE__ */ defineComponent({
|
|
10
|
+
name: 'TooltipDirectiveProvider',
|
|
11
|
+
setup() {
|
|
12
|
+
const show = ref(false)
|
|
13
|
+
const tip = shallowRef()
|
|
14
|
+
const pos = reactive({ x: 0, y: 0 })
|
|
15
|
+
const placement = ref('top')
|
|
16
|
+
|
|
17
|
+
const update = () => {
|
|
18
|
+
if (!current.value) return
|
|
19
|
+
const el = current.value
|
|
20
|
+
const tooltip = el[Tooltip]
|
|
21
|
+
const _tip = tooltip.tip
|
|
22
|
+
tip.value =
|
|
23
|
+
typeof _tip === 'function'
|
|
24
|
+
? _tip
|
|
25
|
+
: isVNode(_tip)
|
|
26
|
+
? () => _tip
|
|
27
|
+
: () =>
|
|
28
|
+
h('span', {
|
|
29
|
+
innerHTML: _tip || el.attributes.getNamedItem('title') || el.innerHTML,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (
|
|
33
|
+
!tooltip.binding.modifiers.auto ||
|
|
34
|
+
el.offsetWidth > el.parentElement.clientWidth ||
|
|
35
|
+
el.offsetHeight > el.parentElement.clientHeight
|
|
36
|
+
) {
|
|
37
|
+
show.value = true
|
|
38
|
+
|
|
39
|
+
const rect = el.getBoundingClientRect()
|
|
40
|
+
placement.value =
|
|
41
|
+
Object.keys(tooltip.binding.modifiers).find((m) =>
|
|
42
|
+
['top', 'right', 'left', 'bottom'].find((k) => m.startsWith(k)),
|
|
43
|
+
) ?? 'top'
|
|
44
|
+
switch (placement.value.split('-')[0]) {
|
|
45
|
+
case 'top': {
|
|
46
|
+
pos.x = rect.left + rect.width / 2
|
|
47
|
+
pos.y = rect.top
|
|
48
|
+
break
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
case 'right': {
|
|
52
|
+
pos.x = rect.left + rect.width
|
|
53
|
+
pos.y = rect.top + rect.height / 2
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
case 'left': {
|
|
58
|
+
pos.x = rect.left
|
|
59
|
+
pos.y = rect.top + rect.height / 2
|
|
60
|
+
break
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
case 'bottom': {
|
|
64
|
+
pos.x = rect.left + rect.width / 2
|
|
65
|
+
pos.y = rect.bottom
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} else if (show.value) {
|
|
70
|
+
show.value = false
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const update180 = debounce(update, 180)
|
|
75
|
+
|
|
76
|
+
useEventListener(
|
|
77
|
+
'mouseover',
|
|
78
|
+
(ev) => {
|
|
79
|
+
const el = ev.target
|
|
80
|
+
const tooltip = el[Tooltip]
|
|
81
|
+
if (!tooltip) {
|
|
82
|
+
if (!current.value?.contains(el)) {
|
|
83
|
+
show.value = false
|
|
84
|
+
current.value = undefined
|
|
85
|
+
}
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
current.value = el
|
|
89
|
+
update180()
|
|
90
|
+
},
|
|
91
|
+
{ passive: true, capture: true },
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return () => {
|
|
95
|
+
return h(
|
|
96
|
+
NTooltip,
|
|
97
|
+
{
|
|
98
|
+
trigger: 'manual',
|
|
99
|
+
show: !!current.value && show.value,
|
|
100
|
+
x: pos.x,
|
|
101
|
+
y: pos.y,
|
|
102
|
+
placement: placement.value,
|
|
103
|
+
},
|
|
104
|
+
tip.value,
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const update = (el, binding) => {
|
|
111
|
+
el[Tooltip] = {
|
|
112
|
+
binding,
|
|
113
|
+
tip: binding.value,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const vTooltip = {
|
|
118
|
+
mounted: update,
|
|
119
|
+
update,
|
|
120
|
+
beforeUnmount(el) {
|
|
121
|
+
if (current.value === el) {
|
|
122
|
+
current.value = undefined
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { computed, defineComponent, Fragment, h } from 'vue'
|
|
2
|
+
import { useStyleTag } from '@vueuse/core'
|
|
3
|
+
import { useApp } from '@ithinkdt/core'
|
|
4
|
+
import { hyphenate } from '@ithinkdt/common/string'
|
|
5
|
+
import { toRGB } from '@ithinkdt/common/color'
|
|
6
|
+
|
|
7
|
+
export { SpinDirectiveProvider, TooltipDirectiveProvider, vSpin, vTooltip } from './directives/index.js'
|
|
8
|
+
|
|
9
|
+
export * from './design/index.js'
|
|
10
|
+
|
|
11
|
+
export const GlobalCssVar = /* @__PURE__ */ defineComponent({
|
|
12
|
+
name: 'GlobalCssVar',
|
|
13
|
+
props: {
|
|
14
|
+
namespace: { type: String, default: undefined },
|
|
15
|
+
},
|
|
16
|
+
setup(props) {
|
|
17
|
+
const app = useApp()
|
|
18
|
+
|
|
19
|
+
useStyleTag(
|
|
20
|
+
computed(
|
|
21
|
+
() =>
|
|
22
|
+
`${props.namespace ? `.${props.namespace}` : ':root'} { ${Object.entries(app.vars)
|
|
23
|
+
.flatMap(([name, value]) => {
|
|
24
|
+
name = hyphenate(name)
|
|
25
|
+
return [`--${name}-rgb: ${toRGB(value).join(' ')};`, `--${name}: rgb(var(--${name}-rgb));`]
|
|
26
|
+
})
|
|
27
|
+
.join(' ')} }`,
|
|
28
|
+
),
|
|
29
|
+
{
|
|
30
|
+
id: `${props.namespace ?? 'global'}-vars`,
|
|
31
|
+
immediate: true,
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return () => h(Fragment)
|
|
36
|
+
},
|
|
37
|
+
})
|
package/src/use-style.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import _useStyle from 'ithinkdt-ui/es/_mixins/use-style'
|
|
2
|
+
import { useMergedClsPrefix } from 'ithinkdt-ui/es/_mixins/use-config'
|
|
3
|
+
|
|
4
|
+
export default function useStyle(mountId, style, clsPrefix, styleIsolate) {
|
|
5
|
+
clsPrefix ??= useMergedClsPrefix()
|
|
6
|
+
_useStyle(mountId, style, clsPrefix, styleIsolate)
|
|
7
|
+
return clsPrefix
|
|
8
|
+
}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { h, withDirectives } from 'vue'
|
|
2
|
+
import { NButton, NIcon } from 'ithinkdt-ui'
|
|
3
|
+
import { vTooltip } from '../directives'
|
|
4
|
+
|
|
5
|
+
export const renderBtn = (Icon, props, tooltip) =>
|
|
6
|
+
withDirectives(
|
|
7
|
+
h(
|
|
8
|
+
NButton,
|
|
9
|
+
{
|
|
10
|
+
text: true,
|
|
11
|
+
...props,
|
|
12
|
+
},
|
|
13
|
+
() =>
|
|
14
|
+
h(NIcon, {
|
|
15
|
+
component: Icon,
|
|
16
|
+
}),
|
|
17
|
+
),
|
|
18
|
+
[[vTooltip, tooltip]],
|
|
19
|
+
)
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"exclude": ["node_modules", "dist"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"noImplicitOverride": true,
|
|
6
|
+
"target": "ESNext",
|
|
7
|
+
"module": "NodeNext",
|
|
8
|
+
"moduleResolution": "NodeNext",
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"strictNullChecks": true
|
|
13
|
+
}
|
|
14
|
+
}
|
package/unocss.d.ts
ADDED
package/unocss.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
colors: Object.fromEntries(
|
|
3
|
+
['primary', 'success', 'warning', 'danger'].map((name) => [
|
|
4
|
+
name,
|
|
5
|
+
{
|
|
6
|
+
DEFAULT: `rgb(var(--color-${name}-rgb) / <alpha-value>)`,
|
|
7
|
+
...Object.fromEntries(
|
|
8
|
+
['hover', 'active'].map((level) => [
|
|
9
|
+
level,
|
|
10
|
+
`rgb(var(--color-${name}-${level}-rgb) / <alpha-value>)`,
|
|
11
|
+
]),
|
|
12
|
+
),
|
|
13
|
+
},
|
|
14
|
+
]),
|
|
15
|
+
),
|
|
16
|
+
}
|