@qw-ui/core 0.2.0 → 0.3.0-next.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/index.d.ts +4 -0
- package/dist/index.js +3 -1
- package/dist/input/index.d.ts +1 -0
- package/dist/input/index.js +139 -0
- package/dist/input/index.js.map +1 -0
- package/dist/input/input.d.ts +96 -0
- package/dist/search-bar/index.js +11 -7
- package/dist/search-bar/index.js.map +1 -1
- package/dist/tag/index.d.ts +1 -0
- package/dist/tag/index.js +22 -0
- package/dist/tag/index.js.map +1 -0
- package/dist/tag/tag.d.ts +34 -0
- package/package.json +10 -2
package/dist/index.d.ts
CHANGED
|
@@ -22,3 +22,7 @@ export { AVATAR_BADGE_TYPES, AVATAR_LOAD_STATUS, AVATAR_PARTS, AVATAR_SHAPES, AV
|
|
|
22
22
|
export type { AvatarBadgeProps, AvatarContentPresence, AvatarFallbackProps, AvatarImageElementProps, AvatarImageProps, AvatarLoadStatus, AvatarPart, AvatarRootProps, AvatarShape, AvatarSize, BadgeConfig, BadgeType, FallbackMode, QwAvatarProps, ResolvedAvatarState, } from "./avatar/index.js";
|
|
23
23
|
export { machine as textAreaMachine, connect as textAreaConnect, createTextAreaProps } from "./textarea/index.js";
|
|
24
24
|
export type { QwTextAreaProps, TextAreaApi } from "./textarea/index.js";
|
|
25
|
+
export { TAG_PARTS, TAG_SIZES, TAG_VARIANTS } from "./tag/index.js";
|
|
26
|
+
export type { TagPart, TagSize, TagVariant, QwTagProps } from "./tag/index.js";
|
|
27
|
+
export { createInputProps } from "./input/index.js";
|
|
28
|
+
export type { QwInputProps, InputApi, InputMode, InputReadOnlyOverflow } from "./input/index.js";
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,6 @@ import { TOPBAR_VARIANTS, createTopBarProps } from "./top-bar/index.js";
|
|
|
10
10
|
import { ICON_FALLBACK_NAME, ICON_PARTS, resolveIconName } from "./icon/index.js";
|
|
11
11
|
import { AVATAR_BADGE_TYPES, AVATAR_LOAD_STATUS, AVATAR_PARTS, AVATAR_SHAPES, AVATAR_SIZES, BADGE_COUNT_MAX, extractInitials, getAvatarBadgeProps, getAvatarFallbackProps, getAvatarImageElementProps, getAvatarImageProps, getAvatarRootProps, resolveAvatarState } from "./avatar/index.js";
|
|
12
12
|
import { connect as connect$1, createTextAreaProps, machine } from "./textarea/index.js";
|
|
13
|
-
|
|
13
|
+
import { TAG_PARTS, TAG_SIZES, TAG_VARIANTS } from "./tag/index.js";
|
|
14
|
+
import { createInputProps } from "./input/index.js";
|
|
15
|
+
export { AVATAR_BADGE_TYPES, AVATAR_LOAD_STATUS, AVATAR_PARTS, AVATAR_SHAPES, AVATAR_SIZES, BADGE_COUNT_MAX, BUTTON_ICON_POSITIONS, BUTTON_PARTS, BUTTON_SHAPES, BUTTON_SIZES, BUTTON_TONES, BUTTON_VARIANTS, DEFAULT_TOAST_DURATION, DEFAULT_TOAST_POSITION, DIVIDER_ORIENTATIONS, DIVIDER_PARTS, DIVIDER_WEIGHTS, EMPTY_PARTS, ICON_FALLBACK_NAME, ICON_PARTS, LOADING_SIZES, SKELETON_ANIMATIONS, SKELETON_PARTS, SKELETON_RADII, SKELETON_VARIANTS, TAG_PARTS, TAG_SIZES, TAG_VARIANTS, TOAST_ICON_TYPES, TOAST_PARTS, TOAST_POSITIONS, TOPBAR_VARIANTS, connect, createDialogProps, createInputProps, createTabsProps, createTextAreaProps, createTopBarProps, dialogMachine, extractInitials, getAvatarBadgeProps, getAvatarFallbackProps, getAvatarImageElementProps, getAvatarImageProps, getAvatarRootProps, getButtonRootProps, isToastIconType, resolveAvatarState, resolveButtonState, resolveIconName, resolveToastDuration, tabsMachine, connect$1 as textAreaConnect, machine as textAreaMachine };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./input.js";
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { createMachine } from "@zag-js/core";
|
|
2
|
+
import { compact } from "@zag-js/utils";
|
|
3
|
+
//#region src/input/input.ts
|
|
4
|
+
/**
|
|
5
|
+
* Input zag.js machine。
|
|
6
|
+
* 唯一状态来源:value(受控/非受控由 bindable 收敛)。
|
|
7
|
+
* readOnly / overflow / maxLines 为纯派生,不进入状态。
|
|
8
|
+
*/
|
|
9
|
+
const machine = createMachine({
|
|
10
|
+
props({ props }) {
|
|
11
|
+
return {
|
|
12
|
+
defaultValue: "",
|
|
13
|
+
readOnly: false,
|
|
14
|
+
readOnlyOverflow: "wrap",
|
|
15
|
+
...props
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
initialState() {
|
|
19
|
+
return "idle";
|
|
20
|
+
},
|
|
21
|
+
context({ bindable, prop }) {
|
|
22
|
+
return { value: bindable(() => {
|
|
23
|
+
const controlledValue = prop("value");
|
|
24
|
+
return {
|
|
25
|
+
defaultValue: prop("defaultValue") ?? "",
|
|
26
|
+
sync: true,
|
|
27
|
+
...controlledValue !== void 0 ? { value: controlledValue } : {},
|
|
28
|
+
onChange(value, prev) {
|
|
29
|
+
if (value !== prev) prop("onChange")?.(value);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}) };
|
|
33
|
+
},
|
|
34
|
+
computed: {
|
|
35
|
+
mode({ prop }) {
|
|
36
|
+
return prop("readOnly") ? "readonly" : "input";
|
|
37
|
+
},
|
|
38
|
+
overflow({ prop }) {
|
|
39
|
+
if (!prop("readOnly")) return void 0;
|
|
40
|
+
return prop("readOnlyOverflow") ?? "wrap";
|
|
41
|
+
},
|
|
42
|
+
clampLines({ prop, computed }) {
|
|
43
|
+
if (computed("mode") !== "readonly") return void 0;
|
|
44
|
+
if (computed("overflow") !== "wrap") return void 0;
|
|
45
|
+
const max = prop("maxLines");
|
|
46
|
+
return typeof max === "number" && max > 0 ? Math.floor(max) : void 0;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
watch({ track, prop, context }) {
|
|
50
|
+
track([() => prop("value")], () => {
|
|
51
|
+
const external = prop("value");
|
|
52
|
+
if (external !== void 0 && external !== context.get("value")) context.set("value", external);
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
states: { idle: { on: { "VALUE.SET": { actions: ["setValue"] } } } },
|
|
56
|
+
implementations: { actions: { setValue({ context, event }) {
|
|
57
|
+
if (event.type === "VALUE.SET") context.set("value", event.value);
|
|
58
|
+
} } }
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* connect:把 service 接成业务友好的 prop-getter。
|
|
62
|
+
* 适配层只需调用 connect(service, normalizeProps),按 api.mode 选择渲染子树。
|
|
63
|
+
*/
|
|
64
|
+
function connect(service, normalizeProps) {
|
|
65
|
+
const { context, computed, prop } = service;
|
|
66
|
+
const value = context.get("value");
|
|
67
|
+
const mode = computed("mode");
|
|
68
|
+
const overflow = computed("overflow");
|
|
69
|
+
const clampLines = computed("clampLines");
|
|
70
|
+
return {
|
|
71
|
+
value,
|
|
72
|
+
mode,
|
|
73
|
+
overflow,
|
|
74
|
+
getRootProps() {
|
|
75
|
+
return normalizeProps.element(compact({
|
|
76
|
+
"data-scope": "input",
|
|
77
|
+
"data-part": "root",
|
|
78
|
+
"data-mode": mode
|
|
79
|
+
}));
|
|
80
|
+
},
|
|
81
|
+
getTitleProps() {
|
|
82
|
+
return normalizeProps.element(compact({
|
|
83
|
+
"data-scope": "input",
|
|
84
|
+
"data-part": "title"
|
|
85
|
+
}));
|
|
86
|
+
},
|
|
87
|
+
getFieldProps() {
|
|
88
|
+
return normalizeProps.element(compact({
|
|
89
|
+
"data-scope": "input",
|
|
90
|
+
"data-part": "field"
|
|
91
|
+
}));
|
|
92
|
+
},
|
|
93
|
+
getInputProps() {
|
|
94
|
+
return normalizeProps.input(compact({
|
|
95
|
+
"data-scope": "input",
|
|
96
|
+
"data-part": "input",
|
|
97
|
+
type: "text",
|
|
98
|
+
value,
|
|
99
|
+
placeholder: prop("placeholder"),
|
|
100
|
+
onChange(event) {
|
|
101
|
+
const target = event.target;
|
|
102
|
+
context.set("value", target.value);
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
},
|
|
106
|
+
getIconProps() {
|
|
107
|
+
return normalizeProps.element(compact({
|
|
108
|
+
"data-scope": "input",
|
|
109
|
+
"data-part": "icon",
|
|
110
|
+
onClick() {
|
|
111
|
+
prop("onIconClick")?.();
|
|
112
|
+
}
|
|
113
|
+
}));
|
|
114
|
+
},
|
|
115
|
+
getReadonlyProps() {
|
|
116
|
+
return normalizeProps.element(compact({
|
|
117
|
+
"data-scope": "input",
|
|
118
|
+
"data-part": "readonly",
|
|
119
|
+
"data-overflow": overflow,
|
|
120
|
+
style: clampLines ? { ["--qw-input-max-lines"]: String(clampLines) } : void 0
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 把 QwInputProps 转成 machine props(默认值收口)。
|
|
127
|
+
*/
|
|
128
|
+
function createInputProps(props = {}) {
|
|
129
|
+
return {
|
|
130
|
+
defaultValue: props.defaultValue ?? "",
|
|
131
|
+
readOnly: props.readOnly ?? false,
|
|
132
|
+
readOnlyOverflow: props.readOnlyOverflow ?? "wrap",
|
|
133
|
+
...props
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { connect, createInputProps, machine };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/input/input.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 * 只读模式溢出策略。\n * - `wrap`:换行展示(默认),可配 `maxLines` 限制最大行数。\n * - `ellipsis`:单行超出省略。\n */\nexport type InputReadOnlyOverflow = \"wrap\" | \"ellipsis\";\n\n/**\n * 渲染模式(由 readOnly 派生)。\n */\nexport type InputMode = \"input\" | \"readonly\";\n\n/**\n * Input 公共 props 契约(双栈共享,不含框架特有节点类型)。\n */\nexport interface QwInputProps {\n /** 唯一 id 前缀。 */\n id?: string;\n /** true=只读模式,false=输入模式。默认 false。 */\n readOnly?: boolean;\n /** 受控值。 */\n value?: string;\n /** 非受控初始值。默认 \"\"。 */\n defaultValue?: string;\n /** 输入变化回调(仅输入模式触发)。 */\n onChange?: (value: string) => void;\n /** 输入模式提示文本(右对齐)。 */\n placeholder?: string;\n /** 左侧标题文本(宽度自适应)。 */\n title?: string;\n /** icon 点击回调(仅输入模式)。 */\n onIconClick?: () => void;\n /** 只读模式溢出策略。默认 \"wrap\"。 */\n readOnlyOverflow?: InputReadOnlyOverflow;\n /** 只读换行最大行数(仅 wrap 生效;缺省不限行数)。 */\n maxLines?: number;\n}\n\n/**\n * 内部 schema,用于 createMachine 类型推导。\n */\ninterface InputSchema extends MachineSchema {\n props: QwInputProps;\n context: {\n value: string;\n };\n computed: {\n mode: InputMode;\n overflow: InputReadOnlyOverflow | undefined;\n clampLines: number | undefined;\n };\n state: \"idle\";\n event: { type: \"VALUE.SET\"; value: string };\n action: \"setValue\";\n}\n\n/**\n * Input zag.js machine。\n * 唯一状态来源:value(受控/非受控由 bindable 收敛)。\n * readOnly / overflow / maxLines 为纯派生,不进入状态。\n */\nexport const machine = createMachine<InputSchema>({\n props({ props }) {\n return {\n defaultValue: \"\",\n readOnly: false,\n readOnlyOverflow: \"wrap\",\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 };\n },\n\n computed: {\n mode({ prop }) {\n return prop(\"readOnly\") ? \"readonly\" : \"input\";\n },\n overflow({ prop }) {\n if (!prop(\"readOnly\")) return undefined;\n return prop(\"readOnlyOverflow\") ?? \"wrap\";\n },\n clampLines({ prop, computed }) {\n if (computed(\"mode\") !== \"readonly\") return undefined;\n if (computed(\"overflow\") !== \"wrap\") return undefined;\n const max = prop(\"maxLines\");\n return typeof max === \"number\" && max > 0 ? Math.floor(max) : undefined;\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 },\n },\n },\n\n implementations: {\n actions: {\n setValue({ context, event }) {\n if (event.type === \"VALUE.SET\") {\n context.set(\"value\", event.value);\n }\n },\n },\n },\n});\n\n/**\n * Input api 类型(由 connect 返回)。\n */\nexport interface InputApi<T extends PropTypes = PropTypes> {\n /** 当前值。 */\n value: string;\n /** 渲染模式:input | readonly。 */\n mode: InputMode;\n /** 只读溢出策略(输入模式为 undefined)。 */\n overflow: InputReadOnlyOverflow | undefined;\n /** 根容器 prop-getter。 */\n getRootProps(): T[\"element\"];\n /** 左侧标题 prop-getter。 */\n getTitleProps(): T[\"element\"];\n /** 输入区容器 prop-getter(输入模式)。 */\n getFieldProps(): T[\"element\"];\n /** 输入框 prop-getter(输入模式)。 */\n getInputProps(): T[\"input\"];\n /** 右侧图标容器 prop-getter(输入模式,承载点击)。 */\n getIconProps(): T[\"element\"];\n /** 只读展示容器 prop-getter(只读模式)。 */\n getReadonlyProps(): T[\"element\"];\n}\n\n/**\n * connect:把 service 接成业务友好的 prop-getter。\n * 适配层只需调用 connect(service, normalizeProps),按 api.mode 选择渲染子树。\n */\nexport function connect<T extends PropTypes>(service: Service<InputSchema>, normalizeProps: NormalizeProps<T>): InputApi<T> {\n const { context, computed, prop } = service;\n\n const value = context.get(\"value\");\n const mode = computed(\"mode\");\n const overflow = computed(\"overflow\");\n const clampLines = computed(\"clampLines\");\n\n return {\n value,\n mode,\n overflow,\n\n getRootProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"input\",\n \"data-part\": \"root\",\n \"data-mode\": mode,\n }),\n );\n },\n\n getTitleProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"input\",\n \"data-part\": \"title\",\n }),\n );\n },\n\n getFieldProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"input\",\n \"data-part\": \"field\",\n }),\n );\n },\n\n getInputProps() {\n return normalizeProps.input(\n compact({\n \"data-scope\": \"input\",\n \"data-part\": \"input\",\n type: \"text\" as const,\n value,\n placeholder: prop(\"placeholder\"),\n onChange(event: JSX.ChangeEvent<HTMLInputElement>) {\n const target = event.target as HTMLInputElement;\n // 直接同步更新,绕过 send() 的 queueMicrotask 延迟\n context.set(\"value\", target.value);\n },\n }),\n );\n },\n\n getIconProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"input\",\n \"data-part\": \"icon\",\n onClick() {\n prop(\"onIconClick\")?.();\n },\n }),\n );\n },\n\n getReadonlyProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"input\",\n \"data-part\": \"readonly\",\n \"data-overflow\": overflow,\n style: clampLines ? ({ [\"--qw-input-max-lines\"]: String(clampLines) } as Record<string, string>) : undefined,\n }),\n );\n },\n };\n}\n\n/**\n * 把 QwInputProps 转成 machine props(默认值收口)。\n */\nexport function createInputProps(props: QwInputProps = {}): Partial<QwInputProps> {\n return {\n defaultValue: props.defaultValue ?? \"\",\n readOnly: props.readOnly ?? false,\n readOnlyOverflow: props.readOnlyOverflow ?? \"wrap\",\n ...props,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { MachineSchema, Service } from "@zag-js/core";
|
|
2
|
+
import type { PropTypes, NormalizeProps } from "@zag-js/types";
|
|
3
|
+
/**
|
|
4
|
+
* 只读模式溢出策略。
|
|
5
|
+
* - `wrap`:换行展示(默认),可配 `maxLines` 限制最大行数。
|
|
6
|
+
* - `ellipsis`:单行超出省略。
|
|
7
|
+
*/
|
|
8
|
+
export type InputReadOnlyOverflow = "wrap" | "ellipsis";
|
|
9
|
+
/**
|
|
10
|
+
* 渲染模式(由 readOnly 派生)。
|
|
11
|
+
*/
|
|
12
|
+
export type InputMode = "input" | "readonly";
|
|
13
|
+
/**
|
|
14
|
+
* Input 公共 props 契约(双栈共享,不含框架特有节点类型)。
|
|
15
|
+
*/
|
|
16
|
+
export interface QwInputProps {
|
|
17
|
+
/** 唯一 id 前缀。 */
|
|
18
|
+
id?: string;
|
|
19
|
+
/** true=只读模式,false=输入模式。默认 false。 */
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
/** 受控值。 */
|
|
22
|
+
value?: string;
|
|
23
|
+
/** 非受控初始值。默认 ""。 */
|
|
24
|
+
defaultValue?: string;
|
|
25
|
+
/** 输入变化回调(仅输入模式触发)。 */
|
|
26
|
+
onChange?: (value: string) => void;
|
|
27
|
+
/** 输入模式提示文本(右对齐)。 */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** 左侧标题文本(宽度自适应)。 */
|
|
30
|
+
title?: string;
|
|
31
|
+
/** icon 点击回调(仅输入模式)。 */
|
|
32
|
+
onIconClick?: () => void;
|
|
33
|
+
/** 只读模式溢出策略。默认 "wrap"。 */
|
|
34
|
+
readOnlyOverflow?: InputReadOnlyOverflow;
|
|
35
|
+
/** 只读换行最大行数(仅 wrap 生效;缺省不限行数)。 */
|
|
36
|
+
maxLines?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 内部 schema,用于 createMachine 类型推导。
|
|
40
|
+
*/
|
|
41
|
+
interface InputSchema extends MachineSchema {
|
|
42
|
+
props: QwInputProps;
|
|
43
|
+
context: {
|
|
44
|
+
value: string;
|
|
45
|
+
};
|
|
46
|
+
computed: {
|
|
47
|
+
mode: InputMode;
|
|
48
|
+
overflow: InputReadOnlyOverflow | undefined;
|
|
49
|
+
clampLines: number | undefined;
|
|
50
|
+
};
|
|
51
|
+
state: "idle";
|
|
52
|
+
event: {
|
|
53
|
+
type: "VALUE.SET";
|
|
54
|
+
value: string;
|
|
55
|
+
};
|
|
56
|
+
action: "setValue";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Input zag.js machine。
|
|
60
|
+
* 唯一状态来源:value(受控/非受控由 bindable 收敛)。
|
|
61
|
+
* readOnly / overflow / maxLines 为纯派生,不进入状态。
|
|
62
|
+
*/
|
|
63
|
+
export declare const machine: import("@zag-js/core").Machine<InputSchema>;
|
|
64
|
+
/**
|
|
65
|
+
* Input api 类型(由 connect 返回)。
|
|
66
|
+
*/
|
|
67
|
+
export interface InputApi<T extends PropTypes = PropTypes> {
|
|
68
|
+
/** 当前值。 */
|
|
69
|
+
value: string;
|
|
70
|
+
/** 渲染模式:input | readonly。 */
|
|
71
|
+
mode: InputMode;
|
|
72
|
+
/** 只读溢出策略(输入模式为 undefined)。 */
|
|
73
|
+
overflow: InputReadOnlyOverflow | undefined;
|
|
74
|
+
/** 根容器 prop-getter。 */
|
|
75
|
+
getRootProps(): T["element"];
|
|
76
|
+
/** 左侧标题 prop-getter。 */
|
|
77
|
+
getTitleProps(): T["element"];
|
|
78
|
+
/** 输入区容器 prop-getter(输入模式)。 */
|
|
79
|
+
getFieldProps(): T["element"];
|
|
80
|
+
/** 输入框 prop-getter(输入模式)。 */
|
|
81
|
+
getInputProps(): T["input"];
|
|
82
|
+
/** 右侧图标容器 prop-getter(输入模式,承载点击)。 */
|
|
83
|
+
getIconProps(): T["element"];
|
|
84
|
+
/** 只读展示容器 prop-getter(只读模式)。 */
|
|
85
|
+
getReadonlyProps(): T["element"];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* connect:把 service 接成业务友好的 prop-getter。
|
|
89
|
+
* 适配层只需调用 connect(service, normalizeProps),按 api.mode 选择渲染子树。
|
|
90
|
+
*/
|
|
91
|
+
export declare function connect<T extends PropTypes>(service: Service<InputSchema>, normalizeProps: NormalizeProps<T>): InputApi<T>;
|
|
92
|
+
/**
|
|
93
|
+
* 把 QwInputProps 转成 machine props(默认值收口)。
|
|
94
|
+
*/
|
|
95
|
+
export declare function createInputProps(props?: QwInputProps): Partial<QwInputProps>;
|
|
96
|
+
export {};
|
package/dist/search-bar/index.js
CHANGED
|
@@ -31,6 +31,7 @@ const machine = createMachine({
|
|
|
31
31
|
const controlledValue = prop("value");
|
|
32
32
|
return {
|
|
33
33
|
defaultValue: prop("defaultValue") ?? "",
|
|
34
|
+
sync: true,
|
|
34
35
|
...controlledValue !== void 0 ? { value: controlledValue } : {},
|
|
35
36
|
onChange(value, prev) {
|
|
36
37
|
if (value !== prev) prop("onChange")?.(value);
|
|
@@ -52,10 +53,10 @@ const machine = createMachine({
|
|
|
52
53
|
return context.get("value").length > 0;
|
|
53
54
|
}
|
|
54
55
|
},
|
|
55
|
-
watch({ track,
|
|
56
|
+
watch({ track, prop, context }) {
|
|
56
57
|
track([() => prop("value")], () => {
|
|
57
58
|
const external = prop("value");
|
|
58
|
-
if (external !== void 0 && external !== context.get("value"))
|
|
59
|
+
if (external !== void 0 && external !== context.get("value")) context.set("value", external);
|
|
59
60
|
});
|
|
60
61
|
},
|
|
61
62
|
states: { idle: { on: {
|
|
@@ -167,11 +168,14 @@ function connect(service, normalizeProps) {
|
|
|
167
168
|
});
|
|
168
169
|
},
|
|
169
170
|
onChange(event) {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
const target = event.target;
|
|
172
|
+
const isComposing = !!event.nativeEvent?.isComposing;
|
|
173
|
+
let next = target.value;
|
|
174
|
+
if (!isComposing) {
|
|
175
|
+
const maxLen = prop("maxLength") ?? 0;
|
|
176
|
+
if (maxLen > 0 && next.length > maxLen) next = next.slice(0, maxLen);
|
|
177
|
+
}
|
|
178
|
+
context.set("value", next);
|
|
175
179
|
},
|
|
176
180
|
onKeyDown(event) {
|
|
177
181
|
if (event.key === "Enter") send({ type: "SEARCH" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/search-bar/search-bar.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 * 清除按钮显示策略。\n */\nexport type SearchBarClearable = boolean | { showWhen?: \"always\" | \"focus\" | \"non-empty\" };\n\n/**\n * 内部归一化后的显示策略(包含 `never`,表示 `clearable={false}`)。\n */\nexport type SearchBarClearableMode = \"always\" | \"focus\" | \"non-empty\" | \"never\";\n\nexport interface SearchBarClearableConfig {\n showWhen: SearchBarClearableMode;\n}\n\n/**\n * 右侧 action 按钮角色。\n */\nexport type SearchBarActionType = \"cancel\" | \"search\";\n\n/**\n * 右侧 action 按钮配置。\n */\nexport interface SearchBarAction {\n /** 按钮角色。 */\n type: SearchBarActionType;\n /** 展示内容:文案或自定义节点。 */\n content: string | unknown;\n /** 取消时是否清空输入。 */\n clearOnCancel?: boolean;\n /** 按钮可访问名称;当 content 为纯图标时必填。 */\n ariaLabel?: string;\n}\n\n/**\n * SearchBar 公共 props 契约(双栈共享)。\n */\nexport interface QwSearchBarProps {\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 clearable?: SearchBarClearable;\n /** 左侧搜索图标:字符串文案或自定义节点。 */\n searchIcon?: string | unknown;\n /** 左侧按钮配置。 */\n prefix?: SearchBarAction;\n /** 右侧按钮配置。 */\n action?: SearchBarAction;\n /** 输入变化回调。 */\n onChange?: (value: string) => void;\n /** 搜索确认回调(Enter 或点击搜索按钮)。 */\n onSearch?: (value: string) => void;\n /** 清空回调。 */\n onClear?: () => void;\n /** 取消回调。 */\n onCancel?: () => void;\n /** 聚焦回调。 */\n onFocus?: () => void;\n /** 失焦回调。 */\n onBlur?: () => void;\n}\n\n/**\n * 内部 schema,用于 createMachine 类型推导。\n */\ninterface SearchBarSchema extends MachineSchema {\n props: QwSearchBarProps;\n context: {\n value: string;\n focused: boolean;\n };\n computed: {\n showClear: boolean;\n clearableConfig: SearchBarClearableConfig;\n };\n state: \"idle\";\n event:\n | { type: \"VALUE.SET\"; value: string; src?: string }\n | { type: \"VALUE.CLEAR\" }\n | { type: \"FOCUS.SET\"; focused: boolean }\n | { type: \"SEARCH\" }\n | { type: \"CANCEL\"; src?: \"prefix\" | \"action\" };\n action:\n | \"setValue\"\n | \"clearValue\"\n | \"setFocused\"\n | \"invokeOnSearch\"\n | \"invokeOnClear\"\n | \"invokeOnCancel\"\n | \"invokeOnFocus\"\n | \"invokeOnBlur\";\n}\n\n/**\n * 解析 clearable 配置为统一对象。\n */\nfunction normalizeClearable(clearable?: SearchBarClearable): SearchBarClearableConfig {\n if (clearable === false) return { showWhen: \"never\" };\n if (clearable === true || clearable == null) return { showWhen: \"non-empty\" };\n return { showWhen: clearable.showWhen ?? \"non-empty\" };\n}\n\n/**\n * SearchBar zag.js machine。\n * 所有交互逻辑唯一来源:value、focus、clear、action、keyboard。\n */\nexport const machine = createMachine<SearchBarSchema>({\n props({ props }) {\n return {\n defaultValue: \"\",\n disabled: false,\n readOnly: false,\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 ...(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 clearableConfig({ prop }) {\n return normalizeClearable(prop(\"clearable\"));\n },\n showClear({ context, computed }) {\n const cfg = computed(\"clearableConfig\");\n if (cfg.showWhen === \"always\") return true;\n if (cfg.showWhen === \"never\") return false;\n if (cfg.showWhen === \"focus\") return context.get(\"focused\");\n return context.get(\"value\").length > 0;\n },\n },\n\n watch({ track, action, prop, context }) {\n track([() => prop(\"value\")], () => {\n const external = prop(\"value\");\n if (external !== undefined && external !== context.get(\"value\")) {\n action([\"setValue\"]);\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 SEARCH: {\n actions: [\"invokeOnSearch\"],\n },\n CANCEL: {\n actions: [\"invokeOnCancel\"],\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 const current = context.get(\"value\");\n let next = event.value;\n if (maxLength > 0 && next.length > maxLength) {\n // 达到 maxLength 后阻止继续输入,保留旧值\n next = current;\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 invokeOnSearch({ context, prop }) {\n prop(\"onSearch\")?.(context.get(\"value\"));\n },\n invokeOnClear({ prop }) {\n prop(\"onClear\")?.();\n },\n invokeOnCancel({ context, prop, event }) {\n const config = event.src === \"prefix\" ? prop(\"prefix\") : prop(\"action\");\n if (config?.type === \"cancel\" && config.clearOnCancel && !prop(\"readOnly\")) {\n context.set(\"value\", \"\");\n }\n prop(\"onCancel\")?.();\n },\n invokeOnFocus() {\n // focus 回调在 setFocused 中已触发\n },\n invokeOnBlur() {\n // blur 回调在 setFocused 中已触发\n },\n },\n },\n});\n\n/**\n * SearchBar api 类型(由 connect 返回)。\n */\nexport interface SearchBarApi<T extends PropTypes = PropTypes> {\n /** 当前输入值。 */\n value: string;\n /** 当前是否聚焦。 */\n focused: boolean;\n /** 是否显示清除按钮。 */\n showClear: boolean;\n /** 清空输入。 */\n clear(): void;\n /** 触发搜索。 */\n search(): void;\n /** 根容器 prop-getter。 */\n getRootProps(): T[\"element\"];\n /** 输入区容器 prop-getter(包含图标、输入框、清除按钮)。 */\n getFieldProps(): T[\"element\"];\n /** 输入框 prop-getter。 */\n getInputProps(): T[\"input\"];\n /** 左侧图标 prop-getter。 */\n getIconProps(): T[\"element\"];\n /** 清除按钮 prop-getter。 */\n getClearButtonProps(): T[\"button\"];\n /** 左侧 prefix 按钮 prop-getter。 */\n getPrefixButtonProps(): T[\"button\"];\n /** 右侧 action 按钮 prop-getter。 */\n getActionButtonProps(): T[\"button\"];\n}\n\n/**\n * connect:把 service 接成业务友好的 prop-getter。\n * 适配层只需调用 connect(service, normalizeProps)。\n */\nexport function connect<T extends PropTypes>(service: Service<SearchBarSchema>, normalizeProps: NormalizeProps<T>): SearchBarApi<T> {\n const { context, computed, prop, send } = service;\n\n const value = context.get(\"value\");\n const focused = context.get(\"focused\");\n const showClear = computed(\"showClear\");\n const disabled = !!prop(\"disabled\");\n const readOnly = !!prop(\"readOnly\");\n const maxLength = prop(\"maxLength\");\n const prefix = prop(\"prefix\");\n const action = prop(\"action\");\n\n return {\n value,\n focused,\n showClear,\n\n clear() {\n send({ type: \"VALUE.CLEAR\" });\n },\n\n search() {\n send({ type: \"SEARCH\" });\n },\n\n getRootProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"root\",\n \"data-focused\": focused ? \"true\" : undefined,\n \"data-disabled\": disabled ? \"true\" : undefined,\n \"data-readonly\": readOnly ? \"true\" : undefined,\n }),\n );\n },\n\n getFieldProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"field\",\n }),\n );\n },\n\n getInputProps() {\n return normalizeProps.input(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"input\",\n type: \"search\" as const,\n role: \"searchbox\" as const,\n value,\n placeholder: prop(\"placeholder\"),\n disabled,\n readOnly,\n maxLength: maxLength && maxLength > 0 ? maxLength : undefined,\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<HTMLInputElement>) {\n const target = event.target as HTMLInputElement;\n const next = target.value;\n send({ type: \"VALUE.SET\", value: next });\n },\n onKeyDown(event: JSX.KeyboardEvent<HTMLInputElement>) {\n if (event.key === \"Enter\") {\n send({ type: \"SEARCH\" });\n }\n },\n }),\n );\n },\n\n getIconProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"icon\",\n \"aria-hidden\": \"true\" as const,\n }),\n );\n },\n\n getClearButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"clear-button\",\n type: \"button\" as const,\n disabled: disabled || readOnly,\n \"aria-label\": \"清除搜索内容\" as const,\n \"aria-hidden\": !showClear,\n hidden: !showClear,\n onClick() {\n send({ type: \"VALUE.CLEAR\" });\n },\n }),\n );\n },\n\n getPrefixButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"prefix-button\",\n type: \"button\" as const,\n disabled,\n \"aria-label\": prefix?.ariaLabel,\n \"data-action\": prefix?.type,\n onClick() {\n if (prefix?.type === \"cancel\") {\n send({ type: \"CANCEL\", src: \"prefix\" });\n } else if (prefix?.type === \"search\") {\n send({ type: \"SEARCH\" });\n }\n },\n }),\n );\n },\n\n getActionButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"action-button\",\n type: \"button\" as const,\n disabled,\n \"aria-label\": action?.ariaLabel,\n \"data-action\": action?.type,\n onClick() {\n if (action?.type === \"cancel\") {\n send({ type: \"CANCEL\", src: \"action\" });\n } else if (action?.type === \"search\") {\n send({ type: \"SEARCH\" });\n }\n },\n }),\n );\n },\n };\n}\n\n/**\n * 把 QwSearchBarProps 转成 machine context(默认值收口)。\n */\nexport function createSearchBarProps(props: QwSearchBarProps = {}): Partial<QwSearchBarProps> {\n return {\n defaultValue: props.defaultValue ?? \"\",\n disabled: props.disabled ?? false,\n readOnly: props.readOnly ?? false,\n ...props,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/search-bar/search-bar.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 * 清除按钮显示策略。\n */\nexport type SearchBarClearable = boolean | { showWhen?: \"always\" | \"focus\" | \"non-empty\" };\n\n/**\n * 内部归一化后的显示策略(包含 `never`,表示 `clearable={false}`)。\n */\nexport type SearchBarClearableMode = \"always\" | \"focus\" | \"non-empty\" | \"never\";\n\nexport interface SearchBarClearableConfig {\n showWhen: SearchBarClearableMode;\n}\n\n/**\n * 右侧 action 按钮角色。\n */\nexport type SearchBarActionType = \"cancel\" | \"search\";\n\n/**\n * 右侧 action 按钮配置。\n */\nexport interface SearchBarAction {\n /** 按钮角色。 */\n type: SearchBarActionType;\n /** 展示内容:文案或自定义节点。 */\n content: string | unknown;\n /** 取消时是否清空输入。 */\n clearOnCancel?: boolean;\n /** 按钮可访问名称;当 content 为纯图标时必填。 */\n ariaLabel?: string;\n}\n\n/**\n * SearchBar 公共 props 契约(双栈共享)。\n */\nexport interface QwSearchBarProps {\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 clearable?: SearchBarClearable;\n /** 左侧搜索图标:字符串文案或自定义节点。 */\n searchIcon?: string | unknown;\n /** 左侧按钮配置。 */\n prefix?: SearchBarAction;\n /** 右侧按钮配置。 */\n action?: SearchBarAction;\n /** 输入变化回调。 */\n onChange?: (value: string) => void;\n /** 搜索确认回调(Enter 或点击搜索按钮)。 */\n onSearch?: (value: string) => void;\n /** 清空回调。 */\n onClear?: () => void;\n /** 取消回调。 */\n onCancel?: () => void;\n /** 聚焦回调。 */\n onFocus?: () => void;\n /** 失焦回调。 */\n onBlur?: () => void;\n}\n\n/**\n * 内部 schema,用于 createMachine 类型推导。\n */\ninterface SearchBarSchema extends MachineSchema {\n props: QwSearchBarProps;\n context: {\n value: string;\n focused: boolean;\n };\n computed: {\n showClear: boolean;\n clearableConfig: SearchBarClearableConfig;\n };\n state: \"idle\";\n event:\n | { type: \"VALUE.SET\"; value: string; src?: string }\n | { type: \"VALUE.CLEAR\" }\n | { type: \"FOCUS.SET\"; focused: boolean }\n | { type: \"SEARCH\" }\n | { type: \"CANCEL\"; src?: \"prefix\" | \"action\" };\n action:\n | \"setValue\"\n | \"clearValue\"\n | \"setFocused\"\n | \"invokeOnSearch\"\n | \"invokeOnClear\"\n | \"invokeOnCancel\"\n | \"invokeOnFocus\"\n | \"invokeOnBlur\";\n}\n\n/**\n * 解析 clearable 配置为统一对象。\n */\nfunction normalizeClearable(clearable?: SearchBarClearable): SearchBarClearableConfig {\n if (clearable === false) return { showWhen: \"never\" };\n if (clearable === true || clearable == null) return { showWhen: \"non-empty\" };\n return { showWhen: clearable.showWhen ?? \"non-empty\" };\n}\n\n/**\n * SearchBar zag.js machine。\n * 所有交互逻辑唯一来源:value、focus、clear、action、keyboard。\n */\nexport const machine = createMachine<SearchBarSchema>({\n props({ props }) {\n return {\n defaultValue: \"\",\n disabled: false,\n readOnly: false,\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 clearableConfig({ prop }) {\n return normalizeClearable(prop(\"clearable\"));\n },\n showClear({ context, computed }) {\n const cfg = computed(\"clearableConfig\");\n if (cfg.showWhen === \"always\") return true;\n if (cfg.showWhen === \"never\") return false;\n if (cfg.showWhen === \"focus\") return context.get(\"focused\");\n return context.get(\"value\").length > 0;\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 SEARCH: {\n actions: [\"invokeOnSearch\"],\n },\n CANCEL: {\n actions: [\"invokeOnCancel\"],\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 const current = context.get(\"value\");\n let next = event.value;\n if (maxLength > 0 && next.length > maxLength) {\n // 达到 maxLength 后阻止继续输入,保留旧值\n next = current;\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 invokeOnSearch({ context, prop }) {\n prop(\"onSearch\")?.(context.get(\"value\"));\n },\n invokeOnClear({ prop }) {\n prop(\"onClear\")?.();\n },\n invokeOnCancel({ context, prop, event }) {\n const config = event.src === \"prefix\" ? prop(\"prefix\") : prop(\"action\");\n if (config?.type === \"cancel\" && config.clearOnCancel && !prop(\"readOnly\")) {\n context.set(\"value\", \"\");\n }\n prop(\"onCancel\")?.();\n },\n invokeOnFocus() {\n // focus 回调在 setFocused 中已触发\n },\n invokeOnBlur() {\n // blur 回调在 setFocused 中已触发\n },\n },\n },\n});\n\n/**\n * SearchBar api 类型(由 connect 返回)。\n */\nexport interface SearchBarApi<T extends PropTypes = PropTypes> {\n /** 当前输入值。 */\n value: string;\n /** 当前是否聚焦。 */\n focused: boolean;\n /** 是否显示清除按钮。 */\n showClear: boolean;\n /** 清空输入。 */\n clear(): void;\n /** 触发搜索。 */\n search(): void;\n /** 根容器 prop-getter。 */\n getRootProps(): T[\"element\"];\n /** 输入区容器 prop-getter(包含图标、输入框、清除按钮)。 */\n getFieldProps(): T[\"element\"];\n /** 输入框 prop-getter。 */\n getInputProps(): T[\"input\"];\n /** 左侧图标 prop-getter。 */\n getIconProps(): T[\"element\"];\n /** 清除按钮 prop-getter。 */\n getClearButtonProps(): T[\"button\"];\n /** 左侧 prefix 按钮 prop-getter。 */\n getPrefixButtonProps(): T[\"button\"];\n /** 右侧 action 按钮 prop-getter。 */\n getActionButtonProps(): T[\"button\"];\n}\n\n/**\n * connect:把 service 接成业务友好的 prop-getter。\n * 适配层只需调用 connect(service, normalizeProps)。\n */\nexport function connect<T extends PropTypes>(service: Service<SearchBarSchema>, normalizeProps: NormalizeProps<T>): SearchBarApi<T> {\n const { context, computed, prop, send } = service;\n\n const value = context.get(\"value\");\n const focused = context.get(\"focused\");\n const showClear = computed(\"showClear\");\n const disabled = !!prop(\"disabled\");\n const readOnly = !!prop(\"readOnly\");\n const maxLength = prop(\"maxLength\");\n const prefix = prop(\"prefix\");\n const action = prop(\"action\");\n\n return {\n value,\n focused,\n showClear,\n\n clear() {\n send({ type: \"VALUE.CLEAR\" });\n },\n\n search() {\n send({ type: \"SEARCH\" });\n },\n\n getRootProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"root\",\n \"data-focused\": focused ? \"true\" : undefined,\n \"data-disabled\": disabled ? \"true\" : undefined,\n \"data-readonly\": readOnly ? \"true\" : undefined,\n }),\n );\n },\n\n getFieldProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"field\",\n }),\n );\n },\n\n getInputProps() {\n return normalizeProps.input(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"input\",\n type: \"search\" as const,\n role: \"searchbox\" as const,\n value,\n placeholder: prop(\"placeholder\"),\n disabled,\n readOnly,\n maxLength: maxLength && maxLength > 0 ? maxLength : undefined,\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<HTMLInputElement>) {\n const target = event.target as HTMLInputElement;\n const nativeEvent = (event as { nativeEvent?: { isComposing?: boolean } }).nativeEvent;\n const isComposing = !!nativeEvent?.isComposing;\n let next = target.value;\n // IME composing 期间跳过 maxLength 截断,允许拼音中间态值流过\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 onKeyDown(event: JSX.KeyboardEvent<HTMLInputElement>) {\n if (event.key === \"Enter\") {\n send({ type: \"SEARCH\" });\n }\n },\n }),\n );\n },\n\n getIconProps() {\n return normalizeProps.element(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"icon\",\n \"aria-hidden\": \"true\" as const,\n }),\n );\n },\n\n getClearButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"clear-button\",\n type: \"button\" as const,\n disabled: disabled || readOnly,\n \"aria-label\": \"清除搜索内容\" as const,\n \"aria-hidden\": !showClear,\n hidden: !showClear,\n onClick() {\n send({ type: \"VALUE.CLEAR\" });\n },\n }),\n );\n },\n\n getPrefixButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"prefix-button\",\n type: \"button\" as const,\n disabled,\n \"aria-label\": prefix?.ariaLabel,\n \"data-action\": prefix?.type,\n onClick() {\n if (prefix?.type === \"cancel\") {\n send({ type: \"CANCEL\", src: \"prefix\" });\n } else if (prefix?.type === \"search\") {\n send({ type: \"SEARCH\" });\n }\n },\n }),\n );\n },\n\n getActionButtonProps() {\n return normalizeProps.button(\n compact({\n \"data-scope\": \"search-bar\",\n \"data-part\": \"action-button\",\n type: \"button\" as const,\n disabled,\n \"aria-label\": action?.ariaLabel,\n \"data-action\": action?.type,\n onClick() {\n if (action?.type === \"cancel\") {\n send({ type: \"CANCEL\", src: \"action\" });\n } else if (action?.type === \"search\") {\n send({ type: \"SEARCH\" });\n }\n },\n }),\n );\n },\n };\n}\n\n/**\n * 把 QwSearchBarProps 转成 machine context(默认值收口)。\n */\nexport function createSearchBarProps(props: QwSearchBarProps = {}): Partial<QwSearchBarProps> {\n return {\n defaultValue: props.defaultValue ?? \"\",\n disabled: props.disabled ?? false,\n readOnly: props.readOnly ?? false,\n ...props,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./tag.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/tag/tag.ts
|
|
2
|
+
/**
|
|
3
|
+
* Tag 契约 — 原型 A(无状态),core 无 machine,只承载双栈共享的类型契约。
|
|
4
|
+
* 渲染契约:`data-scope="tag"`,`data-part="root"`;
|
|
5
|
+
* `data-size` / `data-variant` / `data-bold` 摊平枚举。
|
|
6
|
+
*
|
|
7
|
+
* 尺寸 / 变体 / 部位词表以运行时常量数组为唯一事实源(类型从常量推导),__test__ 直接锁死。
|
|
8
|
+
*/
|
|
9
|
+
/** 尺寸全集:small(小)/ medium(中,默认)/ large(大)。 */
|
|
10
|
+
const TAG_SIZES = [
|
|
11
|
+
"small",
|
|
12
|
+
"medium",
|
|
13
|
+
"large"
|
|
14
|
+
];
|
|
15
|
+
/** 变体全集:solid 填充(默认)/ outlined 框线。 */
|
|
16
|
+
const TAG_VARIANTS = ["solid", "outlined"];
|
|
17
|
+
/** Tag 各渲染部位全集(data-part 取值)。 */
|
|
18
|
+
const TAG_PARTS = ["root"];
|
|
19
|
+
//#endregion
|
|
20
|
+
export { TAG_PARTS, TAG_SIZES, TAG_VARIANTS };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/tag/tag.ts"],"sourcesContent":["/**\n * Tag 契约 — 原型 A(无状态),core 无 machine,只承载双栈共享的类型契约。\n * 渲染契约:`data-scope=\"tag\"`,`data-part=\"root\"`;\n * `data-size` / `data-variant` / `data-bold` 摊平枚举。\n *\n * 尺寸 / 变体 / 部位词表以运行时常量数组为唯一事实源(类型从常量推导),__test__ 直接锁死。\n */\n\n/** 尺寸全集:small(小)/ medium(中,默认)/ large(大)。 */\nexport const TAG_SIZES = [\"small\", \"medium\", \"large\"] as const;\n\n/** 变体全集:solid 填充(默认)/ outlined 框线。 */\nexport const TAG_VARIANTS = [\"solid\", \"outlined\"] as const;\n\n/** Tag 各渲染部位全集(data-part 取值)。 */\nexport const TAG_PARTS = [\"root\"] as const;\n\nexport type TagSize = (typeof TAG_SIZES)[number];\nexport type TagVariant = (typeof TAG_VARIANTS)[number];\nexport type TagPart = (typeof TAG_PARTS)[number];\n\n/**\n * QwTagProps — 双栈共享的行为契约(全字段可选)。\n * 颜色完全由业务方传入,组件不内置任何主题。\n */\nexport interface QwTagProps {\n /** 尺寸,默认 medium。 */\n size?: TagSize;\n /** 变体,默认 solid。 */\n variant?: TagVariant;\n /** 文字是否加粗,默认 false。 */\n bold?: boolean;\n /** 背景色,未传入时使用默认中性灰。 */\n bgcolor?: string;\n /** 文字色,未传入时使用默认灰。 */\n textcolor?: string;\n /** 边框色,仅在 variant=\"outlined\" 时生效;未传入时使用默认灰。 */\n borderColor?: string;\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,YAAY;CAAC;CAAS;CAAU;AAAO;;AAGpD,MAAa,eAAe,CAAC,SAAS,UAAU;;AAGhD,MAAa,YAAY,CAAC,MAAM"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag 契约 — 原型 A(无状态),core 无 machine,只承载双栈共享的类型契约。
|
|
3
|
+
* 渲染契约:`data-scope="tag"`,`data-part="root"`;
|
|
4
|
+
* `data-size` / `data-variant` / `data-bold` 摊平枚举。
|
|
5
|
+
*
|
|
6
|
+
* 尺寸 / 变体 / 部位词表以运行时常量数组为唯一事实源(类型从常量推导),__test__ 直接锁死。
|
|
7
|
+
*/
|
|
8
|
+
/** 尺寸全集:small(小)/ medium(中,默认)/ large(大)。 */
|
|
9
|
+
export declare const TAG_SIZES: readonly ["small", "medium", "large"];
|
|
10
|
+
/** 变体全集:solid 填充(默认)/ outlined 框线。 */
|
|
11
|
+
export declare const TAG_VARIANTS: readonly ["solid", "outlined"];
|
|
12
|
+
/** Tag 各渲染部位全集(data-part 取值)。 */
|
|
13
|
+
export declare const TAG_PARTS: readonly ["root"];
|
|
14
|
+
export type TagSize = (typeof TAG_SIZES)[number];
|
|
15
|
+
export type TagVariant = (typeof TAG_VARIANTS)[number];
|
|
16
|
+
export type TagPart = (typeof TAG_PARTS)[number];
|
|
17
|
+
/**
|
|
18
|
+
* QwTagProps — 双栈共享的行为契约(全字段可选)。
|
|
19
|
+
* 颜色完全由业务方传入,组件不内置任何主题。
|
|
20
|
+
*/
|
|
21
|
+
export interface QwTagProps {
|
|
22
|
+
/** 尺寸,默认 medium。 */
|
|
23
|
+
size?: TagSize;
|
|
24
|
+
/** 变体,默认 solid。 */
|
|
25
|
+
variant?: TagVariant;
|
|
26
|
+
/** 文字是否加粗,默认 false。 */
|
|
27
|
+
bold?: boolean;
|
|
28
|
+
/** 背景色,未传入时使用默认中性灰。 */
|
|
29
|
+
bgcolor?: string;
|
|
30
|
+
/** 文字色,未传入时使用默认灰。 */
|
|
31
|
+
textcolor?: string;
|
|
32
|
+
/** 边框色,仅在 variant="outlined" 时生效;未传入时使用默认灰。 */
|
|
33
|
+
borderColor?: string;
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qw-ui/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-next.0",
|
|
4
4
|
"description": "L2 · Headless Core — zag.js machine 薄包装,纯 TS / SSR-safe,零样式。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "qw-ui contributors",
|
|
@@ -83,6 +83,14 @@
|
|
|
83
83
|
"./textarea": {
|
|
84
84
|
"types": "./dist/textarea/index.d.ts",
|
|
85
85
|
"import": "./dist/textarea/index.js"
|
|
86
|
+
},
|
|
87
|
+
"./tag": {
|
|
88
|
+
"types": "./dist/tag/index.d.ts",
|
|
89
|
+
"import": "./dist/tag/index.js"
|
|
90
|
+
},
|
|
91
|
+
"./input": {
|
|
92
|
+
"types": "./dist/input/index.d.ts",
|
|
93
|
+
"import": "./dist/input/index.js"
|
|
86
94
|
}
|
|
87
95
|
},
|
|
88
96
|
"sideEffects": false,
|
|
@@ -91,7 +99,7 @@
|
|
|
91
99
|
"@zag-js/dialog": "^1.42.0",
|
|
92
100
|
"@zag-js/tabs": "^1.42.0",
|
|
93
101
|
"@zag-js/utils": "1.42.0",
|
|
94
|
-
"@qw-ui/icons": "^0.
|
|
102
|
+
"@qw-ui/icons": "^0.3.0-next.0"
|
|
95
103
|
},
|
|
96
104
|
"devDependencies": {
|
|
97
105
|
"@microsoft/api-extractor": "^7.58.9",
|