@qw-ui/core 0.1.0 → 0.2.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/dist/avatar/avatar.d.ts +145 -0
- package/dist/avatar/index.d.ts +1 -0
- package/dist/avatar/index.js +150 -0
- package/dist/avatar/index.js.map +1 -0
- package/dist/button/button.d.ts +84 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +104 -0
- package/dist/button/index.js.map +1 -0
- package/dist/dialog/dialog.d.ts +59 -0
- package/dist/dialog/index.d.ts +1 -56
- package/dist/dialog/index.js +31 -30
- package/dist/dialog/index.js.map +1 -1
- package/dist/divider/divider.d.ts +23 -0
- package/dist/divider/index.d.ts +1 -0
- package/dist/divider/index.js +18 -0
- package/dist/divider/index.js.map +1 -0
- package/dist/empty/empty.d.ts +12 -0
- package/dist/empty/index.d.ts +1 -0
- package/dist/empty/index.js +21 -0
- package/dist/empty/index.js.map +1 -0
- package/dist/icon/icon.d.ts +11 -0
- package/dist/icon/index.d.ts +1 -0
- package/dist/icon/index.js +20 -0
- package/dist/icon/index.js.map +1 -0
- package/dist/image/image.d.ts +88 -0
- package/dist/image/index.d.ts +1 -0
- package/dist/image/index.js +84 -0
- package/dist/image/index.js.map +1 -0
- package/dist/index.d.ts +24 -4
- package/dist/index.js +13 -50
- package/dist/loading/index.d.ts +1 -0
- package/dist/loading/index.js +19 -0
- package/dist/loading/index.js.map +1 -0
- package/dist/loading/loading.d.ts +26 -0
- package/dist/search-bar/index.d.ts +1 -0
- package/dist/search-bar/index.js +252 -0
- package/dist/search-bar/index.js.map +1 -0
- package/dist/search-bar/search-bar.d.ts +146 -0
- package/dist/skeleton/index.d.ts +1 -0
- package/dist/skeleton/index.js +28 -0
- package/dist/skeleton/index.js.map +1 -0
- package/dist/skeleton/skeleton.d.ts +38 -0
- package/dist/tabs/index.d.ts +1 -36
- package/dist/tabs/index.js +34 -18
- package/dist/tabs/index.js.map +1 -1
- package/dist/tabs/tabs.d.ts +61 -0
- package/dist/textarea/index.d.ts +1 -0
- package/dist/textarea/index.js +231 -0
- package/dist/textarea/index.js.map +1 -0
- package/dist/textarea/textarea.d.ts +113 -0
- package/dist/toast/index.d.ts +1 -0
- package/dist/toast/index.js +44 -0
- package/dist/toast/index.js.map +1 -0
- package/dist/toast/toast.d.ts +52 -0
- package/dist/top-bar/index.d.ts +1 -0
- package/dist/top-bar/index.js +43 -0
- package/dist/top-bar/index.js.map +1 -0
- package/dist/top-bar/top-bar.d.ts +86 -0
- package/dist/visual-manifest.d.ts +142 -0
- package/package.json +67 -6
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Props as ZagTabsProps } from "@zag-js/tabs";
|
|
2
|
+
/**
|
|
3
|
+
* Tabs 契约 — 原型 B(有状态),core 包裹 zag.js tabs machine。
|
|
4
|
+
* 行为 props 进 createTabsProps → zag context;视觉 props(level/variant/size)
|
|
5
|
+
* 不进 machine,由适配层挂 data-* 属性驱动 CSS。
|
|
6
|
+
*
|
|
7
|
+
* 枚举以运行时常量数组为唯一事实源(类型从常量推导),__test__ 直接锁死词表。
|
|
8
|
+
*/
|
|
9
|
+
/** 导航层级全集:primary 一级导航(默认)/ secondary 二级导航。 */
|
|
10
|
+
export declare const TABS_LEVELS: readonly ["primary", "secondary"];
|
|
11
|
+
export type TabsLevel = (typeof TABS_LEVELS)[number];
|
|
12
|
+
/** 视觉形态全集:text 纯文本(默认)/ button 按钮。button 仅 secondary 支持。 */
|
|
13
|
+
export declare const TABS_VARIANTS: readonly ["text", "button"];
|
|
14
|
+
export type TabsVariant = (typeof TABS_VARIANTS)[number];
|
|
15
|
+
/** 字号全集:仅 primary 生效,"14" / "16"(默认)。 */
|
|
16
|
+
export declare const TABS_SIZES: readonly ["14", "16"];
|
|
17
|
+
export type TabsSize = (typeof TABS_SIZES)[number];
|
|
18
|
+
/**
|
|
19
|
+
* QwTabsProps — 双栈共享的行为 + 视觉契约。
|
|
20
|
+
* 行为 props(进 createTabsProps → zag machine):
|
|
21
|
+
* - loop(默认 true)→ zag loopFocus;
|
|
22
|
+
* - value / defaultValue / activationMode 透传;
|
|
23
|
+
* - onValueChange 适配 zag details。
|
|
24
|
+
* 视觉 props(不进 machine,适配层挂 data-*):
|
|
25
|
+
* - level / variant / size。
|
|
26
|
+
*/
|
|
27
|
+
export interface QwTabsProps {
|
|
28
|
+
/** 唯一 id 前缀;不传由适配层生成 (SSR/CSR 一致)。 */
|
|
29
|
+
id?: string;
|
|
30
|
+
/** 受控当前项。 */
|
|
31
|
+
value?: string | null;
|
|
32
|
+
/** 非受控初值。 */
|
|
33
|
+
defaultValue?: string | null;
|
|
34
|
+
/** 激活模式,默认 automatic(聚焦即激活)。 */
|
|
35
|
+
activationMode?: "manual" | "automatic";
|
|
36
|
+
/** 方向键首尾循环,默认 true (zag 原名 loopFocus)。 */
|
|
37
|
+
loop?: boolean;
|
|
38
|
+
/** 选中项变化回调;zag 原生 onValueChange。 */
|
|
39
|
+
onValueChange?: (e: {
|
|
40
|
+
value: string;
|
|
41
|
+
}) => void;
|
|
42
|
+
/** 导航层级,默认 primary。 */
|
|
43
|
+
level?: TabsLevel;
|
|
44
|
+
/** 视觉形态,默认 text。level=primary 时强制 text。 */
|
|
45
|
+
variant?: TabsVariant;
|
|
46
|
+
/** 字号,默认 "16"。仅 level=primary 生效。 */
|
|
47
|
+
size?: TabsSize;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 直接 re-export zag tabs 的 machine 与 connect —— 不包一层。
|
|
51
|
+
* 用 re-export 保留原始可移植类型 (避免 TS2742)。
|
|
52
|
+
*/
|
|
53
|
+
export { machine as tabsMachine, connect } from "@zag-js/tabs";
|
|
54
|
+
/** rebrand zag 的 TabsApi 类型对外。 */
|
|
55
|
+
export type { Api as QwTabsApi } from "@zag-js/tabs";
|
|
56
|
+
/**
|
|
57
|
+
* 把 QwTabsProps 的行为 props 转成 zag tabs machine 的 props (默认值收口 + 命名 rebrand)。
|
|
58
|
+
* 视觉 props(level/variant/size)不出现在返回值中——它们是适配层 data-* 属性。
|
|
59
|
+
* 用法:`useMachine(tabsMachine, createTabsProps(props))`。
|
|
60
|
+
*/
|
|
61
|
+
export declare function createTabsProps(props?: QwTabsProps): Partial<ZagTabsProps>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./textarea.js";
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { createMachine } from "@zag-js/core";
|
|
2
|
+
import { compact } from "@zag-js/utils";
|
|
3
|
+
//#region src/textarea/textarea.ts
|
|
4
|
+
/**
|
|
5
|
+
* TextArea zag.js machine。
|
|
6
|
+
* 所有交互逻辑唯一来源:value、focus、clear、pasteRecognize。
|
|
7
|
+
*/
|
|
8
|
+
const machine = createMachine({
|
|
9
|
+
props({ props }) {
|
|
10
|
+
return {
|
|
11
|
+
defaultValue: "",
|
|
12
|
+
disabled: false,
|
|
13
|
+
readOnly: false,
|
|
14
|
+
showActions: true,
|
|
15
|
+
autoSize: false,
|
|
16
|
+
rows: 4,
|
|
17
|
+
...props
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
initialState() {
|
|
21
|
+
return "idle";
|
|
22
|
+
},
|
|
23
|
+
context({ bindable, prop }) {
|
|
24
|
+
return {
|
|
25
|
+
value: bindable(() => {
|
|
26
|
+
const controlledValue = prop("value");
|
|
27
|
+
return {
|
|
28
|
+
defaultValue: prop("defaultValue") ?? "",
|
|
29
|
+
sync: true,
|
|
30
|
+
...controlledValue !== void 0 ? { value: controlledValue } : {},
|
|
31
|
+
onChange(value, prev) {
|
|
32
|
+
if (value !== prev) prop("onChange")?.(value);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}),
|
|
36
|
+
focused: bindable(() => ({ defaultValue: false }))
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
computed: {
|
|
40
|
+
charCount({ context }) {
|
|
41
|
+
return context.get("value").length;
|
|
42
|
+
},
|
|
43
|
+
hasMaxLength({ prop }) {
|
|
44
|
+
const max = prop("maxLength");
|
|
45
|
+
return max != null && max > 0;
|
|
46
|
+
},
|
|
47
|
+
showClear({ context, prop }) {
|
|
48
|
+
return context.get("value").length > 0 && !prop("disabled") && !prop("readOnly");
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
watch({ track, prop, context }) {
|
|
52
|
+
track([() => prop("value")], () => {
|
|
53
|
+
const external = prop("value");
|
|
54
|
+
if (external !== void 0 && external !== context.get("value")) context.set("value", external);
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
states: { idle: { on: {
|
|
58
|
+
"VALUE.SET": { actions: ["setValue"] },
|
|
59
|
+
"VALUE.CLEAR": { actions: ["clearValue", "invokeOnClear"] },
|
|
60
|
+
"FOCUS.SET": { actions: ["setFocused"] },
|
|
61
|
+
PASTE_RECOGNIZE: { actions: ["invokeOnPasteRecognize"] }
|
|
62
|
+
} } },
|
|
63
|
+
implementations: { actions: {
|
|
64
|
+
setValue({ context, event, prop }) {
|
|
65
|
+
if (event.type === "VALUE.SET") {
|
|
66
|
+
const maxLength = prop("maxLength") ?? 0;
|
|
67
|
+
let next = event.value;
|
|
68
|
+
if (maxLength > 0 && next.length > maxLength) next = next.slice(0, maxLength);
|
|
69
|
+
context.set("value", next);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
clearValue({ context }) {
|
|
73
|
+
context.set("value", "");
|
|
74
|
+
},
|
|
75
|
+
setFocused({ context, event, prop }) {
|
|
76
|
+
if (event.type === "FOCUS.SET") {
|
|
77
|
+
const next = event.focused;
|
|
78
|
+
const prev = context.get("focused");
|
|
79
|
+
context.set("focused", next);
|
|
80
|
+
if (next && !prev) prop("onFocus")?.();
|
|
81
|
+
if (!next && prev) prop("onBlur")?.();
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
invokeOnClear({ prop }) {
|
|
85
|
+
prop("onClear")?.();
|
|
86
|
+
},
|
|
87
|
+
invokeOnPasteRecognize({ prop }) {
|
|
88
|
+
prop("onPasteRecognize")?.();
|
|
89
|
+
},
|
|
90
|
+
invokeOnFocus() {},
|
|
91
|
+
invokeOnBlur() {}
|
|
92
|
+
} }
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* connect:把 service 接成业务友好的 prop-getter。
|
|
96
|
+
* 适配层只需调用 connect(service, normalizeProps)。
|
|
97
|
+
*/
|
|
98
|
+
function connect(service, normalizeProps) {
|
|
99
|
+
const { context, computed, prop, send } = service;
|
|
100
|
+
const value = context.get("value");
|
|
101
|
+
const focused = context.get("focused");
|
|
102
|
+
const charCount = computed("charCount");
|
|
103
|
+
const hasMaxLength = computed("hasMaxLength");
|
|
104
|
+
const showClear = computed("showClear");
|
|
105
|
+
const disabled = !!prop("disabled");
|
|
106
|
+
const readOnly = !!prop("readOnly");
|
|
107
|
+
const showActions = prop("showActions") ?? true;
|
|
108
|
+
const autoSize = !!prop("autoSize");
|
|
109
|
+
const maxLength = prop("maxLength");
|
|
110
|
+
const rows = prop("rows") ?? 4;
|
|
111
|
+
const isEmpty = value.length === 0;
|
|
112
|
+
return {
|
|
113
|
+
value,
|
|
114
|
+
focused,
|
|
115
|
+
charCount,
|
|
116
|
+
hasMaxLength,
|
|
117
|
+
showClear,
|
|
118
|
+
clear() {
|
|
119
|
+
send({ type: "VALUE.CLEAR" });
|
|
120
|
+
},
|
|
121
|
+
pasteRecognize() {
|
|
122
|
+
send({ type: "PASTE_RECOGNIZE" });
|
|
123
|
+
},
|
|
124
|
+
getRootProps() {
|
|
125
|
+
return normalizeProps.element(compact({
|
|
126
|
+
"data-scope": "textarea",
|
|
127
|
+
"data-part": "root",
|
|
128
|
+
"data-focused": focused ? "true" : void 0,
|
|
129
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
130
|
+
"data-readonly": readOnly ? "true" : void 0,
|
|
131
|
+
"data-show-actions": showActions ? "true" : "false",
|
|
132
|
+
"data-auto-size": autoSize ? "true" : void 0,
|
|
133
|
+
"data-empty": isEmpty ? "true" : "false"
|
|
134
|
+
}));
|
|
135
|
+
},
|
|
136
|
+
getTextareaProps() {
|
|
137
|
+
return normalizeProps.textarea(compact({
|
|
138
|
+
"data-scope": "textarea",
|
|
139
|
+
"data-part": "textarea",
|
|
140
|
+
value,
|
|
141
|
+
placeholder: prop("placeholder"),
|
|
142
|
+
disabled,
|
|
143
|
+
readOnly,
|
|
144
|
+
maxLength: maxLength && maxLength > 0 ? maxLength : void 0,
|
|
145
|
+
rows,
|
|
146
|
+
name: prop("name"),
|
|
147
|
+
"aria-disabled": disabled ? "true" : void 0,
|
|
148
|
+
"aria-readonly": readOnly ? "true" : void 0,
|
|
149
|
+
onFocus() {
|
|
150
|
+
send({
|
|
151
|
+
type: "FOCUS.SET",
|
|
152
|
+
focused: true
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
onBlur() {
|
|
156
|
+
send({
|
|
157
|
+
type: "FOCUS.SET",
|
|
158
|
+
focused: false
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
onChange(event) {
|
|
162
|
+
const target = event.target;
|
|
163
|
+
const isComposing = !!event.nativeEvent?.isComposing;
|
|
164
|
+
let next = target.value;
|
|
165
|
+
if (!isComposing) {
|
|
166
|
+
const maxLen = prop("maxLength") ?? 0;
|
|
167
|
+
if (maxLen > 0 && next.length > maxLen) next = next.slice(0, maxLen);
|
|
168
|
+
}
|
|
169
|
+
context.set("value", next);
|
|
170
|
+
}
|
|
171
|
+
}));
|
|
172
|
+
},
|
|
173
|
+
getBottomBarProps() {
|
|
174
|
+
return normalizeProps.element(compact({
|
|
175
|
+
"data-scope": "textarea",
|
|
176
|
+
"data-part": "bottom-bar",
|
|
177
|
+
"data-show-actions": showActions ? "true" : "false"
|
|
178
|
+
}));
|
|
179
|
+
},
|
|
180
|
+
getCounterProps() {
|
|
181
|
+
return normalizeProps.element(compact({
|
|
182
|
+
"data-scope": "textarea",
|
|
183
|
+
"data-part": "counter",
|
|
184
|
+
"data-has-max-length": hasMaxLength ? "true" : void 0,
|
|
185
|
+
"aria-live": "polite"
|
|
186
|
+
}));
|
|
187
|
+
},
|
|
188
|
+
getClearButtonProps() {
|
|
189
|
+
return normalizeProps.button(compact({
|
|
190
|
+
"data-scope": "textarea",
|
|
191
|
+
"data-part": "clear-button",
|
|
192
|
+
type: "button",
|
|
193
|
+
disabled: !showClear || disabled || readOnly,
|
|
194
|
+
"aria-label": "清除内容",
|
|
195
|
+
onClick() {
|
|
196
|
+
send({ type: "VALUE.CLEAR" });
|
|
197
|
+
}
|
|
198
|
+
}));
|
|
199
|
+
},
|
|
200
|
+
getPasteButtonProps() {
|
|
201
|
+
return normalizeProps.button(compact({
|
|
202
|
+
"data-scope": "textarea",
|
|
203
|
+
"data-part": "paste-button",
|
|
204
|
+
type: "button",
|
|
205
|
+
disabled,
|
|
206
|
+
"aria-label": "粘贴并识别",
|
|
207
|
+
onClick() {
|
|
208
|
+
send({ type: "PASTE_RECOGNIZE" });
|
|
209
|
+
}
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 把 QwTextAreaProps 转成 machine context(默认值收口)。
|
|
216
|
+
*/
|
|
217
|
+
function createTextAreaProps(props = {}) {
|
|
218
|
+
return {
|
|
219
|
+
defaultValue: props.defaultValue ?? "",
|
|
220
|
+
disabled: props.disabled ?? false,
|
|
221
|
+
readOnly: props.readOnly ?? false,
|
|
222
|
+
showActions: props.showActions ?? true,
|
|
223
|
+
autoSize: props.autoSize ?? false,
|
|
224
|
+
rows: props.rows ?? 4,
|
|
225
|
+
...props
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
export { connect, createTextAreaProps, machine };
|
|
230
|
+
|
|
231
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/textarea/textarea.ts"],"sourcesContent":["import { createMachine } from \"@zag-js/core\";\nimport type { MachineSchema, Service } from \"@zag-js/core\";\nimport type { PropTypes, NormalizeProps } from \"@zag-js/types\";\nimport type { JSX } from \"@zag-js/types\";\nimport { compact } from \"@zag-js/utils\";\n\n/**\n * TextArea 公共 props 契约(双栈共享)。\n */\nexport interface QwTextAreaProps {\n /** 唯一 id 前缀。 */\n id?: string;\n /** 受控值。 */\n value?: string;\n /** 非受控初始值。 */\n defaultValue?: string;\n /** 占位符。 */\n placeholder?: string;\n /** 最大字符数。 */\n maxLength?: number;\n /** 禁用态。 */\n disabled?: boolean;\n /** 只读态。 */\n readOnly?: boolean;\n /** 可视行数(影响最小高度)。 */\n rows?: number;\n /** 自适应高度。 */\n autoSize?: boolean;\n /** 显示底部操作按钮。 */\n showActions?: boolean;\n /** name 属性。 */\n name?: string;\n /** 输入变化回调。 */\n onChange?: (value: string) => void;\n /** 清空回调。 */\n onClear?: () => void;\n /** 粘贴并识别回调。 */\n onPasteRecognize?: () => void;\n /** 聚焦回调。 */\n onFocus?: () => void;\n /** 失焦回调。 */\n onBlur?: () => void;\n}\n\n/**\n * 内部 schema,用于 createMachine 类型推导。\n */\ninterface TextAreaSchema extends MachineSchema {\n props: QwTextAreaProps;\n context: {\n value: string;\n focused: boolean;\n };\n computed: {\n charCount: number;\n hasMaxLength: boolean;\n showClear: boolean;\n };\n state: \"idle\";\n event:\n | { type: \"VALUE.SET\"; value: string }\n | { type: \"VALUE.CLEAR\" }\n | { type: \"FOCUS.SET\"; focused: boolean }\n | { type: \"PASTE_RECOGNIZE\" };\n action:\n | \"setValue\"\n | \"clearValue\"\n | \"setFocused\"\n | \"invokeOnClear\"\n | \"invokeOnPasteRecognize\"\n | \"invokeOnFocus\"\n | \"invokeOnBlur\";\n}\n\n/**\n * TextArea zag.js machine。\n * 所有交互逻辑唯一来源:value、focus、clear、pasteRecognize。\n */\nexport const machine = createMachine<TextAreaSchema>({\n props({ props }) {\n return {\n defaultValue: \"\",\n disabled: false,\n readOnly: false,\n showActions: true,\n autoSize: false,\n rows: 4,\n ...props,\n };\n },\n\n initialState() {\n return \"idle\";\n },\n\n context({ bindable, prop }) {\n return {\n value: bindable<string>(() => {\n const controlledValue = prop(\"value\");\n return {\n defaultValue: prop(\"defaultValue\") ?? \"\",\n sync: true,\n ...(controlledValue !== undefined ? { value: controlledValue } : {}),\n onChange(value, prev) {\n if (value !== prev) {\n prop(\"onChange\")?.(value);\n }\n },\n };\n }),\n focused: bindable(() => ({\n defaultValue: false,\n })),\n };\n },\n\n computed: {\n charCount({ context }) {\n return context.get(\"value\").length;\n },\n hasMaxLength({ prop }) {\n const max = prop(\"maxLength\");\n return max != null && max > 0;\n },\n showClear({ context, prop }) {\n return context.get(\"value\").length > 0 && !prop(\"disabled\") && !prop(\"readOnly\");\n },\n },\n\n watch({ track, prop, context }) {\n track([() => prop(\"value\")], () => {\n const external = prop(\"value\");\n if (external !== undefined && external !== context.get(\"value\")) {\n context.set(\"value\", external);\n }\n });\n },\n\n states: {\n idle: {\n on: {\n \"VALUE.SET\": {\n actions: [\"setValue\"],\n },\n \"VALUE.CLEAR\": {\n actions: [\"clearValue\", \"invokeOnClear\"],\n },\n \"FOCUS.SET\": {\n actions: [\"setFocused\"],\n },\n PASTE_RECOGNIZE: {\n actions: [\"invokeOnPasteRecognize\"],\n },\n },\n },\n },\n\n implementations: {\n actions: {\n setValue({ context, event, prop }) {\n if (event.type === \"VALUE.SET\") {\n const maxLength = prop(\"maxLength\") ?? 0;\n let next = event.value;\n if (maxLength > 0 && next.length > maxLength) {\n next = next.slice(0, maxLength);\n }\n context.set(\"value\", next);\n }\n },\n clearValue({ context }) {\n context.set(\"value\", \"\");\n },\n setFocused({ context, event, prop }) {\n if (event.type === \"FOCUS.SET\") {\n const next = event.focused;\n const prev = context.get(\"focused\");\n context.set(\"focused\", next);\n if (next && !prev) prop(\"onFocus\")?.();\n if (!next && prev) prop(\"onBlur\")?.();\n }\n },\n invokeOnClear({ prop }) {\n prop(\"onClear\")?.();\n },\n invokeOnPasteRecognize({ prop }) {\n prop(\"onPasteRecognize\")?.();\n },\n invokeOnFocus() {\n // focus 回调在 setFocused 中已触发\n },\n invokeOnBlur() {\n // blur 回调在 setFocused 中已触发\n },\n },\n },\n});\n\n/**\n * TextArea api 类型(由 connect 返回)。\n */\nexport interface TextAreaApi<T extends PropTypes = PropTypes> {\n /** 当前输入值。 */\n value: string;\n /** 当前是否聚焦。 */\n focused: boolean;\n /** 当前字符数。 */\n charCount: number;\n /** 是否有字数限制。 */\n hasMaxLength: boolean;\n /** 是否显示清除按钮。 */\n showClear: boolean;\n /** 清空输入。 */\n clear(): void;\n /** 触发粘贴并识别回调。 */\n pasteRecognize(): void;\n /** 根容器 prop-getter。 */\n getRootProps(): T[\"element\"];\n /** textarea 元素 prop-getter。 */\n getTextareaProps(): T[\"textarea\"];\n /** 底部栏 prop-getter。 */\n getBottomBarProps(): T[\"element\"];\n /** 字数统计 prop-getter。 */\n getCounterProps(): T[\"element\"];\n /** 清除按钮 prop-getter。 */\n getClearButtonProps(): T[\"button\"];\n /** 粘贴按钮 prop-getter。 */\n getPasteButtonProps(): T[\"button\"];\n}\n\n/**\n * connect:把 service 接成业务友好的 prop-getter。\n * 适配层只需调用 connect(service, normalizeProps)。\n */\nexport function connect<T extends PropTypes>(service: Service<TextAreaSchema>, normalizeProps: NormalizeProps<T>): TextAreaApi<T> {\n const { context, computed, prop, send } = service;\n\n const value = context.get(\"value\");\n const focused = context.get(\"focused\");\n const charCount = computed(\"charCount\");\n const hasMaxLength = computed(\"hasMaxLength\");\n const showClear = computed(\"showClear\");\n const disabled = !!prop(\"disabled\");\n const readOnly = !!prop(\"readOnly\");\n const showActions = prop(\"showActions\") ?? true;\n const autoSize = !!prop(\"autoSize\");\n const maxLength = prop(\"maxLength\");\n const rows = prop(\"rows\") ?? 4;\n const isEmpty = value.length === 0;\n\n return {\n value,\n focused,\n charCount,\n hasMaxLength,\n showClear,\n\n clear() {\n send({ type: \"VALUE.CLEAR\" });\n },\n\n pasteRecognize() {\n send({ type: \"PASTE_RECOGNIZE\" });\n },\n\n getRootProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"textarea\",\n \"data-part\": \"root\",\n \"data-focused\": focused ? \"true\" : undefined,\n \"data-disabled\": disabled ? \"true\" : undefined,\n \"data-readonly\": readOnly ? \"true\" : undefined,\n \"data-show-actions\": showActions ? \"true\" : \"false\",\n \"data-auto-size\": autoSize ? \"true\" : undefined,\n \"data-empty\": isEmpty ? \"true\" : \"false\",\n }),\n );\n },\n\n getTextareaProps() {\n return normalizeProps.textarea(\n compact({\n \"data-scope\": \"textarea\",\n \"data-part\": \"textarea\",\n value,\n placeholder: prop(\"placeholder\"),\n disabled,\n readOnly,\n maxLength: maxLength && maxLength > 0 ? maxLength : undefined,\n rows,\n name: prop(\"name\"),\n \"aria-disabled\": disabled ? (\"true\" as const) : undefined,\n \"aria-readonly\": readOnly ? (\"true\" as const) : undefined,\n onFocus() {\n send({ type: \"FOCUS.SET\", focused: true });\n },\n onBlur() {\n send({ type: \"FOCUS.SET\", focused: false });\n },\n onChange(event: JSX.ChangeEvent<HTMLTextAreaElement>) {\n const target = event.target as unknown as HTMLTextAreaElement;\n const nativeEvent = (event as { nativeEvent?: { isComposing?: boolean } }).nativeEvent;\n const isComposing = !!nativeEvent?.isComposing;\n let next = target.value;\n // IME composing 期间跳过 maxLength 截断,允许拼音中间态值流过\n // 防止 React 受控组件因值未更新而重置 DOM,打断输入法\n if (!isComposing) {\n const maxLen = prop(\"maxLength\") ?? 0;\n if (maxLen > 0 && next.length > maxLen) {\n next = next.slice(0, maxLen);\n }\n }\n // 直接同步更新,绕过 send() 的 queueMicrotask 延迟\n context.set(\"value\", next);\n },\n }),\n );\n },\n\n getBottomBarProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"textarea\",\n \"data-part\": \"bottom-bar\",\n \"data-show-actions\": showActions ? \"true\" : \"false\",\n }),\n );\n },\n\n getCounterProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"textarea\",\n \"data-part\": \"counter\",\n \"data-has-max-length\": hasMaxLength ? \"true\" : undefined,\n \"aria-live\": \"polite\" as const,\n }),\n );\n },\n\n getClearButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"textarea\",\n \"data-part\": \"clear-button\",\n type: \"button\" as const,\n disabled: !showClear || disabled || readOnly,\n \"aria-label\": \"清除内容\" as const,\n onClick() {\n send({ type: \"VALUE.CLEAR\" });\n },\n }),\n );\n },\n\n getPasteButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"textarea\",\n \"data-part\": \"paste-button\",\n type: \"button\" as const,\n disabled,\n \"aria-label\": \"粘贴并识别\" as const,\n onClick() {\n send({ type: \"PASTE_RECOGNIZE\" });\n },\n }),\n );\n },\n };\n}\n\n/**\n * 把 QwTextAreaProps 转成 machine context(默认值收口)。\n */\nexport function createTextAreaProps(props: QwTextAreaProps = {}): Partial<QwTextAreaProps> {\n return {\n defaultValue: props.defaultValue ?? \"\",\n disabled: props.disabled ?? false,\n readOnly: props.readOnly ?? false,\n showActions: props.showActions ?? true,\n autoSize: props.autoSize ?? false,\n rows: props.rows ?? 4,\n ...props,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { MachineSchema, Service } from "@zag-js/core";
|
|
2
|
+
import type { PropTypes, NormalizeProps } from "@zag-js/types";
|
|
3
|
+
/**
|
|
4
|
+
* TextArea 公共 props 契约(双栈共享)。
|
|
5
|
+
*/
|
|
6
|
+
export interface QwTextAreaProps {
|
|
7
|
+
/** 唯一 id 前缀。 */
|
|
8
|
+
id?: string;
|
|
9
|
+
/** 受控值。 */
|
|
10
|
+
value?: string;
|
|
11
|
+
/** 非受控初始值。 */
|
|
12
|
+
defaultValue?: string;
|
|
13
|
+
/** 占位符。 */
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
/** 最大字符数。 */
|
|
16
|
+
maxLength?: number;
|
|
17
|
+
/** 禁用态。 */
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
/** 只读态。 */
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
/** 可视行数(影响最小高度)。 */
|
|
22
|
+
rows?: number;
|
|
23
|
+
/** 自适应高度。 */
|
|
24
|
+
autoSize?: boolean;
|
|
25
|
+
/** 显示底部操作按钮。 */
|
|
26
|
+
showActions?: boolean;
|
|
27
|
+
/** name 属性。 */
|
|
28
|
+
name?: string;
|
|
29
|
+
/** 输入变化回调。 */
|
|
30
|
+
onChange?: (value: string) => void;
|
|
31
|
+
/** 清空回调。 */
|
|
32
|
+
onClear?: () => void;
|
|
33
|
+
/** 粘贴并识别回调。 */
|
|
34
|
+
onPasteRecognize?: () => void;
|
|
35
|
+
/** 聚焦回调。 */
|
|
36
|
+
onFocus?: () => void;
|
|
37
|
+
/** 失焦回调。 */
|
|
38
|
+
onBlur?: () => void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 内部 schema,用于 createMachine 类型推导。
|
|
42
|
+
*/
|
|
43
|
+
interface TextAreaSchema extends MachineSchema {
|
|
44
|
+
props: QwTextAreaProps;
|
|
45
|
+
context: {
|
|
46
|
+
value: string;
|
|
47
|
+
focused: boolean;
|
|
48
|
+
};
|
|
49
|
+
computed: {
|
|
50
|
+
charCount: number;
|
|
51
|
+
hasMaxLength: boolean;
|
|
52
|
+
showClear: boolean;
|
|
53
|
+
};
|
|
54
|
+
state: "idle";
|
|
55
|
+
event: {
|
|
56
|
+
type: "VALUE.SET";
|
|
57
|
+
value: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: "VALUE.CLEAR";
|
|
60
|
+
} | {
|
|
61
|
+
type: "FOCUS.SET";
|
|
62
|
+
focused: boolean;
|
|
63
|
+
} | {
|
|
64
|
+
type: "PASTE_RECOGNIZE";
|
|
65
|
+
};
|
|
66
|
+
action: "setValue" | "clearValue" | "setFocused" | "invokeOnClear" | "invokeOnPasteRecognize" | "invokeOnFocus" | "invokeOnBlur";
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* TextArea zag.js machine。
|
|
70
|
+
* 所有交互逻辑唯一来源:value、focus、clear、pasteRecognize。
|
|
71
|
+
*/
|
|
72
|
+
export declare const machine: import("@zag-js/core").Machine<TextAreaSchema>;
|
|
73
|
+
/**
|
|
74
|
+
* TextArea api 类型(由 connect 返回)。
|
|
75
|
+
*/
|
|
76
|
+
export interface TextAreaApi<T extends PropTypes = PropTypes> {
|
|
77
|
+
/** 当前输入值。 */
|
|
78
|
+
value: string;
|
|
79
|
+
/** 当前是否聚焦。 */
|
|
80
|
+
focused: boolean;
|
|
81
|
+
/** 当前字符数。 */
|
|
82
|
+
charCount: number;
|
|
83
|
+
/** 是否有字数限制。 */
|
|
84
|
+
hasMaxLength: boolean;
|
|
85
|
+
/** 是否显示清除按钮。 */
|
|
86
|
+
showClear: boolean;
|
|
87
|
+
/** 清空输入。 */
|
|
88
|
+
clear(): void;
|
|
89
|
+
/** 触发粘贴并识别回调。 */
|
|
90
|
+
pasteRecognize(): void;
|
|
91
|
+
/** 根容器 prop-getter。 */
|
|
92
|
+
getRootProps(): T["element"];
|
|
93
|
+
/** textarea 元素 prop-getter。 */
|
|
94
|
+
getTextareaProps(): T["textarea"];
|
|
95
|
+
/** 底部栏 prop-getter。 */
|
|
96
|
+
getBottomBarProps(): T["element"];
|
|
97
|
+
/** 字数统计 prop-getter。 */
|
|
98
|
+
getCounterProps(): T["element"];
|
|
99
|
+
/** 清除按钮 prop-getter。 */
|
|
100
|
+
getClearButtonProps(): T["button"];
|
|
101
|
+
/** 粘贴按钮 prop-getter。 */
|
|
102
|
+
getPasteButtonProps(): T["button"];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* connect:把 service 接成业务友好的 prop-getter。
|
|
106
|
+
* 适配层只需调用 connect(service, normalizeProps)。
|
|
107
|
+
*/
|
|
108
|
+
export declare function connect<T extends PropTypes>(service: Service<TextAreaSchema>, normalizeProps: NormalizeProps<T>): TextAreaApi<T>;
|
|
109
|
+
/**
|
|
110
|
+
* 把 QwTextAreaProps 转成 machine context(默认值收口)。
|
|
111
|
+
*/
|
|
112
|
+
export declare function createTextAreaProps(props?: QwTextAreaProps): Partial<QwTextAreaProps>;
|
|
113
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./toast.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/toast/toast.ts
|
|
2
|
+
/**
|
|
3
|
+
* @qw-ui/core · Toast 契约(原型 D · 自研命令式单例)
|
|
4
|
+
* ─────────────────────────────────────────────────────────────
|
|
5
|
+
* 纯 TS / SSR-safe,零框架依赖。只收口「框架无关的选项契约 + 纯函数」:
|
|
6
|
+
* 交互(命令式挂载 / 计时 / 渲染)落在各框架适配层,节点类型(icon/content)
|
|
7
|
+
* 由适配层用 `ReactNode` / `VNode` 具体化 `QwToastOptions<Node>`。
|
|
8
|
+
*/
|
|
9
|
+
/** 垂直显示位置。 */
|
|
10
|
+
const TOAST_POSITIONS = [
|
|
11
|
+
"top",
|
|
12
|
+
"bottom",
|
|
13
|
+
"center"
|
|
14
|
+
];
|
|
15
|
+
/** 内建状态图标类型。 */
|
|
16
|
+
const TOAST_ICON_TYPES = [
|
|
17
|
+
"success",
|
|
18
|
+
"fail",
|
|
19
|
+
"loading"
|
|
20
|
+
];
|
|
21
|
+
/** 逻辑部件(承载 data-part 样式与 parity 指纹)。 */
|
|
22
|
+
const TOAST_PARTS = [
|
|
23
|
+
"mask",
|
|
24
|
+
"wrap",
|
|
25
|
+
"body",
|
|
26
|
+
"icon",
|
|
27
|
+
"content"
|
|
28
|
+
];
|
|
29
|
+
/** 默认展示时长(ms)。 */
|
|
30
|
+
const DEFAULT_TOAST_DURATION = 3e3;
|
|
31
|
+
/** 默认垂直位置(对齐设计稿:默认顶部)。 */
|
|
32
|
+
const DEFAULT_TOAST_POSITION = "top";
|
|
33
|
+
/** 判断 icon 是否为内建状态图标类型。 */
|
|
34
|
+
function isToastIconType(icon) {
|
|
35
|
+
return typeof icon === "string" && TOAST_ICON_TYPES.includes(icon);
|
|
36
|
+
}
|
|
37
|
+
/** 解析持续时间:未传用默认 3000;显式 0 透传(不自动关闭)。 */
|
|
38
|
+
function resolveToastDuration(duration) {
|
|
39
|
+
return duration == null ? DEFAULT_TOAST_DURATION : duration;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { DEFAULT_TOAST_DURATION, DEFAULT_TOAST_POSITION, TOAST_ICON_TYPES, TOAST_PARTS, TOAST_POSITIONS, isToastIconType, resolveToastDuration };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/toast/toast.ts"],"sourcesContent":["/**\n * @qw-ui/core · Toast 契约(原型 D · 自研命令式单例)\n * ─────────────────────────────────────────────────────────────\n * 纯 TS / SSR-safe,零框架依赖。只收口「框架无关的选项契约 + 纯函数」:\n * 交互(命令式挂载 / 计时 / 渲染)落在各框架适配层,节点类型(icon/content)\n * 由适配层用 `ReactNode` / `VNode` 具体化 `QwToastOptions<Node>`。\n */\n\n/** 垂直显示位置。 */\nexport const TOAST_POSITIONS = [\"top\", \"bottom\", \"center\"] as const;\nexport type QwToastPosition = (typeof TOAST_POSITIONS)[number];\n\n/** 内建状态图标类型。 */\nexport const TOAST_ICON_TYPES = [\"success\", \"fail\", \"loading\"] as const;\nexport type QwToastIconType = (typeof TOAST_ICON_TYPES)[number];\n\n/** 逻辑部件(承载 data-part 样式与 parity 指纹)。 */\nexport const TOAST_PARTS = [\"mask\", \"wrap\", \"body\", \"icon\", \"content\"] as const;\nexport type QwToastPart = (typeof TOAST_PARTS)[number];\n\n/** 默认展示时长(ms)。 */\nexport const DEFAULT_TOAST_DURATION = 3000;\n\n/** 默认垂直位置(对齐设计稿:默认顶部)。 */\nexport const DEFAULT_TOAST_POSITION: QwToastPosition = \"top\";\n\n/**\n * Toast 选项契约。`Node` / `Style` 由适配层具体化\n * (React `ReactNode` / `CSSProperties`;Vue `VNode` / `StyleValue`)。\n * 与 antd-mobile 风格对齐:命令式 `Toast.show(options)`。\n */\nexport interface QwToastOptions<Node = unknown, Style = unknown> {\n /** 提示主文案。可空(纯图标提示)。 */\n content?: Node;\n /** 前置图标:内建类型 `success | fail | loading`,或任意自定义节点;不传则不展示图标。 */\n icon?: QwToastIconType | Node;\n /** 持续时间(ms),默认 3000;为 0 时不自动关闭。 */\n duration?: number;\n /** 垂直显示位置,默认 top。 */\n position?: QwToastPosition;\n /** 自定义遮罩层内联样式。 */\n maskStyle?: Style;\n /** 自定义遮罩层 class。 */\n maskClassName?: string;\n /** 关闭后回调(自动消失或手动 clear 均触发)。 */\n afterClose?: () => void;\n}\n\n/** 命令式 Toast 单例接口(各框架适配层实现同名同义)。 */\nexport interface QwToast<Node = unknown, Style = unknown> {\n /** 展示一条提示(替换上一条)。 */\n show: (options?: QwToastOptions<Node, Style>) => void;\n /** 立即收起当前提示。 */\n clear: () => void;\n}\n\n/** 判断 icon 是否为内建状态图标类型。 */\nexport function isToastIconType(icon: unknown): icon is QwToastIconType {\n return typeof icon === \"string\" && (TOAST_ICON_TYPES as readonly string[]).includes(icon);\n}\n\n/** 解析持续时间:未传用默认 3000;显式 0 透传(不自动关闭)。 */\nexport function resolveToastDuration(duration?: number): number {\n return duration == null ? DEFAULT_TOAST_DURATION : duration;\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,kBAAkB;CAAC;CAAO;CAAU;AAAQ;;AAIzD,MAAa,mBAAmB;CAAC;CAAW;CAAQ;AAAS;;AAI7D,MAAa,cAAc;CAAC;CAAQ;CAAQ;CAAQ;CAAQ;AAAS;;AAIrE,MAAa,yBAAyB;;AAGtC,MAAa,yBAA0C;;AAiCvD,SAAgB,gBAAgB,MAAwC;CACtE,OAAO,OAAO,SAAS,YAAa,iBAAuC,SAAS,IAAI;AAC1F;;AAGA,SAAgB,qBAAqB,UAA2B;CAC9D,OAAO,YAAY,OAAO,yBAAyB;AACrD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @qw-ui/core · Toast 契约(原型 D · 自研命令式单例)
|
|
3
|
+
* ─────────────────────────────────────────────────────────────
|
|
4
|
+
* 纯 TS / SSR-safe,零框架依赖。只收口「框架无关的选项契约 + 纯函数」:
|
|
5
|
+
* 交互(命令式挂载 / 计时 / 渲染)落在各框架适配层,节点类型(icon/content)
|
|
6
|
+
* 由适配层用 `ReactNode` / `VNode` 具体化 `QwToastOptions<Node>`。
|
|
7
|
+
*/
|
|
8
|
+
/** 垂直显示位置。 */
|
|
9
|
+
export declare const TOAST_POSITIONS: readonly ["top", "bottom", "center"];
|
|
10
|
+
export type QwToastPosition = (typeof TOAST_POSITIONS)[number];
|
|
11
|
+
/** 内建状态图标类型。 */
|
|
12
|
+
export declare const TOAST_ICON_TYPES: readonly ["success", "fail", "loading"];
|
|
13
|
+
export type QwToastIconType = (typeof TOAST_ICON_TYPES)[number];
|
|
14
|
+
/** 逻辑部件(承载 data-part 样式与 parity 指纹)。 */
|
|
15
|
+
export declare const TOAST_PARTS: readonly ["mask", "wrap", "body", "icon", "content"];
|
|
16
|
+
export type QwToastPart = (typeof TOAST_PARTS)[number];
|
|
17
|
+
/** 默认展示时长(ms)。 */
|
|
18
|
+
export declare const DEFAULT_TOAST_DURATION = 3000;
|
|
19
|
+
/** 默认垂直位置(对齐设计稿:默认顶部)。 */
|
|
20
|
+
export declare const DEFAULT_TOAST_POSITION: QwToastPosition;
|
|
21
|
+
/**
|
|
22
|
+
* Toast 选项契约。`Node` / `Style` 由适配层具体化
|
|
23
|
+
* (React `ReactNode` / `CSSProperties`;Vue `VNode` / `StyleValue`)。
|
|
24
|
+
* 与 antd-mobile 风格对齐:命令式 `Toast.show(options)`。
|
|
25
|
+
*/
|
|
26
|
+
export interface QwToastOptions<Node = unknown, Style = unknown> {
|
|
27
|
+
/** 提示主文案。可空(纯图标提示)。 */
|
|
28
|
+
content?: Node;
|
|
29
|
+
/** 前置图标:内建类型 `success | fail | loading`,或任意自定义节点;不传则不展示图标。 */
|
|
30
|
+
icon?: QwToastIconType | Node;
|
|
31
|
+
/** 持续时间(ms),默认 3000;为 0 时不自动关闭。 */
|
|
32
|
+
duration?: number;
|
|
33
|
+
/** 垂直显示位置,默认 top。 */
|
|
34
|
+
position?: QwToastPosition;
|
|
35
|
+
/** 自定义遮罩层内联样式。 */
|
|
36
|
+
maskStyle?: Style;
|
|
37
|
+
/** 自定义遮罩层 class。 */
|
|
38
|
+
maskClassName?: string;
|
|
39
|
+
/** 关闭后回调(自动消失或手动 clear 均触发)。 */
|
|
40
|
+
afterClose?: () => void;
|
|
41
|
+
}
|
|
42
|
+
/** 命令式 Toast 单例接口(各框架适配层实现同名同义)。 */
|
|
43
|
+
export interface QwToast<Node = unknown, Style = unknown> {
|
|
44
|
+
/** 展示一条提示(替换上一条)。 */
|
|
45
|
+
show: (options?: QwToastOptions<Node, Style>) => void;
|
|
46
|
+
/** 立即收起当前提示。 */
|
|
47
|
+
clear: () => void;
|
|
48
|
+
}
|
|
49
|
+
/** 判断 icon 是否为内建状态图标类型。 */
|
|
50
|
+
export declare function isToastIconType(icon: unknown): icon is QwToastIconType;
|
|
51
|
+
/** 解析持续时间:未传用默认 3000;显式 0 透传(不自动关闭)。 */
|
|
52
|
+
export declare function resolveToastDuration(duration?: number): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./top-bar.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/top-bar/top-bar.ts
|
|
2
|
+
/**
|
|
3
|
+
* Top Bar 契约 — 原型 A(无状态),core 无 machine,只承载双栈共享的类型契约。
|
|
4
|
+
* React/Vue 适配层从这里 import 类型与 createTopBarProps,不允许各自再定义一份。
|
|
5
|
+
*
|
|
6
|
+
* Top Bar 是内容面板的头部组件,通过 `variant` prop 选择三种互斥结构形式:
|
|
7
|
+
* basic(标题 + 收起 icon)、tab(tablist + 收起 icon)、extended(返回箭头 + 自定义中间 + 关闭 icon)。
|
|
8
|
+
* Tab 切换复用 @qw-ui/core/tabs。
|
|
9
|
+
*
|
|
10
|
+
* 枚举以运行时常量数组为唯一事实源(类型从常量推导),__test__ 直接锁死词表。
|
|
11
|
+
*/
|
|
12
|
+
/** variant 全集:basic 基础 / tab 标签页 / extended 拓展。 */
|
|
13
|
+
const TOPBAR_VARIANTS = [
|
|
14
|
+
"basic",
|
|
15
|
+
"tab",
|
|
16
|
+
"extended"
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* 把 QwTopBarProps 收口默认值,返回适配层配置对象。
|
|
20
|
+
* 不进 machine(原型 A)。纯函数,SSR-safe。
|
|
21
|
+
*/
|
|
22
|
+
function createTopBarProps(props) {
|
|
23
|
+
const items = props.items;
|
|
24
|
+
return {
|
|
25
|
+
variant: props.variant,
|
|
26
|
+
title: props.title,
|
|
27
|
+
items,
|
|
28
|
+
defaultValue: props.defaultValue ?? null,
|
|
29
|
+
value: props.value ?? null,
|
|
30
|
+
onValueChange: props.onValueChange,
|
|
31
|
+
onCollapse: props.onCollapse,
|
|
32
|
+
collapseLabel: props.collapseLabel ?? "收起",
|
|
33
|
+
onBack: props.onBack,
|
|
34
|
+
backLabel: props.backLabel ?? "返回",
|
|
35
|
+
onClose: props.onClose,
|
|
36
|
+
closeLabel: props.closeLabel ?? "关闭",
|
|
37
|
+
hasTabs: items !== void 0 && items.length > 0
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { TOPBAR_VARIANTS, createTopBarProps };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/top-bar/top-bar.ts"],"sourcesContent":["/**\n * Top Bar 契约 — 原型 A(无状态),core 无 machine,只承载双栈共享的类型契约。\n * React/Vue 适配层从这里 import 类型与 createTopBarProps,不允许各自再定义一份。\n *\n * Top Bar 是内容面板的头部组件,通过 `variant` prop 选择三种互斥结构形式:\n * basic(标题 + 收起 icon)、tab(tablist + 收起 icon)、extended(返回箭头 + 自定义中间 + 关闭 icon)。\n * Tab 切换复用 @qw-ui/core/tabs。\n *\n * 枚举以运行时常量数组为唯一事实源(类型从常量推导),__test__ 直接锁死词表。\n */\n\n/** variant 全集:basic 基础 / tab 标签页 / extended 拓展。 */\nexport const TOPBAR_VARIANTS = [\"basic\", \"tab\", \"extended\"] as const;\n\n/** 结构形式。 */\nexport type TopBarVariant = (typeof TOPBAR_VARIANTS)[number];\n\n/**\n * TopBarItem — Tab 项契约,与 @qw-ui/react/tabs 和 @qw-ui/vue/tabs 的 TabItem 同构。\n * core 定义最小形状(label/content 为 string),适配层各自扩展为 ReactNode / slot。\n */\nexport interface TopBarItem {\n /** 选项唯一值。 */\n value: string;\n /** 选项标签文本。 */\n label: string;\n /** 选项内容(简单文本)。 */\n content?: string;\n /** 禁用该选项:跳过键盘漫游、不可点击。 */\n disabled?: boolean;\n}\n\n/**\n * QwTopBarProps — 双栈共享的行为 + 视觉契约。\n * Tab 相关 props(items/value/defaultValue/onValueChange)透传给 @qw-ui/core/tabs。\n * variant 为必填,其余 props 按 variant 条件可选(flat interface,非 discriminated union)。\n */\nexport interface QwTopBarProps {\n /** 结构形式(必填)。 */\n variant: TopBarVariant;\n\n // ── basic 专用 ──\n /** 标题名称(basic 专用)。 */\n title?: string;\n\n // ── tab 专用 ──\n /** Tab 切换模式的数据项。 */\n items?: TopBarItem[];\n /** Tab 非受控初值。 */\n defaultValue?: string | null;\n /** Tab 受控当前项。 */\n value?: string | null;\n /** Tab 选中项变化回调。 */\n onValueChange?: (e: { value: string }) => void;\n\n // ── basic + tab 共用 ──\n /** 收起 icon 点击回调(关闭内容面板)。 */\n onCollapse?: () => void;\n /** 收起 icon 的 aria-label,默认「收起」。 */\n collapseLabel?: string;\n\n // ── extended 专用 ──\n /** 返回箭头点击回调(面板内导航回退)。 */\n onBack?: () => void;\n /** 返回箭头的 aria-label,默认「返回」。 */\n backLabel?: string;\n /** 关闭 icon 点击回调(关闭面板)。 */\n onClose?: () => void;\n /** 关闭 icon 的 aria-label,默认「关闭」。 */\n closeLabel?: string;\n}\n\n/**\n * ResolvedTopBarProps — createTopBarProps 返回值,适配层消费此对象渲染。\n */\nexport interface ResolvedTopBarProps {\n variant: TopBarVariant;\n title: string | undefined;\n items: TopBarItem[] | undefined;\n defaultValue: string | null;\n value: string | null;\n onValueChange: ((e: { value: string }) => void) | undefined;\n onCollapse: (() => void) | undefined;\n collapseLabel: string;\n onBack: (() => void) | undefined;\n backLabel: string;\n onClose: (() => void) | undefined;\n closeLabel: string;\n hasTabs: boolean;\n}\n\n/**\n * 把 QwTopBarProps 收口默认值,返回适配层配置对象。\n * 不进 machine(原型 A)。纯函数,SSR-safe。\n */\nexport function createTopBarProps(props: QwTopBarProps): ResolvedTopBarProps {\n const items = props.items;\n\n return {\n variant: props.variant,\n title: props.title,\n items,\n defaultValue: props.defaultValue ?? null,\n value: props.value ?? null,\n onValueChange: props.onValueChange,\n onCollapse: props.onCollapse,\n collapseLabel: props.collapseLabel ?? \"收起\",\n onBack: props.onBack,\n backLabel: props.backLabel ?? \"返回\",\n onClose: props.onClose,\n closeLabel: props.closeLabel ?? \"关闭\",\n hasTabs: items !== undefined && items.length > 0,\n };\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,kBAAkB;CAAC;CAAS;CAAO;AAAU;;;;;AAmF1D,SAAgB,kBAAkB,OAA2C;CAC3E,MAAM,QAAQ,MAAM;CAEpB,OAAO;EACL,SAAS,MAAM;EACf,OAAO,MAAM;EACb;EACA,cAAc,MAAM,gBAAgB;EACpC,OAAO,MAAM,SAAS;EACtB,eAAe,MAAM;EACrB,YAAY,MAAM;EAClB,eAAe,MAAM,iBAAiB;EACtC,QAAQ,MAAM;EACd,WAAW,MAAM,aAAa;EAC9B,SAAS,MAAM;EACf,YAAY,MAAM,cAAc;EAChC,SAAS,UAAU,KAAA,KAAa,MAAM,SAAS;CACjD;AACF"}
|