hy-virtual-tree 1.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ ## Changelog
2
+
3
+ ### 1.0.0
4
+
5
+ _2025-07-28_
6
+
7
+ #### Features
8
+
9
+ - 组件 [VirtualTree] 完成开发
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025-present
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,300 @@
1
+ ## VirtualTree虚拟树
2
+
3
+ ### 一、使用案例
4
+ ```vue
5
+ <template>
6
+ <div ref="treeRef"></div>
7
+ </template>
8
+
9
+ <script>
10
+ export default {
11
+ data() {
12
+ return {
13
+ data: [
14
+ {
15
+ "id": "node-1",
16
+ "label": "node-1",
17
+ "status": 2,
18
+ "isDevice": false,
19
+ "children": [
20
+ {
21
+ "id": "node-1-1",
22
+ "label": "node-1-1",
23
+ "status": 1,
24
+ "isDevice": true
25
+ },
26
+ ]
27
+ },
28
+ {
29
+ "id": "node-2",
30
+ "label": "node-2",
31
+ "status": 1,
32
+ "isDevice": false,
33
+ "children": [
34
+ {
35
+ "id": "node-2-1",
36
+ "label": "node-2-1",
37
+ "status": 1,
38
+ "isDevice": true
39
+ },
40
+ {
41
+ "id": "node-2-2",
42
+ "label": "node-2-2",
43
+ "status": 1,
44
+ "isDevice": true
45
+ },
46
+ ]
47
+ }
48
+ ],
49
+ type: 'checkbox',
50
+ }
51
+ },
52
+ mounted() {
53
+ this.virtualTree = new VirtualTree({
54
+ // 指定容器
55
+ container: this.$refs.treeRef,
56
+ // 数据
57
+ data: this.data,
58
+ // 每一项的高度
59
+ itemHeight: 30,
60
+ // 可视区域外的缓冲项数量
61
+ bufferSize: 10,
62
+ // 可视区域外的缓冲项数量
63
+ indent: 16,
64
+ props: {
65
+ // 设置value值
66
+ value: 'id',
67
+ // 设置label值
68
+ label: 'label',
69
+ // 设置children值
70
+ children: 'children',
71
+ // 自定义每一项class值
72
+ class(data, node) {
73
+ if (node.level > 1) return 'children'
74
+ return 'parent'
75
+ },
76
+ // 自定义每一项要显示统计模块
77
+ showCount: (data, node) => {
78
+ return !data.isDevice
79
+ },
80
+ // 自定义要统计的模块(只统计最后一个子节点)
81
+ countFilter(data) {
82
+ return data.isDevice && data.status === 1
83
+ },
84
+ // 自定义要统计的模块(只统计最后一个子节点)
85
+ totalFilter(data) {
86
+ return data.isDevice
87
+ }
88
+ },
89
+ // 点击节点时,触发展开/收起
90
+ expandOnClickNode: true,
91
+ // 点击节点时,触发选中/取消选中
92
+ checkOnClickNode: false,
93
+ // 选中项配置
94
+ rowSelection: {
95
+ // 选中类型,checkbox/radio
96
+ type: this.type,
97
+ // 自定义每一项是否显示选中
98
+ showSelect: (data, node) => {
99
+ if (this.type === 'checkbox') return true
100
+ return this.type === 'radio' && data.isDevice
101
+ },
102
+ // 点击选中/取消选中触发回调
103
+ onCheckChange(data, info, checked) {
104
+ console.log("onCheckChange", data, info, checked)
105
+ }
106
+ },
107
+ // 自定义过滤逻辑
108
+ filterMethod: (params, data) => {
109
+ if (!params) return true;
110
+ return data.id === params
111
+ },
112
+ // 自定义渲染状态模块
113
+ renderStatus(data, node) {
114
+ const el = document.createElement('div')
115
+ if (data.children?.length) return;
116
+ // console.log('data', data)
117
+ el.innerHTML = data.status === 1 ? '在线' : '离线'
118
+ return el
119
+ },
120
+ // 自定义点击事件
121
+ onNodeClick(data, node, e) {
122
+ console.log("onNodeClicks??", data, node, e)
123
+ },
124
+ // 节点展开回调
125
+ onNodeExpand(data, node) {
126
+ console.log('onNodeExpand', node, data)
127
+ },
128
+ // 节点收起回调
129
+ onNodeCollapse(data, node) {
130
+ console.log('onNodeCollapse', node, data)
131
+ },
132
+ })
133
+ },
134
+ methods: {
135
+ // 全量更新数据
136
+ setData() {
137
+ this.virtualTree.setData([])
138
+ },
139
+ // 更新当前已有的数据(有新增删除项请使用 setData 方法)
140
+ updateData() {
141
+ this.virtualTree.updateData([{
142
+ "id": "node-1-1",
143
+ "label": "node-1-1.1",
144
+ "status": 1,
145
+ "isDevice": true
146
+ }])
147
+ },
148
+ setChecked(key) {
149
+ this.virtualTree.setChecked(key)
150
+ },
151
+ // 获取当前选中的选项
152
+ getCheckedKeys() {
153
+ const keys = this.virtualTree.getCheckedKeys()
154
+ const list = this.virtualTree.getCheckedNodes()
155
+ console.log('list', keys, list)
156
+ }
157
+ }
158
+ }
159
+ </script>
160
+ ```
161
+
162
+ ### 二、传参
163
+ | 选项 | 类型 | 描述 | 默认值 |
164
+ | --- | --- | --- | --- |
165
+ | container | string、HTMLElement | 容器 | - |
166
+ | data | Array<Record<string, any>> | <font style="color:rgb(48, 49, 51);">展示数据</font> | [] |
167
+ | props | Props | <font style="color:rgb(48, 49, 51);">配置选项,具体看下表</font> | |
168
+ | itemHeight | number | <font style="color:rgb(48, 49, 51);">每一行高度</font> | 30 |
169
+ | bufferSize | number | <font style="color:rgb(48, 49, 51);">可视区域外的缓冲项数量</font> | 10 |
170
+ | indent | number | <font style="color:rgb(48, 49, 51);">相邻级节点间的水平缩进,单位为像素</font> | 16 |
171
+ | rowSelection | RowSelection | <font style="color:rgba(0, 0, 0, 0.88);background-color:rgba(60, 90, 100, 0.01);">是否可选择</font> | |
172
+ | expandOnClickNode | boolean | <font style="color:rgb(48, 49, 51);">是否在点击节点的时候展开或者收缩节点</font> | true |
173
+ | checkOnClickNode | boolean | <font style="color:rgb(48, 49, 51);">是否在点击节点的时候选中节点</font> | false |
174
+ | renderIcon | (data: TreeNodeData, node: TreeNode) => HTMLElement | <font style="color:rgb(48, 49, 51);">自定义渲染图标</font> | |
175
+ | renderItem | (data: TreeNodeData, node: TreeNode) => HTMLElement | <font style="color:rgb(48, 49, 51);">自定义渲染内容</font> | |
176
+ | onNodeClick | (data: TreeNodeData, node: TreeNode, e: MouseEvent) => void | <font style="color:rgb(48, 49, 51);">当节点被鼠标左键点击的时候触发</font> | |
177
+ | onNodeContextmenu | (data: TreeNodeData, node: TreeNode, e: MouseEvent) => void | <font style="color:rgb(48, 49, 51);">当节点被鼠标右键点击的时候触发</font> | |
178
+
179
+
180
+ ### 三、Props
181
+ | 选项 | 类型 | 描述 | 默认值 |
182
+ | --- | --- | --- | --- |
183
+ | <font style="color:rgb(48, 49, 51);">value</font> | string | <font style="color:rgb(48, 49, 51);">每个树节点用来作为唯一标识的属性,在整棵树中应该是唯一的</font> | value |
184
+ | label | string | <font style="color:rgb(48, 49, 51);">指定节点标签为节点对象的某个属性值</font> | label |
185
+ | <font style="color:rgb(48, 49, 51);">children</font> | string | <font style="color:rgb(48, 49, 51);">指定子树为节点对象的某个属性值</font> | children |
186
+ | <font style="color:rgb(48, 49, 51);">disabled</font> | string | <font style="color:rgb(48, 49, 51);">指定节点选择框是否禁用为节点对象的某个属性值</font> | <font style="color:rgb(48, 49, 51);">disabled</font> |
187
+ | <font style="color:rgb(48, 49, 51);">class </font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">string/Function</font> | <font style="color:rgb(48, 49, 51);">自定义节点类名</font> | <font style="color:rgb(48, 49, 51);">-</font> |
188
+ | <font style="color:rgb(48, 49, 51);">showCount</font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">boolean | ((data: TreeNodeData, node: TreeNode) => boolean)</font> | <font style="color:rgb(48, 49, 51);">是否显示统计模块</font> | <font style="color:rgb(48, 49, 51);"></font> |
189
+ | <font style="color:rgb(48, 49, 51);">total</font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">string</font> | <font style="color:rgb(48, 49, 51);">指定统计总数属性名称</font> | <font style="color:rgb(48, 49, 51);">total</font> |
190
+ | <font style="color:rgb(48, 49, 51);">count</font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">string</font> | <font style="color:rgb(48, 49, 51);">指定统计个数属性名称</font> | <font style="color:rgb(48, 49, 51);">count</font> |
191
+ | <font style="color:rgb(48, 49, 51);">countFilter</font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">(data: TreeNodeData) => boolean</font> | <font style="color:rgb(48, 49, 51);">自定义过滤个数统计逻辑,优先级高于count</font> | <font style="color:rgb(48, 49, 51);"></font> |
192
+ | <font style="color:rgb(48, 49, 51);">totalFilter</font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">(data: TreeNodeData) => boolean</font> | <font style="color:rgb(48, 49, 51);">自定义过滤总数统计逻辑,优先级高于total</font> | <font style="color:rgb(48, 49, 51);"></font> |
193
+
194
+
195
+ ### 四、RowSelection
196
+ | 选项 | 类型 | 描述 | 默认值 |
197
+ | --- | --- | --- | --- |
198
+ | <font style="color:rgb(48, 49, 51);">type</font> | 'checkbox' | 'radio' | <font style="color:rgb(48, 49, 51);">选项类型</font> | checkbox |
199
+ | checkStrictly | boolean | <font style="color:rgb(48, 49, 51);">在type='checkbox'有效,是否严格的遵循父子不互相关联的做法</font> | false |
200
+ | <font style="color:rgb(48, 49, 51);">children</font> | string | <font style="color:rgb(48, 49, 51);">指定子树为节点对象的某个属性值</font> | children |
201
+ | <font style="color:rgb(48, 49, 51);">showSelect</font> | boolean | ((data: TreeNodeData, node: TreeNode) => boolean) | <font style="color:rgb(48, 49, 51);">是否显示选项</font> | <font style="color:rgb(48, 49, 51);">false</font> |
202
+ | <font style="color:rgb(48, 49, 51);">onCheckChange </font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">(data: TreeNodeData, info: {</font><br/><font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">checkedKeys: TreeKey[], checkedNodes: TreeNodeData[], halfCheckedKeys: TreeKey[], halfCheckedNodes: TreeNodeData[]</font><br/><font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">}, checked: boolean) => void</font> | <font style="color:rgb(48, 49, 51);">选项变化回调事件</font> | <font style="color:rgb(48, 49, 51);">-</font> |
203
+
204
+
205
+ ### 五、方法
206
+ | 选项 | 描述 | 参数 |
207
+ | --- | --- | --- |
208
+ | setData | 全量更新数据 | (data: TreeData) |
209
+ | updateData | 局部更新数据,仅可更新已有的数据 | (data: TreeData) |
210
+ | getNode | 获取指定节点数据 | (data: TreeKey | TreeNodeData) => TreeNode |
211
+ | scrollToIndex | 滚动到指定下标位置 | (index: number) |
212
+ | scrollToNode | 滚动到指定key的位置 | (key: TreeKey) |
213
+ | filter | 手动触发filterMethod函数 | (params: any) |
214
+ | <font style="color:rgb(48, 49, 51);">getCheckedKeys</font> | 若节点可被选择(即 showCheckbox 为 true),则返回目前被选中的节点所组成的数组,<font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">leafOnly </font>是否只返回根节点 | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">(leafOnly: boolean)</font> |
215
+ | getCheckedNodes | 若节点可被选择(即 showCheckbox 为 true),则返回目前被选中的节点所组成的数组,<font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">leafOnly </font>是否只返回根节点 | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">(leafOnly: boolean)</font> |
216
+ | <font style="color:rgb(48, 49, 51);">setChecked</font> | <font style="color:rgb(48, 49, 51);">通过 key 设置某个节点的勾选状态</font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">(key: TreeKey, checked: boolean)</font> |
217
+ | <font style="color:rgb(48, 49, 51);">setCheckedKeys</font> | <font style="color:rgb(48, 49, 51);">通过 keys 设置目前勾选的节点</font> | <font style="color:rgb(48, 49, 51);background-color:rgb(245, 247, 250);">(keys: TreeKey[])</font> |
218
+ | refresh | 刷新视图 | |
219
+ | destroy | 销毁组件 | |
220
+
221
+
222
+ ### 六、声明类型
223
+ ```typescript
224
+ export type TreeKey = string | number
225
+ export type TreeNodeData = Record<string, any>
226
+ export type TreeData = Array<TreeNodeData>
227
+ export interface TreeNode {
228
+ key: TreeKey
229
+ level: number
230
+ parent?: TreeNode
231
+ children?: TreeNode[]
232
+ data: TreeNodeData
233
+ disabled?: boolean
234
+ label?: string
235
+ isLeaf?: boolean
236
+ expanded?: boolean
237
+ count?: number
238
+ total?: number
239
+ }
240
+ export interface Tree {
241
+ treeNodeMap: Map<TreeKey, TreeNode>
242
+ levelTreeNodeMap: Map<number, TreeNode[]>
243
+ treeNodes: TreeNode[]
244
+ maxLevel: number
245
+ }
246
+ export interface Props {
247
+ value: string
248
+ label: string
249
+ children: string
250
+ disabled: string
251
+ class: string | ((data: TreeNodeData, node: TreeNode) => string)
252
+ // total、count 和 countFilter、totalFilter 只能二选一
253
+ showCount: boolean | ((data: TreeNodeData, node: TreeNode) => boolean) // 是否显示统计模块
254
+ total: string // 总数字段
255
+ count: string // 统计字段
256
+ countFilter?: (data: TreeNodeData) => boolean // 自定义过滤个数统计,优先级高于count
257
+ totalFilter?: (data: TreeNodeData) => boolean // 自定义过滤总数统计,优先级高于total、count
258
+ }
259
+ export interface RowSelection {
260
+ type: 'checkbox' | 'radio'
261
+ checkStrictly?: boolean // 在type='checkbox'有效,是否严格的遵循父子不互相关联的做法
262
+ showSelect?: boolean | ((data: TreeNodeData, node: TreeNode) => boolean)
263
+ onCheckChange?: (
264
+ data: TreeNodeData,
265
+ info: {
266
+ checkedKeys: TreeKey[]
267
+ checkedNodes: TreeNodeData[]
268
+ halfCheckedKeys: TreeKey[]
269
+ halfCheckedNodes: TreeNodeData[]
270
+ },
271
+ checked: boolean
272
+ ) => void
273
+ }
274
+
275
+ export interface VirtualTreeProps {
276
+ container: string | Element | HTMLElement
277
+ data: TreeData
278
+ emptyText?: string // 内容为空的时候展示的文本
279
+ props?: Partial<Props>
280
+ itemHeight?: number
281
+ bufferSize?: number
282
+ showSelected?: boolean
283
+ indent?: number
284
+ expandOnClickNode?: boolean // 是否在点击节点的时候展开或者收缩节点
285
+ checkOnClickNode?: boolean // 是否在点击节点的时候选中节点
286
+ rowSelection?: RowSelection
287
+ filterMethod?: (params: any, data: TreeNodeData, node: TreeNode) => boolean // 自定义过滤方法
288
+ renderIcon?: (data: TreeNodeData, node: TreeNode) => Element // 自定义渲染图标
289
+ renderItem?: (data: TreeNodeData, node: TreeNode) => Element // 自定义渲染内容
290
+ renderStatus?: (data: TreeNodeData, node: TreeNode) => Element | undefined // 自定义状态文本
291
+ onNodeClick?: (data: TreeNodeData, node: TreeNode, e: MouseEvent) => void // 当节点被鼠标左键点击的时候触发
292
+ onNodeContextmenu?: (
293
+ data: TreeNodeData,
294
+ node: TreeNode,
295
+ e: MouseEvent
296
+ ) => void // 当节点被鼠标右键点击的时候触发
297
+ }
298
+
299
+ ```
300
+
package/dist/index.css ADDED
@@ -0,0 +1,278 @@
1
+ .hy-checkbox {
2
+ --hy-checkbox-height: 26px;
3
+ --hy-checkbox-font-size: 14px;
4
+ --hy-checkbox-font-weight: 500;
5
+ --hy-checkbox-text-color: #fff;
6
+ --hy-checkbox-input-height: 14px;
7
+ --hy-checkbox-input-width: 14px;
8
+ --hy-checkbox-checked-icon-color: #fff;
9
+ --hy-checkbox-checked-text-color: var(--hy-color-primary);
10
+ --hy-checkbox-checked-input-border-color: var(--hy-color-primary);
11
+ --hy-checkbox-checked-bg-color: var(--hy-bg-color-primary);
12
+ --hy-checkbox-input-border: 1px solid #dcdfe6;
13
+ --hy-checkbox-input-border-color-hover: var(--hy-color-primary);
14
+ --hy-checkbox-disabled-input-fill: #f5f7fa;
15
+ --hy-checkbox-disabled-border-color: #dcdfe6;
16
+ --hy-checkbox-border-radius: 2px;
17
+ --hy-index-normal: 1;
18
+ align-items: center;
19
+ color: var(--hy-checkbox-text-color);
20
+ cursor: pointer;
21
+ display: inline-flex;
22
+ font-size: var(--hy-font-size-base);
23
+ font-weight: var(--hy-checkbox-font-weight);
24
+ height: var(--hy-checkbox-height, 32px);
25
+ margin-right: 8px;
26
+ position: relative;
27
+ -webkit-user-select: none;
28
+ -moz-user-select: none;
29
+ user-select: none;
30
+ white-space: nowrap;
31
+ }
32
+ .hy-checkbox.disabled .hy-checkbox__input {
33
+ cursor: not-allowed;
34
+ }
35
+ .hy-checkbox.is-checked .hy-checkbox__input .hy-checkbox__inner {
36
+ background-color: var(--hy-checkbox-checked-bg-color);
37
+ box-shadow: 0 0 4px 0 var(--hy-color-primary);
38
+ border-color: var(--hy-checkbox-checked-input-border-color);
39
+ }
40
+ .hy-checkbox.is-checked .hy-checkbox__input .hy-checkbox__inner::after {
41
+ border-color: var(--hy-checkbox-checked-icon-color);
42
+ transform: translate(-45%, -60%) rotate(45deg) scaleY(1);
43
+ }
44
+ .hy-checkbox.is-disabled .hy-checkbox__input .hy-checkbox__inner {
45
+ background-color: var(--hy-checkbox-disabled-input-fill);
46
+ border-color: var(--hy-checkbox-disabled-border-color);
47
+ cursor: not-allowed;
48
+ }
49
+ .hy-checkbox.is-indeterminate .hy-checkbox__input .hy-checkbox__inner {
50
+ background-color: var(--hy-checkbox-checked-bg-color);
51
+ border-color: var(--hy-checkbox-checked-input-border-color);
52
+ }
53
+ .hy-checkbox.is-indeterminate .hy-checkbox__input .hy-checkbox__inner::before {
54
+ background-color: var(--hy-checkbox-checked-icon-color);
55
+ content: "";
56
+ display: block;
57
+ height: 2px;
58
+ left: 0;
59
+ position: absolute;
60
+ right: 0;
61
+ top: 5px;
62
+ transform: scale(0.5);
63
+ }
64
+ .hy-checkbox.is-indeterminate .hy-checkbox__input .hy-checkbox__inner::after {
65
+ display: none;
66
+ }
67
+ .hy-checkbox:not(.is-disabled) .hy-checkbox__input .hy-checkbox__inner:hover {
68
+ border-color: var(--hy-checkbox-input-border-color-hover);
69
+ }
70
+ .hy-checkbox .hy-checkbox__input {
71
+ white-space: nowrap;
72
+ cursor: pointer;
73
+ outline: none;
74
+ display: inline-flex;
75
+ position: relative;
76
+ }
77
+ .hy-checkbox .hy-checkbox__input .hy-checkbox__inner {
78
+ background-color: transparent;
79
+ border: var(--hy-checkbox-input-border);
80
+ border-radius: var(--hy-checkbox-border-radius);
81
+ box-sizing: border-box;
82
+ display: inline-block;
83
+ height: var(--hy-checkbox-input-height);
84
+ position: relative;
85
+ transition: border-color 0.25s cubic-bezier(0.71, -0.46, 0.29, 1.46), background-color 0.25s cubic-bezier(0.71, -0.46, 0.29, 1.46), outline 0.25s cubic-bezier(0.71, -0.46, 0.29, 1.46);
86
+ width: var(--hy-checkbox-input-width);
87
+ z-index: var(--hy-index-normal);
88
+ }
89
+ .hy-checkbox .hy-checkbox__input .hy-checkbox__inner::after {
90
+ border: 1px solid transparent;
91
+ border-left: 0;
92
+ border-top: 0;
93
+ box-sizing: content-box;
94
+ content: "";
95
+ height: 7px;
96
+ left: 50%;
97
+ position: absolute;
98
+ top: 50%;
99
+ transform: translate(-45%, -60%) rotate(45deg) scaleY(0);
100
+ transform-origin: center;
101
+ transition: transform 0.15s ease-in 0.05s;
102
+ width: 3px;
103
+ }
104
+ .hy-checkbox .hy-checkbox__label {
105
+ display: inline-block;
106
+ padding-left: 8px;
107
+ line-height: 1;
108
+ font-size: var(--hy-font-size-base);
109
+ color: var(--hy-checkbox-checked-text-color);
110
+ }
111
+
112
+ .hy-radio {
113
+ --hy-radio-height: 26px;
114
+ --hy-radio-font-size: 14px;
115
+ --hy-radio-font-weight: 500;
116
+ --hy-radio-text-color: #fff;
117
+ --hy-radio-input-height: 14px;
118
+ --hy-radio-input-width: 14px;
119
+ --hy-radio-checked-icon-color: #fff;
120
+ --hy-radio-checked-text-color: var(--hy-color-primary);
121
+ --hy-radio-checked-input-border-color: var(--hy-color-primary);
122
+ --hy-radio-checked-bg-color: transparent;
123
+ --hy-radio-input-border: 1px solid #dcdfe6;
124
+ --hy-radio-input-border-color-hover: var(--hy-color-primary);
125
+ --hy-radio-disabled-input-fill: #f5f7fa;
126
+ --hy-radio-disabled-border-color: #dcdfe6;
127
+ --hy-radio-border-radius: 100%;
128
+ --hy-index-normal: 1;
129
+ align-items: center;
130
+ color: var(--hy-radio-text-color);
131
+ cursor: pointer;
132
+ display: inline-flex;
133
+ font-size: var(--hy-font-size-base);
134
+ font-weight: var(--hy-radio-font-weight);
135
+ height: var(--hy-radio-height, 32px);
136
+ margin-right: 8px;
137
+ position: relative;
138
+ -webkit-user-select: none;
139
+ -moz-user-select: none;
140
+ user-select: none;
141
+ white-space: nowrap;
142
+ }
143
+ .hy-radio.disabled .hy-radio__input {
144
+ cursor: not-allowed;
145
+ }
146
+ .hy-radio.is-checked .hy-radio__input .hy-radio__inner {
147
+ background-color: var(--hy-radio-checked-bg-color);
148
+ border-color: var(--hy-radio-checked-input-border-color);
149
+ }
150
+ .hy-radio.is-checked .hy-radio__input .hy-radio__inner::after {
151
+ transform: translate(-50%, -50%) scale(1);
152
+ }
153
+ .hy-radio.is-disabled .hy-radio__input .hy-radio__inner {
154
+ background-color: var(--hy-radio-disabled-input-fill);
155
+ border-color: var(--hy-radio-disabled-border-color);
156
+ cursor: not-allowed;
157
+ }
158
+ .hy-radio:not(.is-disabled) .hy-radio__input .hy-radio__inner:hover {
159
+ border-color: var(--hy-radio-input-border-color-hover);
160
+ }
161
+ .hy-radio .hy-radio__input {
162
+ white-space: nowrap;
163
+ cursor: pointer;
164
+ outline: none;
165
+ display: inline-flex;
166
+ position: relative;
167
+ }
168
+ .hy-radio .hy-radio__input .hy-radio__inner {
169
+ background-color: transparent;
170
+ border: var(--hy-radio-input-border);
171
+ border-radius: var(--hy-radio-border-radius);
172
+ box-sizing: border-box;
173
+ display: inline-block;
174
+ height: var(--hy-radio-input-height);
175
+ position: relative;
176
+ transition: border-color 0.25s cubic-bezier(0.71, -0.46, 0.29, 1.46), background-color 0.25s cubic-bezier(0.71, -0.46, 0.29, 1.46), outline 0.25s cubic-bezier(0.71, -0.46, 0.29, 1.46);
177
+ width: var(--hy-radio-input-width);
178
+ z-index: var(--hy-index-normal);
179
+ }
180
+ .hy-radio .hy-radio__input .hy-radio__inner::after {
181
+ width: 4px;
182
+ height: 4px;
183
+ border-radius: var(--hy-radio-border-radius);
184
+ background-color: var(--hy-radio-checked-input-border-color);
185
+ content: "";
186
+ position: absolute;
187
+ left: 50%;
188
+ top: 50%;
189
+ transform: translate(-50%, -50%) scale(0);
190
+ transform-origin: center;
191
+ transition: transform 0.15s ease-in 0.05s;
192
+ }
193
+ .hy-radio .hy-radio__label {
194
+ display: inline-block;
195
+ padding-left: 8px;
196
+ line-height: 1;
197
+ font-size: var(--hy-font-size-base);
198
+ color: var(--hy-radio-checked-text-color);
199
+ }
200
+
201
+ .hy-tree {
202
+ --icon-color: #ffffff;
203
+ --text-color: #ffffff;
204
+ --text-size: 14px;
205
+ --node-bg: #1c334a;
206
+ --node-hover-bg: var(--hy-color-primary);
207
+ color: var(--text-color);
208
+ background: var(--node-bg);
209
+ }
210
+ .hy-tree > div {
211
+ padding: 0 10px;
212
+ }
213
+ .hy-tree .hy-tree-node {
214
+ width: 100%;
215
+ padding: 0 10px;
216
+ cursor: pointer;
217
+ border: 1px solid transparent;
218
+ box-sizing: border-box;
219
+ border-radius: 4px;
220
+ }
221
+ .hy-tree .hy-tree-node:hover {
222
+ box-shadow: 0 0 10px 0 var(--node-hover-bg) inset;
223
+ border: 1px solid var(--node-hover-bg);
224
+ }
225
+ .hy-tree .hy-tree-node.active {
226
+ box-shadow: 0 0 20px 0 var(--node-hover-bg) inset;
227
+ }
228
+ .hy-tree .hy-tree-node .hy-tree-node__content {
229
+ display: flex;
230
+ align-items: center;
231
+ }
232
+ .hy-tree .hy-expand {
233
+ width: var(--text-size);
234
+ height: var(--text-size);
235
+ margin-right: 4px;
236
+ cursor: pointer;
237
+ display: flex;
238
+ align-items: center;
239
+ justify-content: center;
240
+ }
241
+ .hy-tree .hy-expand.expanded .hy-expand-icon {
242
+ transform: rotate(0);
243
+ }
244
+ .hy-tree .hy-expand .hy-expand-icon {
245
+ --icon-size: 6px;
246
+ width: 0;
247
+ height: 0;
248
+ border-top: var(--icon-size) solid var(--icon-color);
249
+ border-left: var(--icon-size) solid transparent;
250
+ border-right: var(--icon-size) solid transparent;
251
+ text-align: center;
252
+ transform: rotate(-90deg);
253
+ transition: all 0.3s linear;
254
+ }
255
+ .hy-tree .hy-tree-icon {
256
+ margin-right: 4px;
257
+ }
258
+ .hy-tree .hy-tree-content {
259
+ width: 0;
260
+ flex: 1;
261
+ font-size: var(--text-size);
262
+ }
263
+ .hy-tree .hy-tree-content .hy-tree-statistics {
264
+ margin-left: 4px;
265
+ }
266
+ .hy-tree .hy-tree-content .hy-tree-statistics > span {
267
+ color: var(--hy-color-primary);
268
+ }
269
+ .hy-tree .disabled {
270
+ cursor: disabled;
271
+ }
272
+
273
+ :root {
274
+ --hy-font-size-base: 14px;
275
+ --hy-color-primary: #2df2ff;
276
+ --hy-bg-color-primary: rgba(45, 242, 255, 0.5);
277
+ --hy-text-color-placeholder: #a8abb2;
278
+ }