@manohub/ui 0.6.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/README.md +141 -0
- package/dist/components/avatar/index.d.ts +283 -0
- package/dist/components/avatar/index.js +167 -0
- package/dist/components/badge/index.d.ts +132 -0
- package/dist/components/badge/index.js +99 -0
- package/dist/components/button/index.d.ts +578 -0
- package/dist/components/button/index.js +191 -0
- package/dist/components/capsule/index.d.ts +161 -0
- package/dist/components/capsule/index.js +101 -0
- package/dist/components/card/index.d.ts +456 -0
- package/dist/components/card/index.js +231 -0
- package/dist/components/checkbox/index.d.ts +331 -0
- package/dist/components/checkbox/index.js +165 -0
- package/dist/components/code/index.d.ts +134 -0
- package/dist/components/code/index.js +117 -0
- package/dist/components/code-editor/index.d.ts +163 -0
- package/dist/components/code-editor/index.js +98 -0
- package/dist/components/collapse/index.d.ts +125 -0
- package/dist/components/collapse/index.js +95 -0
- package/dist/components/control-size.d.ts +8 -0
- package/dist/components/dialog/index.d.ts +224 -0
- package/dist/components/dialog/index.js +194 -0
- package/dist/components/divider/index.d.ts +64 -0
- package/dist/components/divider/index.js +62 -0
- package/dist/components/drawer/index.d.ts +226 -0
- package/dist/components/drawer/index.js +190 -0
- package/dist/components/filter/index.d.ts +391 -0
- package/dist/components/filter/index.js +203 -0
- package/dist/components/form/index.d.ts +704 -0
- package/dist/components/form/index.js +329 -0
- package/dist/components/input/index.d.ts +980 -0
- package/dist/components/input/index.js +334 -0
- package/dist/components/layout/index.d.ts +162 -0
- package/dist/components/layout/index.js +100 -0
- package/dist/components/list-view/index.d.ts +563 -0
- package/dist/components/list-view/index.js +221 -0
- package/dist/components/loading/index.d.ts +77 -0
- package/dist/components/loading/index.js +40 -0
- package/dist/components/nav/index.d.ts +293 -0
- package/dist/components/nav/index.js +164 -0
- package/dist/components/notice/index.d.ts +137 -0
- package/dist/components/notice/index.js +101 -0
- package/dist/components/notification/index.d.ts +118 -0
- package/dist/components/notification/index.js +99 -0
- package/dist/components/page/index.d.ts +276 -0
- package/dist/components/page/index.js +351 -0
- package/dist/components/page/split-resize.d.ts +42 -0
- package/dist/components/page/split-resize.js +36 -0
- package/dist/components/pagination/index.d.ts +171 -0
- package/dist/components/pagination/index.js +226 -0
- package/dist/components/panel/index.d.ts +401 -0
- package/dist/components/panel/index.js +193 -0
- package/dist/components/part-padding.d.ts +27 -0
- package/dist/components/part-padding.js +23 -0
- package/dist/components/query-state/index.d.ts +207 -0
- package/dist/components/query-state/index.js +181 -0
- package/dist/components/radio/index.d.ts +473 -0
- package/dist/components/radio/index.js +250 -0
- package/dist/components/search/index.d.ts +167 -0
- package/dist/components/search/index.js +105 -0
- package/dist/components/select/index.d.ts +252 -0
- package/dist/components/select/index.js +281 -0
- package/dist/components/skeleton/index.d.ts +88 -0
- package/dist/components/skeleton/index.js +56 -0
- package/dist/components/steps/index.d.ts +199 -0
- package/dist/components/steps/index.js +117 -0
- package/dist/components/switch/index.d.ts +150 -0
- package/dist/components/switch/index.js +103 -0
- package/dist/components/table/index.d.ts +351 -0
- package/dist/components/table/index.js +286 -0
- package/dist/components/tabs/index.d.ts +179 -0
- package/dist/components/tabs/index.js +153 -0
- package/dist/components/tabset/index.d.ts +105 -0
- package/dist/components/tabset/index.js +68 -0
- package/dist/components/tag/index.d.ts +203 -0
- package/dist/components/tag/index.js +144 -0
- package/dist/components/toast/index.d.ts +271 -0
- package/dist/components/toast/index.js +116 -0
- package/dist/components/tooltip/index.d.ts +126 -0
- package/dist/components/tooltip/index.js +97 -0
- package/dist/components/tree/index.d.ts +317 -0
- package/dist/components/tree/index.js +251 -0
- package/dist/components/upload/index.d.ts +384 -0
- package/dist/components/upload/index.js +172 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +102 -0
- package/dist/services/base.d.ts +21 -0
- package/dist/services/base.js +42 -0
- package/dist/services/dialog.d.ts +41 -0
- package/dist/services/dialog.js +83 -0
- package/dist/services/index.d.ts +12 -0
- package/dist/services/loading.d.ts +21 -0
- package/dist/services/loading.js +41 -0
- package/dist/services/toast.d.ts +40 -0
- package/dist/services/toast.js +74 -0
- package/dist/styles/components/avatar.css +128 -0
- package/dist/styles/components/avatar.tokens.css +24 -0
- package/dist/styles/components/badge.css +237 -0
- package/dist/styles/components/button.css +454 -0
- package/dist/styles/components/button.tokens.css +56 -0
- package/dist/styles/components/capsule.css +120 -0
- package/dist/styles/components/capsule.tokens.css +27 -0
- package/dist/styles/components/card.css +192 -0
- package/dist/styles/components/checkbox.css +145 -0
- package/dist/styles/components/checkbox.tokens.css +26 -0
- package/dist/styles/components/code-editor.css +91 -0
- package/dist/styles/components/code-editor.tokens.css +24 -0
- package/dist/styles/components/code.css +153 -0
- package/dist/styles/components/code.tokens.css +29 -0
- package/dist/styles/components/collapse.css +62 -0
- package/dist/styles/components/collapse.tokens.css +20 -0
- package/dist/styles/components/dialog.css +106 -0
- package/dist/styles/components/dialog.tokens.css +24 -0
- package/dist/styles/components/divider.css +71 -0
- package/dist/styles/components/divider.tokens.css +21 -0
- package/dist/styles/components/drawer.css +143 -0
- package/dist/styles/components/drawer.tokens.css +22 -0
- package/dist/styles/components/filter.css +41 -0
- package/dist/styles/components/filter.tokens.css +14 -0
- package/dist/styles/components/form.css +220 -0
- package/dist/styles/components/form.tokens.css +15 -0
- package/dist/styles/components/input.css +445 -0
- package/dist/styles/components/input.tokens.css +41 -0
- package/dist/styles/components/layout.css +198 -0
- package/dist/styles/components/list-view.css +244 -0
- package/dist/styles/components/loading.css +76 -0
- package/dist/styles/components/loading.tokens.css +23 -0
- package/dist/styles/components/nav.css +48 -0
- package/dist/styles/components/notice.css +153 -0
- package/dist/styles/components/notice.tokens.css +17 -0
- package/dist/styles/components/notification.css +159 -0
- package/dist/styles/components/notification.tokens.css +37 -0
- package/dist/styles/components/page.css +473 -0
- package/dist/styles/components/pagination.css +297 -0
- package/dist/styles/components/pagination.tokens.css +32 -0
- package/dist/styles/components/panel.css +77 -0
- package/dist/styles/components/query-state.css +88 -0
- package/dist/styles/components/radio.css +271 -0
- package/dist/styles/components/radio.tokens.css +26 -0
- package/dist/styles/components/search.css +30 -0
- package/dist/styles/components/select.css +242 -0
- package/dist/styles/components/select.tokens.css +33 -0
- package/dist/styles/components/skeleton.css +60 -0
- package/dist/styles/components/skeleton.tokens.css +27 -0
- package/dist/styles/components/steps.css +157 -0
- package/dist/styles/components/steps.tokens.css +42 -0
- package/dist/styles/components/switch.css +165 -0
- package/dist/styles/components/switch.tokens.css +26 -0
- package/dist/styles/components/table.css +439 -0
- package/dist/styles/components/table.tokens.css +41 -0
- package/dist/styles/components/tabs.css +234 -0
- package/dist/styles/components/tabs.tokens.css +30 -0
- package/dist/styles/components/tag.css +200 -0
- package/dist/styles/components/tag.tokens.css +30 -0
- package/dist/styles/components/toast.css +150 -0
- package/dist/styles/components/toast.tokens.css +26 -0
- package/dist/styles/components/tooltip.css +89 -0
- package/dist/styles/components/tooltip.tokens.css +25 -0
- package/dist/styles/components/tree.css +139 -0
- package/dist/styles/components/tree.tokens.css +20 -0
- package/dist/styles/components/upload.css +150 -0
- package/dist/styles/components/upload.tokens.css +27 -0
- package/dist/styles/index.css +149 -0
- package/dist/styles/operations/collapse.css +84 -0
- package/dist/styles/operations/popup.css +189 -0
- package/dist/styles/operations/tabset.css +57 -0
- package/dist/utils/state-illustration.d.ts +31 -0
- package/dist/utils/state-illustration.js +156 -0
- package/package.json +62 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { type PropType, type SlotsType, type VNode, type VNodeChild } from 'vue';
|
|
2
|
+
import { type Padding } from '../part-padding';
|
|
3
|
+
/**
|
|
4
|
+
* 页面骨架复合件 —— 页面结构与滚动归属的统一契约。
|
|
5
|
+
*
|
|
6
|
+
* 滚动契约写死在 `.mh-page*` 样式:Header / Filter / Footer 不滚;
|
|
7
|
+
* Body 只管**页面级**滚动归属,只有两档:`mode="scroll"` 自身滚(长内容页)、
|
|
8
|
+
* `mode="plain"`(默认)不滚 —— 滚动下沉到区域内容区或内容件自持的定高容器。
|
|
9
|
+
* **表格不靠 Body 的 mode 表达**:外框由 `Table` 的 `framed` 档给,高度与滚动由
|
|
10
|
+
* 承载它的内容件承担(`Panel.Body`,或表格件自带的定高容器)。
|
|
11
|
+
*
|
|
12
|
+
* **Split 不滚,栏内滚动由内容承担** —— 双栏内部用 `Panel`
|
|
13
|
+
* (Header 固定 + Body `overflow:auto`)承载各自区域的滚动。
|
|
14
|
+
*
|
|
15
|
+
* 与 `Panel` 的分工:本件是**页面级**(唯一),管页头规格、页脚固定、页面级滚动归属;
|
|
16
|
+
* `Panel` 是**区域级**(可多个),只管区域内的三件与区域内滚动。分页归属跟承载表格的容器走。
|
|
17
|
+
*
|
|
18
|
+
* **侧栏能力(内建,缺省开启,无需消费方配置)**:
|
|
19
|
+
* - 分隔线即拖拽手柄:按住左右拖即改宽,夹在 `minWidth`(缺省 160)与 `maxWidth`(缺省 480)之间;
|
|
20
|
+
* - 手柄上悬停显形一个箭头按钮,点击收起侧栏;收起后页面边缘常驻 16px 展开把手,点击展开并恢复收起前的宽度
|
|
21
|
+
* —— 展开按钮**占满整条把手**(全高全宽 + 描边 + 底),收起态里它是整块侧栏唯一看得见的东西;
|
|
22
|
+
* - `width` 只是**初始宽度**:拖拽结果不持久化(刷新 / 重进回到初始宽度),收起也不清拖出的宽度;
|
|
23
|
+
* - **宽度过渡期间内容不重排**:内容按展开时的宽度定宽(组件下发 `--mh-page-split-frozen`),
|
|
24
|
+
* 多出的部分由容器裁掉 —— 栏内滚动容器不会因宽度被压缩而闪出滚动条;
|
|
25
|
+
* - 左侧栏用 `sidebar`、右侧栏用 `rightSidebar`(互为镜像:分隔线在侧栏左侧、拖拽方向相反),
|
|
26
|
+
* 两者可同时存在构成三栏;某侧传 `resizable: false` 时该侧不渲染手柄与把手。
|
|
27
|
+
*
|
|
28
|
+
* 两级滚动归属(契约):
|
|
29
|
+
* - `Page.Body.mode` 管**页面级**(scroll/plain,全页唯一);
|
|
30
|
+
* - `Panel.Body` 管**区域内**(Header 固定 / 内容滚)。
|
|
31
|
+
*
|
|
32
|
+
* **成员位置固定(归位)**:成员不按书写顺序渲染 —— 以本件为根,一律归到固定位置,
|
|
33
|
+
* 于是「读代码的顺序」不必等于「视觉顺序」(页脚写最前面也仍在底部):
|
|
34
|
+
* - **页面级行**:`Filter → Header → Body → Footer`(与 `Panel` 同款归位法);
|
|
35
|
+
* - **主体区**:自由内容(含直接写在根下的 `Page.Split`)—— 没写显式 `Page.Body` 时
|
|
36
|
+
* 自动包一层 `Page.Body`(缺省 `mode="plain"`),所以双栏可以只写 `Page.Split`。
|
|
37
|
+
*
|
|
38
|
+
* 页头右侧的自由位(搜索框 / 按钮 / 等)是 `Page.Header` 的 `extra` 位(属性或插槽),
|
|
39
|
+
* 没有独立的成员件:那个位置只有页头一个承载者,写成成员等于给它加一层没有意义的转发。
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // 模板 A:列表页(页头 + 表格 + 页脚分页)
|
|
43
|
+
* <Page>
|
|
44
|
+
* <Page.Header title="技能分类" subTitle="共 128 个技能"
|
|
45
|
+
* extra={<搜索框 modelValue={kw} />} />
|
|
46
|
+
* <Page.Body mode="plain">
|
|
47
|
+
* <Table framed rows={rows} columns={columns} rowKey="id" />
|
|
48
|
+
* </Page.Body>
|
|
49
|
+
* <Page.Footer>
|
|
50
|
+
* <Pagination page={page} pageSize={size} total={total} onPageChange={…} />
|
|
51
|
+
* </Page.Footer>
|
|
52
|
+
* </Page>
|
|
53
|
+
*
|
|
54
|
+
* // 模板 B:双栏(Split 提供中间分隔线,栏内滚动由 Panel 承担)
|
|
55
|
+
* <Page.Body mode="plain">
|
|
56
|
+
* <Page.Split sidebar={{ width: 208, content: () => <Panel title="业务域" /> }}>
|
|
57
|
+
* <Panel title="值映射列表"><Table framed rows={rows} columns={cols} rowKey="id" /></Panel>
|
|
58
|
+
* </Page.Split>
|
|
59
|
+
* </Page.Body>
|
|
60
|
+
*/
|
|
61
|
+
/** 页面骨架(及其各部件)的内距:与 `Panel` / `Form` 共用一套口径(`number` = 四边同值;`{ y, x }` 分别给) */
|
|
62
|
+
export type PagePadding = Padding;
|
|
63
|
+
export interface PageHeaderProps {
|
|
64
|
+
/** 传入 title 走内置布局;不传则纯 slot 直出(自定义页头出口) */
|
|
65
|
+
title?: string;
|
|
66
|
+
/**
|
|
67
|
+
* 页头副标题:标题**下一行**的次要说明(对象名、统计口径等),随 `title` 一起进内置布局。
|
|
68
|
+
* 不传即不渲染(页头条高度不变);`title` 不传时本项不生效(整块走自定义页头出口)。
|
|
69
|
+
*/
|
|
70
|
+
subTitle?: string;
|
|
71
|
+
/**
|
|
72
|
+
* 标题左侧图标:**svg 节点或返回节点的函数**皆可。缺省渲染设计稿默认图标
|
|
73
|
+
* (MH后台-0911 node 0:3904:26×26 圆角方块,`currentColor` 随主题)。
|
|
74
|
+
*/
|
|
75
|
+
icon?: VNodeChild | (() => VNodeChild);
|
|
76
|
+
/**
|
|
77
|
+
* 页头右侧的自由位(唯一可自定义位):搜索框 / 按钮 / 下拉等,水平排列。
|
|
78
|
+
* 传节点或返回节点的函数。布局(垂直居中 / 间距)由标准样式锁死。
|
|
79
|
+
*/
|
|
80
|
+
extra?: unknown;
|
|
81
|
+
}
|
|
82
|
+
export interface PageBodyProps {
|
|
83
|
+
/** scroll=自身滚(长内容页);plain=不滚(默认,滚动归区域内容区 / 内容件自持的定高容器) */
|
|
84
|
+
mode?: 'scroll' | 'plain';
|
|
85
|
+
/**
|
|
86
|
+
* 内容区内距(px 数字 = 四边同值;`{ y, x }` 分别给)。缺省不落行内样式,沿用 CSS 默认(`0 20px`)——
|
|
87
|
+
* 行内只覆盖**给到的轴**,所以传 `{ y: 16 }` 时左右仍是 CSS 的 20px(与页头左对齐不破)。
|
|
88
|
+
* 典型用法:「表格 / 卡片不贴页头下边线」→ `padding={{ y: 16 }}`。
|
|
89
|
+
*/
|
|
90
|
+
padding?: PagePadding;
|
|
91
|
+
}
|
|
92
|
+
export interface PageSplitSidebarProps {
|
|
93
|
+
/**
|
|
94
|
+
* 该侧栏的**初始**内容宽(px 数字,`content-box` 语义):
|
|
95
|
+
* 不传即自适应(不写死宽度);拖拽后以拖出的宽度为准 —— 拖拽结果不持久化,刷新 / 重进回到本值。
|
|
96
|
+
*/
|
|
97
|
+
width?: number;
|
|
98
|
+
/** 是否内建拖拽手柄与收起按钮,缺省 `true`;`false` 时该侧不渲染手柄/把手、不挂监听 */
|
|
99
|
+
resizable?: boolean;
|
|
100
|
+
/** 拖拽下限(px),缺省 160 */
|
|
101
|
+
minWidth?: number;
|
|
102
|
+
/** 拖拽上限(px),缺省 480 */
|
|
103
|
+
maxWidth?: number;
|
|
104
|
+
/** 首帧是否处于收起态,缺省 `false`;`resizable: false` 时忽略本项 */
|
|
105
|
+
defaultCollapsed?: boolean;
|
|
106
|
+
/** 侧栏内容(区域头与滚动归 `Panel`) */
|
|
107
|
+
content?: () => VNodeChild;
|
|
108
|
+
}
|
|
109
|
+
export interface PageSplitProps {
|
|
110
|
+
/** 左侧栏 */
|
|
111
|
+
sidebar?: PageSplitSidebarProps;
|
|
112
|
+
/** 右侧栏(**镜像**:分隔线在侧栏左侧、拖拽方向相反、收起把手贴右边缘;与 `sidebar` 可同时存在,构成三栏) */
|
|
113
|
+
rightSidebar?: PageSplitSidebarProps;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Object.assign 复合件。两个注意点:
|
|
117
|
+
* 1. Object.assign 后不要再 defineComponent 二次包装,否则 props 类型丢失;
|
|
118
|
+
* 2. 每个子件必须显式声明 slots(SlotsType),否则 JSX 下 slot 参数类型退化为 any。
|
|
119
|
+
*/
|
|
120
|
+
export declare const Page: {
|
|
121
|
+
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<Readonly<{}> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, import("vue").PublicProps, {}, true, {}, SlotsType<{
|
|
122
|
+
default?: () => VNodeChild;
|
|
123
|
+
}>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
|
|
124
|
+
P: {};
|
|
125
|
+
B: {};
|
|
126
|
+
D: {};
|
|
127
|
+
C: {};
|
|
128
|
+
M: {};
|
|
129
|
+
Defaults: {};
|
|
130
|
+
}, Readonly<{}> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, {}>;
|
|
131
|
+
__isFragment?: never;
|
|
132
|
+
__isTeleport?: never;
|
|
133
|
+
__isSuspense?: never;
|
|
134
|
+
} & import("vue").ComponentOptionsBase<Readonly<{}> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, {}, {}, string, SlotsType<{
|
|
135
|
+
default?: () => VNodeChild;
|
|
136
|
+
}>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & {
|
|
137
|
+
Filter: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
138
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`14px 20px 0`) */
|
|
139
|
+
padding: {
|
|
140
|
+
type: PropType<PagePadding>;
|
|
141
|
+
default: undefined;
|
|
142
|
+
};
|
|
143
|
+
}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
144
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`14px 20px 0`) */
|
|
145
|
+
padding: {
|
|
146
|
+
type: PropType<PagePadding>;
|
|
147
|
+
default: undefined;
|
|
148
|
+
};
|
|
149
|
+
}>> & Readonly<{}>, {
|
|
150
|
+
padding: Padding;
|
|
151
|
+
}, SlotsType<{
|
|
152
|
+
default?: () => VNodeChild;
|
|
153
|
+
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
154
|
+
Header: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
155
|
+
title: {
|
|
156
|
+
type: StringConstructor;
|
|
157
|
+
default: undefined;
|
|
158
|
+
};
|
|
159
|
+
subTitle: {
|
|
160
|
+
type: StringConstructor;
|
|
161
|
+
default: undefined;
|
|
162
|
+
};
|
|
163
|
+
icon: {
|
|
164
|
+
type: PropType<any>;
|
|
165
|
+
default: undefined;
|
|
166
|
+
};
|
|
167
|
+
extra: {
|
|
168
|
+
type: PropType<any>;
|
|
169
|
+
default: undefined;
|
|
170
|
+
};
|
|
171
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`14px 20px 13px`) */
|
|
172
|
+
padding: {
|
|
173
|
+
type: PropType<PagePadding>;
|
|
174
|
+
default: undefined;
|
|
175
|
+
};
|
|
176
|
+
}>, () => string | number | boolean | VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
177
|
+
[key: string]: any;
|
|
178
|
+
}> | import("vue").VNodeArrayChildren | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
179
|
+
title: {
|
|
180
|
+
type: StringConstructor;
|
|
181
|
+
default: undefined;
|
|
182
|
+
};
|
|
183
|
+
subTitle: {
|
|
184
|
+
type: StringConstructor;
|
|
185
|
+
default: undefined;
|
|
186
|
+
};
|
|
187
|
+
icon: {
|
|
188
|
+
type: PropType<any>;
|
|
189
|
+
default: undefined;
|
|
190
|
+
};
|
|
191
|
+
extra: {
|
|
192
|
+
type: PropType<any>;
|
|
193
|
+
default: undefined;
|
|
194
|
+
};
|
|
195
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`14px 20px 13px`) */
|
|
196
|
+
padding: {
|
|
197
|
+
type: PropType<PagePadding>;
|
|
198
|
+
default: undefined;
|
|
199
|
+
};
|
|
200
|
+
}>> & Readonly<{}>, {
|
|
201
|
+
padding: Padding;
|
|
202
|
+
icon: any;
|
|
203
|
+
title: string;
|
|
204
|
+
subTitle: string;
|
|
205
|
+
extra: any;
|
|
206
|
+
}, SlotsType<{
|
|
207
|
+
default?: () => VNodeChild;
|
|
208
|
+
extra?: () => VNodeChild;
|
|
209
|
+
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
210
|
+
Body: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
211
|
+
mode: {
|
|
212
|
+
type: () => NonNullable<PageBodyProps["mode"]>;
|
|
213
|
+
default: string;
|
|
214
|
+
};
|
|
215
|
+
padding: {
|
|
216
|
+
type: PropType<PagePadding>;
|
|
217
|
+
default: undefined;
|
|
218
|
+
};
|
|
219
|
+
}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
220
|
+
mode: {
|
|
221
|
+
type: () => NonNullable<PageBodyProps["mode"]>;
|
|
222
|
+
default: string;
|
|
223
|
+
};
|
|
224
|
+
padding: {
|
|
225
|
+
type: PropType<PagePadding>;
|
|
226
|
+
default: undefined;
|
|
227
|
+
};
|
|
228
|
+
}>> & Readonly<{}>, {
|
|
229
|
+
padding: Padding;
|
|
230
|
+
mode: NonNullable<"scroll" | "plain" | undefined>;
|
|
231
|
+
}, SlotsType<{
|
|
232
|
+
default?: () => VNodeChild;
|
|
233
|
+
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
234
|
+
Footer: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
235
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`12px 20px`) */
|
|
236
|
+
padding: {
|
|
237
|
+
type: PropType<PagePadding>;
|
|
238
|
+
default: undefined;
|
|
239
|
+
};
|
|
240
|
+
}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
241
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`12px 20px`) */
|
|
242
|
+
padding: {
|
|
243
|
+
type: PropType<PagePadding>;
|
|
244
|
+
default: undefined;
|
|
245
|
+
};
|
|
246
|
+
}>> & Readonly<{}>, {
|
|
247
|
+
padding: Padding;
|
|
248
|
+
}, SlotsType<{
|
|
249
|
+
default?: () => VNodeChild;
|
|
250
|
+
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
251
|
+
Split: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
252
|
+
sidebar: {
|
|
253
|
+
type: () => PageSplitProps["sidebar"];
|
|
254
|
+
default: undefined;
|
|
255
|
+
};
|
|
256
|
+
rightSidebar: {
|
|
257
|
+
type: () => PageSplitProps["rightSidebar"];
|
|
258
|
+
default: undefined;
|
|
259
|
+
};
|
|
260
|
+
}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
261
|
+
sidebar: {
|
|
262
|
+
type: () => PageSplitProps["sidebar"];
|
|
263
|
+
default: undefined;
|
|
264
|
+
};
|
|
265
|
+
rightSidebar: {
|
|
266
|
+
type: () => PageSplitProps["rightSidebar"];
|
|
267
|
+
default: undefined;
|
|
268
|
+
};
|
|
269
|
+
}>> & Readonly<{}>, {
|
|
270
|
+
sidebar: PageSplitSidebarProps | undefined;
|
|
271
|
+
rightSidebar: PageSplitSidebarProps | undefined;
|
|
272
|
+
}, SlotsType<{
|
|
273
|
+
default?: () => VNodeChild;
|
|
274
|
+
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
275
|
+
};
|
|
276
|
+
export type PageCompound = typeof Page;
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { defineComponent, Comment, Text, h, createVNode, onBeforeUnmount, ref, watch, computed } from "vue";
|
|
2
|
+
import { Icon } from "@manohub/icon";
|
|
3
|
+
import { mergePartStyle, partPaddingStyle } from "../part-padding.js";
|
|
4
|
+
import { measureSidebarWidth, clampSplitWidth, projectSplitWidth } from "./split-resize.js";
|
|
5
|
+
const DefaultHeaderIcon = () => createVNode(Icon, {
|
|
6
|
+
"name": "page-title",
|
|
7
|
+
"size": 26
|
|
8
|
+
}, null);
|
|
9
|
+
const PageFilter = /* @__PURE__ */ defineComponent({
|
|
10
|
+
name: "PageFilter",
|
|
11
|
+
inheritAttrs: false,
|
|
12
|
+
props: {
|
|
13
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`14px 20px 0`) */
|
|
14
|
+
padding: {
|
|
15
|
+
type: [Number, Object],
|
|
16
|
+
default: void 0
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
slots: Object,
|
|
20
|
+
setup(props, {
|
|
21
|
+
slots,
|
|
22
|
+
attrs
|
|
23
|
+
}) {
|
|
24
|
+
return () => createVNode("div", {
|
|
25
|
+
"class": ["mh-page__filter", attrs.class].filter(Boolean).join(" "),
|
|
26
|
+
"style": mergePartStyle(attrs.style, props.padding)
|
|
27
|
+
}, [slots.default?.()]);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const PageHeader = /* @__PURE__ */ defineComponent({
|
|
31
|
+
name: "PageHeader",
|
|
32
|
+
inheritAttrs: false,
|
|
33
|
+
props: {
|
|
34
|
+
title: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: void 0
|
|
37
|
+
},
|
|
38
|
+
subTitle: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: void 0
|
|
41
|
+
},
|
|
42
|
+
icon: {
|
|
43
|
+
type: [Function, Object, Array, String],
|
|
44
|
+
default: void 0
|
|
45
|
+
},
|
|
46
|
+
extra: {
|
|
47
|
+
type: [Function, Object, Array, String],
|
|
48
|
+
default: void 0
|
|
49
|
+
},
|
|
50
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`14px 20px 13px`) */
|
|
51
|
+
padding: {
|
|
52
|
+
type: [Number, Object],
|
|
53
|
+
default: void 0
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
slots: Object,
|
|
57
|
+
setup(props, {
|
|
58
|
+
slots,
|
|
59
|
+
attrs
|
|
60
|
+
}) {
|
|
61
|
+
const renderPosition = (slotFn, propValue) => {
|
|
62
|
+
if (slotFn) return slotFn();
|
|
63
|
+
if (typeof propValue === "function") return propValue();
|
|
64
|
+
return propValue;
|
|
65
|
+
};
|
|
66
|
+
const renderIcon = () => {
|
|
67
|
+
const icon = props.icon;
|
|
68
|
+
if (typeof icon === "function") return icon();
|
|
69
|
+
return icon ?? DefaultHeaderIcon();
|
|
70
|
+
};
|
|
71
|
+
return () => {
|
|
72
|
+
if (!props.title) {
|
|
73
|
+
const custom = slots.default?.() ?? null;
|
|
74
|
+
const paddingStyle = partPaddingStyle(props.padding);
|
|
75
|
+
return paddingStyle === void 0 ? custom : createVNode("div", {
|
|
76
|
+
"style": paddingStyle
|
|
77
|
+
}, [custom]);
|
|
78
|
+
}
|
|
79
|
+
const extra = renderPosition(slots.extra, props.extra);
|
|
80
|
+
return createVNode("header", {
|
|
81
|
+
"class": "mh-page__header",
|
|
82
|
+
"style": mergePartStyle(attrs.style, props.padding)
|
|
83
|
+
}, [createVNode("div", {
|
|
84
|
+
"class": "mh-page__header-inner"
|
|
85
|
+
}, [createVNode("div", {
|
|
86
|
+
"class": "mh-page__header-text"
|
|
87
|
+
}, [createVNode("div", {
|
|
88
|
+
"class": "mh-page__header-title-row"
|
|
89
|
+
}, [createVNode("span", {
|
|
90
|
+
"class": "mh-page__header-icon"
|
|
91
|
+
}, [renderIcon()]), createVNode("h1", {
|
|
92
|
+
"class": "mh-page__header-title"
|
|
93
|
+
}, [props.title])]), props.subTitle ? createVNode("div", {
|
|
94
|
+
"class": "mh-page__header-subtitle"
|
|
95
|
+
}, [props.subTitle]) : null]), extra ? createVNode("div", {
|
|
96
|
+
"class": "mh-page__header-extra"
|
|
97
|
+
}, [extra]) : null])]);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const PageBody = /* @__PURE__ */ defineComponent({
|
|
102
|
+
name: "PageBody",
|
|
103
|
+
inheritAttrs: false,
|
|
104
|
+
props: {
|
|
105
|
+
mode: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: "plain"
|
|
108
|
+
},
|
|
109
|
+
padding: {
|
|
110
|
+
type: [Number, Object],
|
|
111
|
+
default: void 0
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
slots: Object,
|
|
115
|
+
setup(props, {
|
|
116
|
+
slots,
|
|
117
|
+
attrs
|
|
118
|
+
}) {
|
|
119
|
+
return () => createVNode("div", {
|
|
120
|
+
"class": ["mh-page__body", `mh-page__body--${props.mode}`],
|
|
121
|
+
"style": mergePartStyle(attrs.style, props.padding)
|
|
122
|
+
}, [slots.default?.()]);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const PageFooter = /* @__PURE__ */ defineComponent({
|
|
126
|
+
name: "PageFooter",
|
|
127
|
+
inheritAttrs: false,
|
|
128
|
+
props: {
|
|
129
|
+
/** 内距(px 数字 = 四边同值;`{ y, x }` 分别给);缺省不落行内样式,沿用 CSS 默认(`12px 20px`) */
|
|
130
|
+
padding: {
|
|
131
|
+
type: [Number, Object],
|
|
132
|
+
default: void 0
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
slots: Object,
|
|
136
|
+
setup(props, {
|
|
137
|
+
slots,
|
|
138
|
+
attrs
|
|
139
|
+
}) {
|
|
140
|
+
return () => createVNode("footer", {
|
|
141
|
+
"class": "mh-page__footer",
|
|
142
|
+
"style": mergePartStyle(attrs.style, props.padding)
|
|
143
|
+
}, [createVNode("div", {
|
|
144
|
+
"class": "mh-page__footer-inner"
|
|
145
|
+
}, [slots.default?.()])]);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
const PageSplit = /* @__PURE__ */ defineComponent({
|
|
149
|
+
name: "PageSplit",
|
|
150
|
+
inheritAttrs: false,
|
|
151
|
+
props: {
|
|
152
|
+
sidebar: {
|
|
153
|
+
type: Object,
|
|
154
|
+
default: void 0
|
|
155
|
+
},
|
|
156
|
+
rightSidebar: {
|
|
157
|
+
type: Object,
|
|
158
|
+
default: void 0
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
slots: Object,
|
|
162
|
+
setup(props, {
|
|
163
|
+
slots
|
|
164
|
+
}) {
|
|
165
|
+
const createSide = (side) => {
|
|
166
|
+
const config = () => side === "right" ? props.rightSidebar : props.sidebar;
|
|
167
|
+
const enabled = () => !!config() && config()?.resizable !== false;
|
|
168
|
+
const asideEl = ref(null);
|
|
169
|
+
const draggedWidth = ref(null);
|
|
170
|
+
const frozenWidth = ref(null);
|
|
171
|
+
const dragging = ref(false);
|
|
172
|
+
const collapsed = ref(enabled() && config()?.defaultCollapsed === true);
|
|
173
|
+
let startX = 0;
|
|
174
|
+
let startWidth = 0;
|
|
175
|
+
const onPointerMove = (event) => {
|
|
176
|
+
const deltaX = event.clientX - startX;
|
|
177
|
+
draggedWidth.value = clampSplitWidth(projectSplitWidth(startWidth, deltaX, side), config()?.minWidth, config()?.maxWidth);
|
|
178
|
+
};
|
|
179
|
+
const stopDrag = () => {
|
|
180
|
+
dragging.value = false;
|
|
181
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
182
|
+
window.removeEventListener("pointerup", stopDrag);
|
|
183
|
+
window.removeEventListener("pointercancel", stopDrag);
|
|
184
|
+
};
|
|
185
|
+
const startDrag = (event) => {
|
|
186
|
+
const pointer = event;
|
|
187
|
+
if (!enabled() || collapsed.value || pointer.button !== 0) return;
|
|
188
|
+
const target = pointer.target;
|
|
189
|
+
if (target?.closest?.(".mh-page__split-handle-btn")) return;
|
|
190
|
+
pointer.preventDefault();
|
|
191
|
+
startX = pointer.clientX;
|
|
192
|
+
startWidth = measureSidebarWidth(asideEl.value, side, config()?.width);
|
|
193
|
+
dragging.value = true;
|
|
194
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
195
|
+
window.addEventListener("pointerup", stopDrag);
|
|
196
|
+
window.addEventListener("pointercancel", stopDrag);
|
|
197
|
+
};
|
|
198
|
+
const toggleCollapsed = () => {
|
|
199
|
+
if (!enabled()) return;
|
|
200
|
+
stopDrag();
|
|
201
|
+
frozenWidth.value = collapsed.value ? null : measureSidebarWidth(asideEl.value, side, draggedWidth.value ?? config()?.width);
|
|
202
|
+
collapsed.value = !collapsed.value;
|
|
203
|
+
};
|
|
204
|
+
watch(() => config()?.width, () => {
|
|
205
|
+
draggedWidth.value = null;
|
|
206
|
+
frozenWidth.value = null;
|
|
207
|
+
});
|
|
208
|
+
const width = computed(() => {
|
|
209
|
+
if (collapsed.value) return 0;
|
|
210
|
+
return draggedWidth.value ?? config()?.width;
|
|
211
|
+
});
|
|
212
|
+
const contentWidth = computed(() => frozenWidth.value ?? draggedWidth.value ?? config()?.width);
|
|
213
|
+
return {
|
|
214
|
+
asideEl,
|
|
215
|
+
dragging,
|
|
216
|
+
collapsed,
|
|
217
|
+
width,
|
|
218
|
+
contentWidth,
|
|
219
|
+
enabled,
|
|
220
|
+
startDrag,
|
|
221
|
+
stopDrag,
|
|
222
|
+
toggleCollapsed
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
const left = createSide("left");
|
|
226
|
+
const right = createSide("right");
|
|
227
|
+
onBeforeUnmount(() => {
|
|
228
|
+
left.stopDrag();
|
|
229
|
+
right.stopDrag();
|
|
230
|
+
});
|
|
231
|
+
const renderRail = (side, state) => {
|
|
232
|
+
const isRight = side === "right";
|
|
233
|
+
const shown = state.collapsed.value;
|
|
234
|
+
return createVNode("div", {
|
|
235
|
+
"key": `${side}-rail`,
|
|
236
|
+
"class": isRight ? "mh-page__split-rail--right" : "mh-page__split-rail"
|
|
237
|
+
}, [createVNode("button", {
|
|
238
|
+
"type": "button",
|
|
239
|
+
"class": "mh-page__split-rail-btn",
|
|
240
|
+
"aria-label": isRight ? "展开右侧栏" : "展开左侧栏",
|
|
241
|
+
"tabindex": shown ? 0 : -1,
|
|
242
|
+
"aria-hidden": shown ? void 0 : "true",
|
|
243
|
+
"onClick": () => state.toggleCollapsed()
|
|
244
|
+
}, [createVNode(Icon, {
|
|
245
|
+
"name": isRight ? "chevron-left" : "chevron-right",
|
|
246
|
+
"size": 16
|
|
247
|
+
}, null)])]);
|
|
248
|
+
};
|
|
249
|
+
const renderSide = (side, state) => {
|
|
250
|
+
const config = side === "right" ? props.rightSidebar : props.sidebar;
|
|
251
|
+
if (!config) return null;
|
|
252
|
+
const isRight = side === "right";
|
|
253
|
+
const width = state.width.value;
|
|
254
|
+
const contentWidth = state.contentWidth.value;
|
|
255
|
+
const asideStyle = {};
|
|
256
|
+
if (width !== void 0 && width !== null) asideStyle.width = `${width}px`;
|
|
257
|
+
if (contentWidth !== void 0 && contentWidth !== null) {
|
|
258
|
+
asideStyle["--mh-page-split-frozen"] = `${contentWidth}px`;
|
|
259
|
+
}
|
|
260
|
+
const aside = createVNode("aside", {
|
|
261
|
+
"key": `${side}-sidebar`,
|
|
262
|
+
"ref": state.asideEl,
|
|
263
|
+
"class": isRight ? "mh-page__split-sidebar--right" : "mh-page__split-sidebar",
|
|
264
|
+
"style": Object.keys(asideStyle).length > 0 ? asideStyle : void 0,
|
|
265
|
+
"data-dragging": state.dragging.value ? "" : void 0
|
|
266
|
+
}, [config.content?.()]);
|
|
267
|
+
const handle = state.enabled() && !state.collapsed.value ? createVNode("div", {
|
|
268
|
+
"key": `${side}-handle`,
|
|
269
|
+
"class": isRight ? "mh-page__split-handle--right" : "mh-page__split-handle",
|
|
270
|
+
"role": "separator",
|
|
271
|
+
"aria-orientation": "vertical",
|
|
272
|
+
"data-dragging": state.dragging.value ? "" : void 0,
|
|
273
|
+
"onPointerdown": state.startDrag
|
|
274
|
+
}, [createVNode("button", {
|
|
275
|
+
"type": "button",
|
|
276
|
+
"class": "mh-page__split-handle-btn",
|
|
277
|
+
"aria-label": isRight ? "收起右侧栏" : "收起左侧栏",
|
|
278
|
+
"onClick": () => state.toggleCollapsed()
|
|
279
|
+
}, [createVNode(Icon, {
|
|
280
|
+
"name": isRight ? "chevron-right" : "chevron-left",
|
|
281
|
+
"size": 12
|
|
282
|
+
}, null)])]) : null;
|
|
283
|
+
return isRight ? [handle, aside] : [aside, handle];
|
|
284
|
+
};
|
|
285
|
+
return () => {
|
|
286
|
+
const rootClass = ["mh-page__split", props.rightSidebar ? "mh-page__split--has-right" : "", props.sidebar && left.collapsed.value ? "mh-page__split--left-collapsed" : "", props.rightSidebar && right.collapsed.value ? "mh-page__split--right-collapsed" : "", left.dragging.value || right.dragging.value ? "mh-page__split--resizing" : ""].filter(Boolean).join(" ");
|
|
287
|
+
return createVNode("div", {
|
|
288
|
+
"class": rootClass
|
|
289
|
+
}, [props.sidebar && left.enabled() ? renderRail("left", left) : null, renderSide("left", left), createVNode("div", {
|
|
290
|
+
"class": "mh-page__split-main"
|
|
291
|
+
}, [slots.default?.()]), renderSide("right", right), props.rightSidebar && right.enabled() ? renderRail("right", right) : null]);
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
function splitParts(children) {
|
|
296
|
+
const filter = [];
|
|
297
|
+
const header = [];
|
|
298
|
+
const body = [];
|
|
299
|
+
const footer = [];
|
|
300
|
+
const rest = [];
|
|
301
|
+
for (const child of children) {
|
|
302
|
+
if (child.type === PageFilter) filter.push(child);
|
|
303
|
+
else if (child.type === PageHeader) header.push(child);
|
|
304
|
+
else if (child.type === PageBody) body.push(child);
|
|
305
|
+
else if (child.type === PageFooter) footer.push(child);
|
|
306
|
+
else rest.push(child);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
filter,
|
|
310
|
+
header,
|
|
311
|
+
body,
|
|
312
|
+
footer,
|
|
313
|
+
rest
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const PageRoot = /* @__PURE__ */ defineComponent({
|
|
317
|
+
name: "Page",
|
|
318
|
+
slots: Object,
|
|
319
|
+
setup(_, {
|
|
320
|
+
slots
|
|
321
|
+
}) {
|
|
322
|
+
return () => {
|
|
323
|
+
const raw = slots.default?.();
|
|
324
|
+
const list = Array.isArray(raw) ? raw : raw == null ? [] : [raw];
|
|
325
|
+
const children = list.filter((child) => !!child && typeof child === "object" && !Array.isArray(child) && child.type !== Comment && !(child.type === Text && String(child.children ?? "").trim() === ""));
|
|
326
|
+
const {
|
|
327
|
+
filter,
|
|
328
|
+
header,
|
|
329
|
+
body,
|
|
330
|
+
footer,
|
|
331
|
+
rest
|
|
332
|
+
} = splitParts(children);
|
|
333
|
+
const bodyNodes = rest.length > 0 ? [...body, h(PageBody, null, {
|
|
334
|
+
default: () => rest
|
|
335
|
+
})] : body;
|
|
336
|
+
return createVNode("div", {
|
|
337
|
+
"class": "mh-page"
|
|
338
|
+
}, [filter, header, bodyNodes, footer]);
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
const Page = Object.assign(PageRoot, {
|
|
343
|
+
Filter: PageFilter,
|
|
344
|
+
Header: PageHeader,
|
|
345
|
+
Body: PageBody,
|
|
346
|
+
Footer: PageFooter,
|
|
347
|
+
Split: PageSplit
|
|
348
|
+
});
|
|
349
|
+
export {
|
|
350
|
+
Page
|
|
351
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Page.Split` 侧栏交互的内部纯函数与常量(**不进对外导出面**:`src/index.ts` 白名单不外扩)。
|
|
3
|
+
*
|
|
4
|
+
* 抽出来的理由:宽度夹取与基准实测是**带分支的纯逻辑**(区间归一化、非有限数容错、
|
|
5
|
+
* content-box 语义下的内距扣除、无布局环境下的回退顺序),放一处便于直接单测;
|
|
6
|
+
* 组件里只留状态与 DOM 绑定。
|
|
7
|
+
*
|
|
8
|
+
* 已知取舍:`measureSidebarWidth` 依赖 `clientWidth`,jsdom 没有布局引擎(恒为 0),
|
|
9
|
+
* 走的是「回退到 prop 宽度 → 回退到兜底常量」这条路径;浏览器里才有真实实测值。
|
|
10
|
+
*/
|
|
11
|
+
/** 侧栏所在侧:`left` = `sidebar` 槽位;`right` = `rightSidebar` 槽位(镜像) */
|
|
12
|
+
export type SplitSide = 'left' | 'right';
|
|
13
|
+
/** 拖拽下限缺省值(px) */
|
|
14
|
+
export declare const SPLIT_MIN_WIDTH = 160;
|
|
15
|
+
/** 拖拽上限缺省值(px) */
|
|
16
|
+
export declare const SPLIT_MAX_WIDTH = 480;
|
|
17
|
+
/**
|
|
18
|
+
* 未传 `width` 且实测不到时的拖拽基准兜底。
|
|
19
|
+
* **只用于拖拽基准**,不参与初始渲染 —— 所以不违背「不传 `width` 就不写死宽度」的口径。
|
|
20
|
+
*/
|
|
21
|
+
export declare const SPLIT_FALLBACK_WIDTH = 208;
|
|
22
|
+
/** 区间归一化:`max < min` 时按数值大小归位(不抛错),缺省 160 / 480 */
|
|
23
|
+
export declare function splitWidthBounds(min?: number, max?: number): {
|
|
24
|
+
min: number;
|
|
25
|
+
max: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 夹取到区间并取整(防亚像素抖动)。
|
|
29
|
+
*
|
|
30
|
+
* 非有限数的口径:`NaN` 无法比较大小 ⇒ 落回下限;`±Infinity` 有明确方向
|
|
31
|
+
* ("拖到无穷远"就是顶到那侧的边界)⇒ 由 `Math.min/max` 自然夹到上限 / 下限。
|
|
32
|
+
*/
|
|
33
|
+
export declare function clampSplitWidth(value: number, min?: number, max?: number): number;
|
|
34
|
+
/** 拖拽位移投影为宽度:右侧栏镜像(向左拖变宽) */
|
|
35
|
+
export declare function projectSplitWidth(base: number, deltaX: number, side: SplitSide): number;
|
|
36
|
+
/**
|
|
37
|
+
* 实测某侧栏的**内容宽**(与 `box-sizing: content-box` 的 `width` 同语义)。
|
|
38
|
+
*
|
|
39
|
+
* `clientWidth` 天然排除边框、包含内距,故减去该侧内距即内容宽 —— 不必手工补 1px 边框。
|
|
40
|
+
* 取不到正值(jsdom 无布局 / 未渲染 / 已收起)时按 `prop 宽度 → 兜底常量` 回退。
|
|
41
|
+
*/
|
|
42
|
+
export declare function measureSidebarWidth(el: HTMLElement | null | undefined, side: SplitSide, fallback?: number): number;
|