@ticatec/topside-elements 1.2.1 → 1.2.3
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/dist/data_display/tree_view/README.md +2 -0
- package/dist/data_display/tree_view/README_CN.md +2 -0
- package/dist/data_display/tree_view/TreeNodeView.svelte +29 -16
- package/dist/data_display/tree_view/TreeNodeView.svelte.d.ts +3 -1
- package/dist/data_display/tree_view/TreeView.svelte +13 -1
- package/dist/data_display/tree_view/TreeView.svelte.d.ts +4 -0
- package/dist/general/button/IconButton.svelte +24 -2
- package/dist/general/button/IconButton.svelte.d.ts +13 -0
- package/dist/general/button/IconButton_README.md +27 -2
- package/dist/general/button/IconButton_README_CN.md +27 -2
- package/dist/general/button/index.d.ts +2 -2
- package/dist/general/button/index.js +1 -1
- package/dist/overlay/dialog/DialogManager.svelte.d.ts +3 -1
- package/dist/overlay/dialog/DialogManager.svelte.js +18 -1
- package/dist/overlay/dialog/IDialog.d.ts +6 -0
- package/dist/overlay/dialog/README.md +25 -0
- package/dist/overlay/dialog/README_CN.md +25 -0
- package/dist/overlay/dialog/index.d.ts +2 -2
- package/dist/topside-elements.css +18 -0
- package/docs/components/README.md +3 -3
- package/docs/components/icon-button/README.md +16 -0
- package/docs/components/icon-button/example.svelte +2 -0
- package/docs/components/tree-view/README.md +2 -0
- package/package.json +1 -1
|
@@ -41,6 +41,8 @@ import {HierarchyTree} from "@ticatec/topside-elements/TreeNodes";
|
|
|
41
41
|
| `lazyLoader` | `TreeLazyLoader \| null` | `null` | Interface for loading child nodes on demand |
|
|
42
42
|
| `activeNode` | `any` | `null` | The currently selected node (supports `bind:activeNode` two-way binding) |
|
|
43
43
|
| `defaultExpandDepth` | `number` | `0` | Initial expand depth (0 = fully collapsed, 1 = expand root nodes, and so on) |
|
|
44
|
+
| `expandOnClick` | `boolean` | `true` | Whether clicking a directory row toggles its expanded/collapsed state. Defaults to `true` |
|
|
45
|
+
| `showChevron` | `boolean` | `true` | Whether to display the leading expand/collapse chevron icon. Defaults to `true`. When `expandOnClick` is `false`, this is forced to `true` (cannot be hidden) so users always have a way to open the branch |
|
|
44
46
|
| `isVisible` | `NodeVisibleFun` | - | Predicate that filters which nodes are visible |
|
|
45
47
|
| `onchange` | `OnNodeSelectionChange` | - | Callback fired when the selection changes; return `false` to cancel the selection |
|
|
46
48
|
| `onfocus` | `(event: FocusEvent) => void` | - | Callback fired when the tree container gains focus |
|
|
@@ -41,6 +41,8 @@ import {HierarchyTree} from "@ticatec/topside-elements/TreeNodes";
|
|
|
41
41
|
| `lazyLoader` | `TreeLazyLoader \| null` | `null` | 异步懒加载子节点的接口 |
|
|
42
42
|
| `activeNode` | `any` | `null` | 当前选中的节点(支持 `bind:activeNode` 双向绑定) |
|
|
43
43
|
| `defaultExpandDepth` | `number` | `0` | 初始展开深度(0 = 全折叠,1 = 展开根节点,依此类推) |
|
|
44
|
+
| `expandOnClick` | `boolean` | `true` | 是否允许点击目录行时自动切换展开/折叠。默认 `true` |
|
|
45
|
+
| `showChevron` | `boolean` | `true` | 是否显示最前面用于展开/折叠的 chevron 图标。默认 `true`。若 `expandOnClick` 为 `false` 则强制为 `true`(不能隐藏,保证用户拥有打开入口) |
|
|
44
46
|
| `isVisible` | `NodeVisibleFun` | - | 控制节点可见性的过滤函数 |
|
|
45
47
|
| `onchange` | `OnNodeSelectionChange` | - | 节点选择变化时的回调函数(返回 `false` 可取消本次选择) |
|
|
46
48
|
| `onfocus` | `(event: FocusEvent) => void` | - | 树容器获得焦点时的回调 |
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
expandable: boolean;
|
|
18
18
|
expanded: boolean;
|
|
19
19
|
loading?: boolean;
|
|
20
|
-
|
|
20
|
+
expandOnClick?: boolean;
|
|
21
|
+
showChevron?: boolean;
|
|
22
|
+
ontoggle: () => void | Promise<void>;
|
|
21
23
|
onselect: (node: IHierarchyData<any>) => Promise<void>;
|
|
22
24
|
oncontextmenu?: OnContextMenu;
|
|
23
25
|
}
|
|
@@ -32,6 +34,8 @@
|
|
|
32
34
|
expandable,
|
|
33
35
|
expanded,
|
|
34
36
|
loading = false,
|
|
37
|
+
expandOnClick = true,
|
|
38
|
+
showChevron = true,
|
|
35
39
|
ontoggle,
|
|
36
40
|
onselect,
|
|
37
41
|
oncontextmenu
|
|
@@ -65,6 +69,9 @@
|
|
|
65
69
|
|
|
66
70
|
const handleRowClick = async () => {
|
|
67
71
|
await onselect(node);
|
|
72
|
+
if (expandOnClick && expandable) {
|
|
73
|
+
await ontoggle();
|
|
74
|
+
}
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
const handleRowKeyDown = async (e: KeyboardEvent) => {
|
|
@@ -92,23 +99,29 @@
|
|
|
92
99
|
oncontextmenu={handleContextMenu} role="treeitem" tabindex="0" aria-selected={isSelected}
|
|
93
100
|
aria-expanded={expandable ? expanded : undefined}>
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{#if
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
{#if showChevron}
|
|
103
|
+
<div class="item-chevron">
|
|
104
|
+
{#if expandable}
|
|
105
|
+
{#if loading}
|
|
106
|
+
<img alt="loading" class="chevron-img" src={iconLoading}/>
|
|
107
|
+
{:else if chevronIcon}
|
|
108
|
+
<span role="button" tabindex="0" aria-label={expanded ? 'Collapse' : 'Expand'} onclick={handleIconClick} onkeydown={handleIconKeyDown} class="chevron-btn">
|
|
109
|
+
{@render chevronIcon({ expanded, loading })}
|
|
110
|
+
</span>
|
|
111
|
+
{:else}
|
|
112
|
+
<span role="button" tabindex="0" aria-label={expanded ? 'Collapse' : 'Expand'} onclick={handleIconClick} onkeydown={handleIconKeyDown} class="chevron-btn default-arrow">
|
|
113
|
+
{expanded ? '▾' : '▸'}
|
|
114
|
+
</span>
|
|
115
|
+
{/if}
|
|
103
116
|
{:else}
|
|
104
|
-
<span
|
|
105
|
-
{expanded ? '▾' : '▸'}
|
|
106
|
-
</span>
|
|
117
|
+
<span class="chevron-spacer"></span>
|
|
107
118
|
{/if}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
</div>
|
|
120
|
+
{:else if loading}
|
|
121
|
+
<div class="item-chevron">
|
|
122
|
+
<img alt="loading" class="chevron-img" src={iconLoading}/>
|
|
123
|
+
</div>
|
|
124
|
+
{/if}
|
|
112
125
|
|
|
113
126
|
{#if nodeIcon}
|
|
114
127
|
<div class="node-type-icon">
|
|
@@ -13,7 +13,9 @@ interface Props {
|
|
|
13
13
|
expandable: boolean;
|
|
14
14
|
expanded: boolean;
|
|
15
15
|
loading?: boolean;
|
|
16
|
-
|
|
16
|
+
expandOnClick?: boolean;
|
|
17
|
+
showChevron?: boolean;
|
|
18
|
+
ontoggle: () => void | Promise<void>;
|
|
17
19
|
onselect: (node: IHierarchyData<any>) => Promise<void>;
|
|
18
20
|
oncontextmenu?: OnContextMenu;
|
|
19
21
|
}
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
title?: string;
|
|
49
49
|
/** 初始展开多少层(0 = 全部折叠,1 = 展开根节点,依此类推)。默认 0。 */
|
|
50
50
|
defaultExpandDepth?: number;
|
|
51
|
+
/** 点击目录时是否自动切换展开/折叠。默认 true。 */
|
|
52
|
+
expandOnClick?: boolean;
|
|
53
|
+
/** 是否显示最前面用于展开/折叠的 chevron 图标。默认 true;若 expandOnClick 为 false 则不能隐藏(强制为 true)。 */
|
|
54
|
+
showChevron?: boolean;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
let {
|
|
@@ -67,9 +71,15 @@
|
|
|
67
71
|
onblur,
|
|
68
72
|
oncontextmenu,
|
|
69
73
|
title = undefined,
|
|
70
|
-
defaultExpandDepth = 0
|
|
74
|
+
defaultExpandDepth = 0,
|
|
75
|
+
expandOnClick = true,
|
|
76
|
+
showChevron = true
|
|
71
77
|
}: Props = $props();
|
|
72
78
|
|
|
79
|
+
// 只有当 expandOnClick 为 true 时,才允许隐藏 chevron。
|
|
80
|
+
// 如果 expandOnClick 为 false(即点击目录不自动打开),根据规则强制不能隐藏(保持用于打开)。
|
|
81
|
+
let effectiveShowChevron = $derived(!expandOnClick ? true : showChevron);
|
|
82
|
+
|
|
73
83
|
// 展开 / 已加载 / 加载中的 key 集合。SvelteSet 让 .add/.delete 触发响应式更新。
|
|
74
84
|
let expanded = new SvelteSet<string | number>();
|
|
75
85
|
let loaded = new SvelteSet<string | number>();
|
|
@@ -167,6 +177,8 @@
|
|
|
167
177
|
{chevronIcon}
|
|
168
178
|
{activeNode}
|
|
169
179
|
expandable={isBranch(node)}
|
|
180
|
+
{expandOnClick}
|
|
181
|
+
showChevron={effectiveShowChevron}
|
|
170
182
|
{oncontextmenu}
|
|
171
183
|
onselect={selectNode}
|
|
172
184
|
expanded={expanded.has(tree.getKey(node.data))}
|
|
@@ -39,6 +39,10 @@ interface Props {
|
|
|
39
39
|
title?: string;
|
|
40
40
|
/** 初始展开多少层(0 = 全部折叠,1 = 展开根节点,依此类推)。默认 0。 */
|
|
41
41
|
defaultExpandDepth?: number;
|
|
42
|
+
/** 点击目录时是否自动切换展开/折叠。默认 true。 */
|
|
43
|
+
expandOnClick?: boolean;
|
|
44
|
+
/** 是否显示最前面用于展开/折叠的 chevron 图标。默认 true;若 expandOnClick 为 false 则不能隐藏(强制为 true)。 */
|
|
45
|
+
showChevron?: boolean;
|
|
42
46
|
}
|
|
43
47
|
declare const TreeView: import("svelte").Component<Props, {}, "activeNode">;
|
|
44
48
|
type TreeView = ReturnType<typeof TreeView>;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
|
|
5
5
|
type ButtonType = 'default' | 'primary' | 'secondary' | 'third' | 'forth';
|
|
6
|
+
export type IconButtonVariant = 'circle' | 'round';
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
type?: ButtonType;
|
|
@@ -18,6 +19,18 @@
|
|
|
18
19
|
children?: Snippet;
|
|
19
20
|
tabindex?: number;
|
|
20
21
|
outer?: boolean;
|
|
22
|
+
/** 形状变体:'circle' (默认正圆) | 'round' (圆角矩形) */
|
|
23
|
+
variant?: IconButtonVariant;
|
|
24
|
+
/** 形状别名,支持与 variant 相同的取值 */
|
|
25
|
+
shape?: IconButtonVariant;
|
|
26
|
+
/** 是否为圆角(round corner 便捷布尔属性) */
|
|
27
|
+
round?: boolean;
|
|
28
|
+
/** 是否无边框 */
|
|
29
|
+
borderless?: boolean;
|
|
30
|
+
/** 是否有边框(默认为 true,设为 false 时等同于 borderless) */
|
|
31
|
+
border?: boolean;
|
|
32
|
+
/** 是否有边框(border 别名) */
|
|
33
|
+
bordered?: boolean;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
let {
|
|
@@ -32,9 +45,18 @@
|
|
|
32
45
|
theme,
|
|
33
46
|
children,
|
|
34
47
|
tabindex = -1,
|
|
35
|
-
outer = true
|
|
48
|
+
outer = true,
|
|
49
|
+
variant = 'circle',
|
|
50
|
+
shape,
|
|
51
|
+
round = false,
|
|
52
|
+
borderless = false,
|
|
53
|
+
border = true,
|
|
54
|
+
bordered = true
|
|
36
55
|
}: Props = $props();
|
|
37
56
|
|
|
57
|
+
const isRound = $derived(round || shape === 'round' || variant === 'round');
|
|
58
|
+
const shapeClass = $derived(isRound ? 'round' : 'circle');
|
|
59
|
+
const isBorderless = $derived(borderless || !border || !bordered);
|
|
38
60
|
|
|
39
61
|
const handleClick = (e: MouseEvent) => {
|
|
40
62
|
if (!disabled) {
|
|
@@ -48,7 +70,7 @@
|
|
|
48
70
|
|
|
49
71
|
</script>
|
|
50
72
|
|
|
51
|
-
<button type={htmlType} class="topside-button icon-button {size} {type} {theme}" class:outer class:no-outer={!outer} {style} class:disabled {disabled}
|
|
73
|
+
<button type={htmlType} class="topside-button icon-button {size} {type} {shapeClass} {theme}" class:outer class:no-outer={!outer} class:borderless={isBorderless} {style} class:disabled {disabled}
|
|
52
74
|
tabindex={disabled ? -1 : tabindex} data-tip={disabled ? null : tooltip} onclick={handleClick}>
|
|
53
75
|
{#if children}
|
|
54
76
|
{@render children()}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Size } from "../../types";
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
type ButtonType = 'default' | 'primary' | 'secondary' | 'third' | 'forth';
|
|
4
|
+
export type IconButtonVariant = 'circle' | 'round';
|
|
4
5
|
interface Props {
|
|
5
6
|
type?: ButtonType;
|
|
6
7
|
htmlType?: 'button' | 'submit' | 'reset';
|
|
@@ -15,6 +16,18 @@ interface Props {
|
|
|
15
16
|
children?: Snippet;
|
|
16
17
|
tabindex?: number;
|
|
17
18
|
outer?: boolean;
|
|
19
|
+
/** 形状变体:'circle' (默认正圆) | 'round' (圆角矩形) */
|
|
20
|
+
variant?: IconButtonVariant;
|
|
21
|
+
/** 形状别名,支持与 variant 相同的取值 */
|
|
22
|
+
shape?: IconButtonVariant;
|
|
23
|
+
/** 是否为圆角(round corner 便捷布尔属性) */
|
|
24
|
+
round?: boolean;
|
|
25
|
+
/** 是否无边框 */
|
|
26
|
+
borderless?: boolean;
|
|
27
|
+
/** 是否有边框(默认为 true,设为 false 时等同于 borderless) */
|
|
28
|
+
border?: boolean;
|
|
29
|
+
/** 是否有边框(border 别名) */
|
|
30
|
+
bordered?: boolean;
|
|
18
31
|
}
|
|
19
32
|
declare const IconButton: import("svelte").Component<Props, {}, "">;
|
|
20
33
|
type IconButton = ReturnType<typeof IconButton>;
|
|
@@ -37,7 +37,12 @@ const handleClick = (event) => {
|
|
|
37
37
|
|------|------|---------|-------------|
|
|
38
38
|
| `type` | `'default' \| 'primary' \| 'secondary' \| 'third' \| 'forth'` | `'default'` | Button type/style |
|
|
39
39
|
| `disabled` | `boolean` | `false` | Whether the button is disabled |
|
|
40
|
-
| `size` | `'
|
|
40
|
+
| `size` | `'large' \| 'medium' \| 'small'` | `'medium'` | Button size |
|
|
41
|
+
| `variant` | `'circle' \| 'round'` | `'circle'` | Button shape variant: `'circle'` for circular, `'round'` for round corner (rounded square) |
|
|
42
|
+
| `shape` | `'circle' \| 'round'` | `'circle'` | Shape alias, supports the same values as `variant` |
|
|
43
|
+
| `round` | `boolean` | `false` | Shorthand boolean for rounded corner (equivalent to `variant="round"`) |
|
|
44
|
+
| `borderless` | `boolean` | `false` | Whether the button is borderless |
|
|
45
|
+
| `border` | `boolean` | `true` | Whether the button has a border (setting `false` is equivalent to `borderless`) |
|
|
41
46
|
| `style` | `string` | `''` | Custom CSS styles |
|
|
42
47
|
| `icon` | `string` | `''` | Icon class name (e.g., FontAwesome classes) |
|
|
43
48
|
| `theme` | `string` | - | Theme variant |
|
|
@@ -45,7 +50,27 @@ const handleClick = (event) => {
|
|
|
45
50
|
| `onclick` | `(event: MouseEvent) => void` | - | Click event handler |
|
|
46
51
|
| `children` | `Snippet` | - | Custom icon content snippet |
|
|
47
52
|
| `tabindex` | `number` | `-1` | Tab index (defaults to -1 so button does not take Tab focus) |
|
|
48
|
-
| `outer` | `boolean` | `true` | Whether to show outer frame/ring when focused or active (defaults to true; set to false to remove focus/active outer frame) |
|
|
53
|
+
| `outer` | `boolean` | `true` | Whether to show outer frame/ring when focused or active (defaults to true; set to false to remove focus/active outer frame and border) |
|
|
54
|
+
|
|
55
|
+
## Shape and Border
|
|
56
|
+
|
|
57
|
+
By default, IconButton renders as a circle (`circle`) with a subtle border. You can set it to a rounded corner square via `variant="round"` or `round={true}`, and remove the border with `borderless={true}` or `border={false}`.
|
|
58
|
+
|
|
59
|
+
```svelte
|
|
60
|
+
<!-- Default circular with border -->
|
|
61
|
+
<IconButton icon="icon_google_add" type="primary" />
|
|
62
|
+
|
|
63
|
+
<!-- Round corner -->
|
|
64
|
+
<IconButton icon="icon_google_add" type="primary" variant="round" />
|
|
65
|
+
<!-- Or using the round shorthand boolean -->
|
|
66
|
+
<IconButton icon="icon_google_add" type="primary" round />
|
|
67
|
+
|
|
68
|
+
<!-- Borderless circle -->
|
|
69
|
+
<IconButton icon="icon_google_favorite" type="primary" borderless={true} />
|
|
70
|
+
|
|
71
|
+
<!-- Borderless round corner -->
|
|
72
|
+
<IconButton icon="icon_google_favorite" type="primary" variant="round" borderless={true} />
|
|
73
|
+
```
|
|
49
74
|
|
|
50
75
|
## Button Types
|
|
51
76
|
|
|
@@ -37,7 +37,12 @@ const handleClick = (event) => {
|
|
|
37
37
|
|------|------|---------|-------------|
|
|
38
38
|
| `type` | `'default' \| 'primary' \| 'secondary' \| 'third' \| 'forth'` | `'default'` | 按钮类型/样式 |
|
|
39
39
|
| `disabled` | `boolean` | `false` | 是否禁用按钮 |
|
|
40
|
-
| `size` | `'
|
|
40
|
+
| `size` | `'large' \| 'medium' \| 'small'` | `'medium'` | 按钮尺寸 |
|
|
41
|
+
| `variant` | `'circle' \| 'round'` | `'circle'` | 按钮形状变体:`'circle'` 为正圆,`'round'` 为圆角矩形 |
|
|
42
|
+
| `shape` | `'circle' \| 'round'` | `'circle'` | 形状别名,支持与 `variant` 相同的取值 |
|
|
43
|
+
| `round` | `boolean` | `false` | 是否为圆角矩形(等同于 `variant="round"`) |
|
|
44
|
+
| `borderless` | `boolean` | `false` | 是否无边框 |
|
|
45
|
+
| `border` | `boolean` | `true` | 是否有边框(设为 `false` 时等同于 `borderless`) |
|
|
41
46
|
| `style` | `string` | `''` | 自定义CSS样式 |
|
|
42
47
|
| `icon` | `string` | `''` | 图标类名(如FontAwesome类) |
|
|
43
48
|
| `theme` | `string` | - | 主题变体 |
|
|
@@ -45,7 +50,27 @@ const handleClick = (event) => {
|
|
|
45
50
|
| `onclick` | `(event: MouseEvent) => void` | - | 点击事件处理器 |
|
|
46
51
|
| `children` | `Snippet` | - | 自定义图标内容片段 |
|
|
47
52
|
| `tabindex` | `number` | `-1` | Tab 键导航顺序(默认为 -1,取消 Tab 键停留) |
|
|
48
|
-
| `outer` | `boolean` | `true` | focused / active 状态时是否带外框(默认为 true,设为 false
|
|
53
|
+
| `outer` | `boolean` | `true` | focused / active 状态时是否带外框(默认为 true,设为 false 时去除聚焦/激活外框与边框) |
|
|
54
|
+
|
|
55
|
+
## 形状与边框
|
|
56
|
+
|
|
57
|
+
IconButton 默认呈现为正圆(`circle`)且带有细边框。你可以通过 `variant="round"` 或 `round={true}` 将其设为圆角矩形(round corner),并通过 `borderless={true}` 或 `border={false}` 设置为无边框。
|
|
58
|
+
|
|
59
|
+
```svelte
|
|
60
|
+
<!-- 默认正圆带边框 -->
|
|
61
|
+
<IconButton icon="icon_google_add" type="primary" />
|
|
62
|
+
|
|
63
|
+
<!-- 圆角矩形 (round corner) -->
|
|
64
|
+
<IconButton icon="icon_google_add" type="primary" variant="round" />
|
|
65
|
+
<!-- 或使用便捷布尔属性 -->
|
|
66
|
+
<IconButton icon="icon_google_add" type="primary" round />
|
|
67
|
+
|
|
68
|
+
<!-- 无边框正圆 -->
|
|
69
|
+
<IconButton icon="icon_google_favorite" type="primary" borderless={true} />
|
|
70
|
+
|
|
71
|
+
<!-- 无边框圆角矩形 -->
|
|
72
|
+
<IconButton icon="icon_google_favorite" type="primary" variant="round" borderless={true} />
|
|
73
|
+
```
|
|
49
74
|
|
|
50
75
|
## 按钮类型
|
|
51
76
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Button from "./Button.svelte";
|
|
2
|
-
import IconButton from "./IconButton.svelte";
|
|
2
|
+
import IconButton, { type IconButtonVariant } from "./IconButton.svelte";
|
|
3
3
|
import TextButton from "./TextButton.svelte";
|
|
4
4
|
import type { ButtonType } from "../../types";
|
|
5
5
|
export default Button;
|
|
6
6
|
export { IconButton };
|
|
7
7
|
export { TextButton };
|
|
8
|
-
export type { ButtonType };
|
|
8
|
+
export type { ButtonType, IconButtonVariant };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModalResult, type OnClose } from './IDialog';
|
|
1
|
+
import { ModalResult, type OnClose, type DialogOptions } from './IDialog';
|
|
2
2
|
export interface DialogEntry {
|
|
3
3
|
id: string;
|
|
4
4
|
component: any;
|
|
@@ -14,6 +14,8 @@ export declare function _isBoardMounted(): boolean;
|
|
|
14
14
|
export declare function _markBoardMounted(): void;
|
|
15
15
|
declare class DialogManager {
|
|
16
16
|
stack: DialogEntry[];
|
|
17
|
+
/** 首次打开前配置挂载容器;不调用时默认挂载到 document.body。 */
|
|
18
|
+
init({ target }?: DialogOptions): void;
|
|
17
19
|
show(component: any, params?: Record<string, any>): Promise<ModalResult>;
|
|
18
20
|
/**
|
|
19
21
|
* 内部使用:由 Dialog / CommonDialog 调用,触发对应 entry 的 Promise resolve 并出栈。
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import DialogBoard from './DialogBoard.svelte';
|
|
2
|
+
import { mount } from 'svelte';
|
|
2
3
|
import { ModalResult } from './IDialog';
|
|
3
4
|
import { mountToBody } from '../common/mountSingleton';
|
|
4
5
|
let boardMounted = false;
|
|
6
|
+
let showCalled = false;
|
|
7
|
+
let mountTarget = null;
|
|
5
8
|
/**
|
|
6
9
|
* 内部使用:DialogBoard 在组件初始化时检查并标记,确保全局只有一个 board 实际渲染 stack。
|
|
7
10
|
* 防止"用户在 app 根放了 <DialogBoard/> + manager 自动 mount"导致的重复渲染。
|
|
@@ -20,11 +23,24 @@ function ensureMounted() {
|
|
|
20
23
|
// _markBoardMounted() on init, since a board the consumer placed manually
|
|
21
24
|
// in their app root also needs to mark it mounted, not just this
|
|
22
25
|
// auto-mount path.
|
|
23
|
-
|
|
26
|
+
if (mountTarget) {
|
|
27
|
+
mount(DialogBoard, { target: mountTarget });
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
mountToBody(DialogBoard);
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
33
|
class DialogManager {
|
|
26
34
|
stack = $state([]);
|
|
35
|
+
/** 首次打开前配置挂载容器;不调用时默认挂载到 document.body。 */
|
|
36
|
+
init({ target = null } = {}) {
|
|
37
|
+
if (showCalled || boardMounted) {
|
|
38
|
+
throw new Error('Dialog target is locked. Call init() before the first show() and before mounting DialogBoard.');
|
|
39
|
+
}
|
|
40
|
+
mountTarget = target;
|
|
41
|
+
}
|
|
27
42
|
show(component, params = {}) {
|
|
43
|
+
showCalled = true;
|
|
28
44
|
ensureMounted();
|
|
29
45
|
const id = Math.floor((Date.now() + Math.random()) * 100).toString(36);
|
|
30
46
|
// onclose 为规范名;onClose 为 5.x 兼容别名(NAMING R1)
|
|
@@ -49,6 +65,7 @@ class DialogManager {
|
|
|
49
65
|
const dialogManager = new DialogManager();
|
|
50
66
|
if (typeof window !== 'undefined') {
|
|
51
67
|
window.Dialog = {
|
|
68
|
+
init: options => dialogManager.init(options),
|
|
52
69
|
show: (component, params) => dialogManager.show(component, params),
|
|
53
70
|
showModal: (component, params) => {
|
|
54
71
|
void dialogManager.show(component, params);
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { ModalResult } from "../message-box/IMessageBox";
|
|
2
2
|
export { ModalResult };
|
|
3
3
|
export type OnClose = (result: ModalResult) => void;
|
|
4
|
+
export interface DialogOptions {
|
|
5
|
+
/** 自动挂载的容器;省略或为 null 时使用 document.body。首次打开前配置。 */
|
|
6
|
+
target?: HTMLElement | null;
|
|
7
|
+
}
|
|
4
8
|
export default interface IDialog {
|
|
9
|
+
/** 配置自动挂载容器,必须在首次 show() 和 DialogBoard 挂载前调用。 */
|
|
10
|
+
init: (options?: DialogOptions) => void;
|
|
5
11
|
/**
|
|
6
12
|
* 打开一个对话框,返回 Promise<ModalResult>。
|
|
7
13
|
* 调用者可 await 获取用户的关闭结果(MR_OK / MR_CANCEL / MR_CLOSE)。
|
|
@@ -36,6 +36,31 @@ import Dialog, { dialogManager, CommonDialog, ModalResult } from "@ticatec/topsi
|
|
|
36
36
|
|
|
37
37
|
> **Legacy compatibility**: if you previously placed `<DialogBoard/>` in your app root, you can leave it or remove it. When multiple boards coexist, only the first one to mount actually renders the stack — dialogs are never duplicated.
|
|
38
38
|
|
|
39
|
+
### Custom mount container
|
|
40
|
+
|
|
41
|
+
The default mount target is `document.body`. To render dialogs inside a specific div, call `init` after the element exists and before opening the first dialog:
|
|
42
|
+
|
|
43
|
+
```svelte
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
import {onMount} from 'svelte';
|
|
46
|
+
import {dialogManager} from '@ticatec/topside-elements/Dialog';
|
|
47
|
+
|
|
48
|
+
let dialogHost: HTMLDivElement;
|
|
49
|
+
|
|
50
|
+
onMount(() => {
|
|
51
|
+
dialogManager.init({target: dialogHost});
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<div bind:this={dialogHost}></div>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
All subsequent `dialogManager.show(...)` / `window.Dialog.show(...)` calls render inside this container. `window.Dialog.init({target: element})` is also supported. Calling `init()` or `init({target: null})` selects the default body target.
|
|
59
|
+
|
|
60
|
+
`init` only configures the target; mounting still happens on the first `show`. Keep the container mounted while using the dialog system. Calling `init` after the first `show` call or after DialogBoard has mounted throws an error, even if mounting failed or all dialogs have closed. A manually placed `<DialogBoard/>` uses its actual location, so omit this configuration in that mode. Directly rendered `<Dialog>` components are unaffected.
|
|
61
|
+
|
|
62
|
+
This changes DOM placement only: dialogs retain `position: fixed` and are not automatically constrained to the container bounds.
|
|
63
|
+
|
|
39
64
|
## Two usage modes
|
|
40
65
|
|
|
41
66
|
### Mode 1: Imperative (recommended)
|
|
@@ -36,6 +36,31 @@ import Dialog, { dialogManager, CommonDialog, ModalResult } from "@ticatec/topsi
|
|
|
36
36
|
|
|
37
37
|
> **旧项目兼容**:如果你之前在 app 根放了 `<DialogBoard/>`,可以保留也可以删除。多个 board 共存时只有第一个真正渲染 stack,不会重复显示对话框。
|
|
38
38
|
|
|
39
|
+
### 指定挂载容器
|
|
40
|
+
|
|
41
|
+
默认自动挂载到 `document.body`。如果需要让弹窗成为某个 div 的子节点,在该元素已创建、且首次打开弹窗之前调用 `init`:
|
|
42
|
+
|
|
43
|
+
```svelte
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
import {onMount} from 'svelte';
|
|
46
|
+
import {dialogManager} from '@ticatec/topside-elements/Dialog';
|
|
47
|
+
|
|
48
|
+
let dialogHost: HTMLDivElement;
|
|
49
|
+
|
|
50
|
+
onMount(() => {
|
|
51
|
+
dialogManager.init({target: dialogHost});
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<div bind:this={dialogHost}></div>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
之后所有 `dialogManager.show(...)` / `window.Dialog.show(...)` 打开的弹窗都渲染在这个容器内。也可以通过 `window.Dialog.init({target: element})` 配置;`init()` 或 `init({target: null})` 使用默认 body。
|
|
59
|
+
|
|
60
|
+
`init` 只配置容器,仍在首次 `show` 时挂载。容器应在弹窗系统使用期间保持挂载。首次调用 `show` 或 DialogBoard 挂载后,再调用 `init` 会抛出错误,即使挂载未成功或所有弹窗已关闭也不能重新配置。手动放置 `<DialogBoard/>` 时以其实际位置为准,应省略此配置;直接渲染 `<Dialog>` 不受此配置影响。
|
|
61
|
+
|
|
62
|
+
此配置只改变 DOM 挂载位置;弹窗仍使用 `position: fixed`,不会自动限制在容器边界内。
|
|
63
|
+
|
|
39
64
|
## 两种使用模式
|
|
40
65
|
|
|
41
66
|
### 模式一:命令式(推荐)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Dialog from "./Dialog.svelte";
|
|
2
2
|
import dialogManager from "./DialogManager.svelte";
|
|
3
3
|
import type IDialog from "./IDialog";
|
|
4
|
-
import { ModalResult, type OnClose } from "./IDialog";
|
|
4
|
+
import { ModalResult, type OnClose, type DialogOptions } from "./IDialog";
|
|
5
5
|
import type { BeforeClose } from "./BeforeClose";
|
|
6
6
|
import CommonDialog from "./CommonDialog.svelte";
|
|
7
7
|
import DialogBoard from "./DialogBoard.svelte";
|
|
@@ -11,4 +11,4 @@ declare global {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
export default Dialog;
|
|
14
|
-
export { dialogManager, ModalResult, type OnClose, type IDialog, type BeforeClose, CommonDialog, DialogBoard };
|
|
14
|
+
export { dialogManager, ModalResult, type OnClose, type DialogOptions, type IDialog, type BeforeClose, CommonDialog, DialogBoard };
|
|
@@ -943,6 +943,24 @@
|
|
|
943
943
|
.topside-button.icon-button.small i {
|
|
944
944
|
font-size: 16px;
|
|
945
945
|
}
|
|
946
|
+
.topside-button.icon-button.circle {
|
|
947
|
+
border-radius: 50%;
|
|
948
|
+
}
|
|
949
|
+
.topside-button.icon-button.round, .topside-button.icon-button.round-corner {
|
|
950
|
+
border-radius: var(--topside-button-radius, var(--radius-md, 4px));
|
|
951
|
+
}
|
|
952
|
+
.topside-button.icon-button.borderless {
|
|
953
|
+
border-color: transparent !important;
|
|
954
|
+
}
|
|
955
|
+
.topside-button.icon-button.borderless.disabled {
|
|
956
|
+
border-color: transparent !important;
|
|
957
|
+
}
|
|
958
|
+
.topside-button.icon-button.no-outer {
|
|
959
|
+
border-color: transparent !important;
|
|
960
|
+
}
|
|
961
|
+
.topside-button.icon-button.no-outer.disabled {
|
|
962
|
+
border-color: transparent !important;
|
|
963
|
+
}
|
|
946
964
|
.topside-button.icon-button.no-outer:focus, .topside-button.icon-button.no-outer:focus-visible, .topside-button.icon-button.no-outer:active {
|
|
947
965
|
outline: none !important;
|
|
948
966
|
box-shadow: none !important;
|
|
@@ -109,8 +109,8 @@
|
|
|
109
109
|
| [Accordion](./accordion/README.md) | `@ticatec/topside-elements/Accordion` | [示例](./accordion/example.svelte) | 5 |
|
|
110
110
|
| [Card](./card/README.md) | `@ticatec/topside-elements/Card` | [示例](./card/example.svelte) | 11 |
|
|
111
111
|
| [ConciseDataTable](./concise-data-table/README.md) | `@ticatec/topside-elements/ConciseDataTable` | [示例](./concise-data-table/example.svelte) | 10 |
|
|
112
|
-
| [ListBox](./list-box/README.md) | `@ticatec/topside-elements/ListBox` | [示例](./list-box/example.svelte) |
|
|
113
|
-
| [TreeView](./tree-view/README.md) | `@ticatec/topside-elements/TreeView` | [示例](./tree-view/example.svelte) |
|
|
112
|
+
| [ListBox](./list-box/README.md) | `@ticatec/topside-elements/ListBox` | [示例](./list-box/example.svelte) | 22 |
|
|
113
|
+
| [TreeView](./tree-view/README.md) | `@ticatec/topside-elements/TreeView` | [示例](./tree-view/example.svelte) | 19 |
|
|
114
114
|
|
|
115
115
|
## 通用
|
|
116
116
|
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
| --- | --- | --- | ---: |
|
|
119
119
|
| [Button](./button/README.md) | `@ticatec/topside-elements/Button` | [示例](./button/example.svelte) | 14 |
|
|
120
120
|
| [Icon](./icon/README.md) | `@ticatec/topside-elements/Icon` | [示例](./icon/example.svelte) | 8 |
|
|
121
|
-
| [IconButton](./icon-button/README.md) | `@ticatec/topside-elements/IconButton` | [示例](./icon-button/example.svelte) |
|
|
121
|
+
| [IconButton](./icon-button/README.md) | `@ticatec/topside-elements/IconButton` | [示例](./icon-button/example.svelte) | 18 |
|
|
122
122
|
| [Separator](./separator/README.md) | `@ticatec/topside-elements/Separator` | [示例](./separator/example.svelte) | 2 |
|
|
123
123
|
| [Split](./split/README.md) | `@ticatec/topside-elements/Split` | [示例](./split/example.svelte) | 10 |
|
|
124
124
|
| [Tag](./tag/README.md) | `@ticatec/topside-elements/Tag` | [示例](./tag/example.svelte) | 10 |
|
|
@@ -33,6 +33,12 @@ import IconButton from '@ticatec/topside-elements/IconButton';
|
|
|
33
33
|
| `children` | 是 | `Snippet` | 否 | `undefined` | 否 | Svelte Snippet 子内容,用于自定义组件主体。 |
|
|
34
34
|
| `tabindex` | 否 | `number` | 否 | `-1` | 否 | 可选配置,具体取值范围和数据结构见类型列。 |
|
|
35
35
|
| `outer` | 否 | `boolean` | 否 | `true` | 否 | 可选配置,具体取值范围和数据结构见类型列。 |
|
|
36
|
+
| `variant` | 是 | `IconButtonVariant` | 否 | `'circle'` | 否 | 形状变体:'circle' (默认正圆) \| 'round' (圆角矩形) |
|
|
37
|
+
| `shape` | 否 | `IconButtonVariant` | 否 | `undefined` | 否 | 形状别名,支持与 variant 相同的取值 |
|
|
38
|
+
| `round` | 否 | `boolean` | 否 | `false` | 否 | 是否为圆角(round corner 便捷布尔属性) |
|
|
39
|
+
| `borderless` | 否 | `boolean` | 否 | `false` | 否 | 是否无边框 |
|
|
40
|
+
| `border` | 否 | `boolean` | 否 | `true` | 否 | 是否有边框(默认为 true,设为 false 时等同于 borderless) |
|
|
41
|
+
| `bordered` | 否 | `boolean` | 否 | `true` | 否 | 是否有边框(border 别名) |
|
|
36
42
|
|
|
37
43
|
> 组件的“可绑定”表示源码使用了 Svelte 5 的 `$bindable`;服务的“响应式”表示成员使用了 `$state`。复杂类型请同时参考 TypeScript 自动补全。
|
|
38
44
|
|
|
@@ -62,6 +68,14 @@ Svelte Snippet 子内容,用于自定义组件主体。
|
|
|
62
68
|
- 默认值:`undefined`
|
|
63
69
|
- 示例写法:`children={children}`
|
|
64
70
|
|
|
71
|
+
### `variant`
|
|
72
|
+
|
|
73
|
+
形状变体:'circle' (默认正圆) | 'round' (圆角矩形)
|
|
74
|
+
|
|
75
|
+
- 类型:`IconButtonVariant`
|
|
76
|
+
- 默认值:`'circle'`
|
|
77
|
+
- 示例写法:`variant="circle"`
|
|
78
|
+
|
|
65
79
|
## 相关类型定义
|
|
66
80
|
|
|
67
81
|
以下定义帮助理解复杂属性的数据结构;其中部分辅助类型属于内部实现,不一定是独立导出入口。
|
|
@@ -88,8 +102,10 @@ export type Size = 'small' | 'medium' | 'large';
|
|
|
88
102
|
|
|
89
103
|
<IconButton
|
|
90
104
|
onclick={async (...args: any[]) => { console.log('onclick', ...args); return true; }}
|
|
105
|
+
variant="circle"
|
|
91
106
|
disabled={false}
|
|
92
107
|
size="small"
|
|
108
|
+
round={true}
|
|
93
109
|
>
|
|
94
110
|
<div>在这里放置子内容</div>
|
|
95
111
|
</IconButton>
|
|
@@ -38,6 +38,8 @@ import TreeView from '@ticatec/topside-elements/TreeView';
|
|
|
38
38
|
| `oncontextmenu` | 否 | `OnContextMenu` | 否 | `undefined` | 否 | 节点右键菜单回调 (event, node) |
|
|
39
39
|
| `title` | 否 | `string` | 否 | `undefined` | 否 | 可选配置,具体取值范围和数据结构见类型列。 |
|
|
40
40
|
| `defaultExpandDepth` | 否 | `number` | 否 | `0` | 否 | 初始展开多少层(0 = 全部折叠,1 = 展开根节点,依此类推)。默认 0。 |
|
|
41
|
+
| `expandOnClick` | 是 | `boolean` | 否 | `true` | 否 | 点击目录行时是否自动切换展开/折叠。默认 true。 |
|
|
42
|
+
| `showChevron` | 否 | `boolean` | 否 | `true` | 否 | 是否显示最前面用于展开/折叠的 chevron 图标。默认 true;若 expandOnClick 为 false 则强制为 true(不能隐藏,保证可打开)。 |
|
|
41
43
|
|
|
42
44
|
> 组件的“可绑定”表示源码使用了 Svelte 5 的 `$bindable`;服务的“响应式”表示成员使用了 `$state`。复杂类型请同时参考 TypeScript 自动补全。
|
|
43
45
|
|
package/package.json
CHANGED