@mariotzz/tzz-element 1.0.0 → 2.1.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/es/Button/button.d.ts +10 -0
- package/es/Button/button.js +157 -0
- package/es/Button/button.module.css +164 -0
- package/es/Button/index.d.ts +2 -7
- package/es/Button/index.js +1 -20
- package/es/Button/types.d.ts +58 -0
- package/es/Button/types.js +1 -0
- package/es/Button/utils.d.ts +32 -0
- package/es/Button/utils.js +111 -0
- package/es/index.d.ts +1 -3
- package/es/index.js +1 -3
- package/es/typings.d.ts +4 -0
- package/lib/Button/button.d.ts +10 -0
- package/lib/Button/button.js +155 -0
- package/lib/Button/button.module.css +164 -0
- package/lib/Button/index.d.ts +2 -7
- package/lib/Button/index.js +3 -9
- package/lib/Button/types.d.ts +58 -0
- package/lib/{Foo/index.js → Button/types.js} +3 -13
- package/lib/Button/utils.d.ts +32 -0
- package/lib/Button/utils.js +113 -0
- package/lib/index.d.ts +1 -3
- package/lib/index.js +3 -19
- package/lib/typings.d.ts +4 -0
- package/package.json +1 -1
- package/es/Demo/index.d.ts +0 -5
- package/es/Demo/index.js +0 -8
- package/es/Foo/index.d.ts +0 -5
- package/es/Foo/index.js +0 -7
- package/lib/Demo/index.d.ts +0 -5
- package/lib/Demo/index.js +0 -27
- package/lib/Foo/index.d.ts +0 -5
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ButtonProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Button:生产级可复用按钮组件
|
|
5
|
+
* - variant/size/loading/disabled/icon/asChild
|
|
6
|
+
* - 默认 type="button" 避免表单误提交
|
|
7
|
+
* - loading/disabled 统一语义与交互拦截
|
|
8
|
+
* - focus-visible 清晰可见
|
|
9
|
+
*/
|
|
10
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
var _excluded = ["variant", "size", "block", "disabled", "loading", "loadingText", "startIcon", "endIcon", "spinnerPlacement", "asChild", "className", "children", "onClick", "onKeyDown", "type"];
|
|
3
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
4
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
5
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
7
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
8
|
+
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
9
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
10
|
+
import * as React from 'react';
|
|
11
|
+
import styles from "./button.module.css";
|
|
12
|
+
import { Slot, cn } from "./utils";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 判断 children 是否包含可读文本(用于 icon-only 的 a11y 提示)
|
|
16
|
+
*/
|
|
17
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
+
function hasReadableText(node) {
|
|
20
|
+
if (node === null || node === undefined || typeof node === 'boolean') return false;
|
|
21
|
+
if (typeof node === 'string' || typeof node === 'number') return String(node).trim().length > 0;
|
|
22
|
+
if (Array.isArray(node)) return node.some(hasReadableText);
|
|
23
|
+
if ( /*#__PURE__*/React.isValidElement(node)) return hasReadableText(node.props.children);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Button:生产级可复用按钮组件
|
|
29
|
+
* - variant/size/loading/disabled/icon/asChild
|
|
30
|
+
* - 默认 type="button" 避免表单误提交
|
|
31
|
+
* - loading/disabled 统一语义与交互拦截
|
|
32
|
+
* - focus-visible 清晰可见
|
|
33
|
+
*/
|
|
34
|
+
export var Button = /*#__PURE__*/React.forwardRef(function Button(props, ref) {
|
|
35
|
+
var _props$variant = props.variant,
|
|
36
|
+
variant = _props$variant === void 0 ? 'default' : _props$variant,
|
|
37
|
+
_props$size = props.size,
|
|
38
|
+
size = _props$size === void 0 ? 'md' : _props$size,
|
|
39
|
+
_props$block = props.block,
|
|
40
|
+
block = _props$block === void 0 ? false : _props$block,
|
|
41
|
+
_props$disabled = props.disabled,
|
|
42
|
+
disabled = _props$disabled === void 0 ? false : _props$disabled,
|
|
43
|
+
_props$loading = props.loading,
|
|
44
|
+
loading = _props$loading === void 0 ? false : _props$loading,
|
|
45
|
+
loadingText = props.loadingText,
|
|
46
|
+
startIcon = props.startIcon,
|
|
47
|
+
endIcon = props.endIcon,
|
|
48
|
+
_props$spinnerPlaceme = props.spinnerPlacement,
|
|
49
|
+
spinnerPlacement = _props$spinnerPlaceme === void 0 ? 'start' : _props$spinnerPlaceme,
|
|
50
|
+
_props$asChild = props.asChild,
|
|
51
|
+
asChild = _props$asChild === void 0 ? false : _props$asChild,
|
|
52
|
+
className = props.className,
|
|
53
|
+
children = props.children,
|
|
54
|
+
onClick = props.onClick,
|
|
55
|
+
onKeyDown = props.onKeyDown,
|
|
56
|
+
type = props.type,
|
|
57
|
+
rest = _objectWithoutProperties(props, _excluded);
|
|
58
|
+
var isDisabled = disabled || loading;
|
|
59
|
+
|
|
60
|
+
// icon-only 场景的可访问性提示(开发态)
|
|
61
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
62
|
+
if (size === 'icon') {
|
|
63
|
+
var hasText = hasReadableText(children);
|
|
64
|
+
var ariaLabel = props['aria-label'];
|
|
65
|
+
if (!hasText && (!ariaLabel || ariaLabel.trim().length === 0)) {
|
|
66
|
+
// 不强制 throw,避免影响 demo/运行,但会提示使用者补齐 a11y
|
|
67
|
+
// 你也可以改为 throw new Error(...) 来更严格
|
|
68
|
+
// eslint-disable-next-line no-console
|
|
69
|
+
console.warn('[tzz-element/Button] size="icon" 时建议提供 aria-label(或 children 包含可读文本),以满足无障碍要求。');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 统一事件拦截:disabled/loading 时阻止交互
|
|
76
|
+
* - 对于原生 button:disabled 属性即可阻止,但我们仍做防御性拦截(避免 asChild 情况差异)
|
|
77
|
+
* - 对于 asChild:必须拦截 click/keyboard
|
|
78
|
+
*/
|
|
79
|
+
var handleClick = function handleClick(e) {
|
|
80
|
+
if (isDisabled) {
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
e.stopPropagation();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
onClick === null || onClick === void 0 || onClick(e);
|
|
86
|
+
};
|
|
87
|
+
var handleKeyDown = function handleKeyDown(e) {
|
|
88
|
+
if (isDisabled) {
|
|
89
|
+
// 禁用态阻止键盘触发(Enter/Space)
|
|
90
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
e.stopPropagation();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
onKeyDown === null || onKeyDown === void 0 || onKeyDown(e);
|
|
97
|
+
};
|
|
98
|
+
var classes = cn(styles.button, styles[variant], styles[size], block && styles.block, className);
|
|
99
|
+
|
|
100
|
+
// loading 时文案:有 loadingText 则优先,否则沿用 children
|
|
101
|
+
var content = loading ? loadingText !== null && loadingText !== void 0 ? loadingText : children : children;
|
|
102
|
+
var Spinner = /*#__PURE__*/_jsx("span", {
|
|
103
|
+
className: styles.iconSlot,
|
|
104
|
+
"aria-hidden": "true",
|
|
105
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
106
|
+
className: styles.spinner
|
|
107
|
+
})
|
|
108
|
+
});
|
|
109
|
+
var Start = loading && spinnerPlacement === 'start' ? Spinner : startIcon ? /*#__PURE__*/_jsx("span", {
|
|
110
|
+
className: styles.iconSlot,
|
|
111
|
+
"aria-hidden": "true",
|
|
112
|
+
children: startIcon
|
|
113
|
+
}) : null;
|
|
114
|
+
var End = loading && spinnerPlacement === 'end' ? Spinner : endIcon ? /*#__PURE__*/_jsx("span", {
|
|
115
|
+
className: styles.iconSlot,
|
|
116
|
+
"aria-hidden": "true",
|
|
117
|
+
children: endIcon
|
|
118
|
+
}) : null;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* asChild:
|
|
122
|
+
* - 注入 className / data-* / aria-* / 事件
|
|
123
|
+
* - disabled/loading 时设置 aria-disabled 与 tabIndex(避免被聚焦)
|
|
124
|
+
*
|
|
125
|
+
* 注意:asChild 会把语义交给 child(例如 <a> / <Link>)。
|
|
126
|
+
* 组件会尽力提供一致的“禁用交互”行为,但最终语义仍建议由使用者选择正确元素。
|
|
127
|
+
*/
|
|
128
|
+
var sharedProps = _objectSpread({
|
|
129
|
+
className: classes,
|
|
130
|
+
'data-variant': variant,
|
|
131
|
+
'data-size': size,
|
|
132
|
+
'data-disabled': isDisabled ? 'true' : 'false',
|
|
133
|
+
'data-loading': loading ? 'true' : 'false',
|
|
134
|
+
'aria-busy': loading ? true : undefined,
|
|
135
|
+
onClick: handleClick,
|
|
136
|
+
onKeyDown: handleKeyDown
|
|
137
|
+
}, rest);
|
|
138
|
+
if (asChild) {
|
|
139
|
+
return /*#__PURE__*/_jsx(Slot
|
|
140
|
+
// Slot ref 类型为 HTMLElement,这里为了保持 Button 对外签名为 HTMLButtonElement,使用 cast
|
|
141
|
+
, _objectSpread(_objectSpread({
|
|
142
|
+
ref: ref
|
|
143
|
+
}, sharedProps), {}, {
|
|
144
|
+
"aria-disabled": isDisabled ? true : undefined,
|
|
145
|
+
tabIndex: isDisabled ? -1 : rest.tabIndex,
|
|
146
|
+
children: children
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
return /*#__PURE__*/_jsxs("button", _objectSpread(_objectSpread({
|
|
150
|
+
ref: ref,
|
|
151
|
+
disabled: isDisabled
|
|
152
|
+
}, sharedProps), {}, {
|
|
153
|
+
// ✅ eslint-plugin-react 认可:静态字面量 / 简单三元(且最终返回字面量)
|
|
154
|
+
type: type === 'submit' ? 'submit' : type === 'reset' ? 'reset' : 'button',
|
|
155
|
+
children: [Start, content, End]
|
|
156
|
+
}));
|
|
157
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* button.module.css
|
|
2
|
+
设计目标:
|
|
3
|
+
- 使用 CSS Variables 方便主题化
|
|
4
|
+
- focus-visible 清晰可见(键盘才出现)
|
|
5
|
+
- disabled/loading 统一语义(原生 disabled + data-disabled)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
.button {
|
|
9
|
+
--tzz-radius: 10px;
|
|
10
|
+
--tzz-fg: #111827;
|
|
11
|
+
--tzz-bg: #fff;
|
|
12
|
+
--tzz-border: rgba(17, 24, 39, 14%);
|
|
13
|
+
--tzz-primary: #1677ff;
|
|
14
|
+
--tzz-primary-fg: #fff;
|
|
15
|
+
--tzz-danger: #ef4444;
|
|
16
|
+
--tzz-danger-fg: #fff;
|
|
17
|
+
--tzz-ring: rgba(22, 119, 255, 35%);
|
|
18
|
+
|
|
19
|
+
appearance: none;
|
|
20
|
+
border: 1px solid var(--tzz-border);
|
|
21
|
+
background: var(--tzz-bg);
|
|
22
|
+
color: var(--tzz-fg);
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
vertical-align: middle;
|
|
27
|
+
gap: 8px;
|
|
28
|
+
white-space: nowrap;
|
|
29
|
+
user-select: none;
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
line-height: 1;
|
|
32
|
+
text-decoration: none;
|
|
33
|
+
border-radius: var(--tzz-radius);
|
|
34
|
+
transition: background-color 160ms ease, border-color 160ms ease,
|
|
35
|
+
color 160ms ease, box-shadow 160ms ease, transform 80ms ease;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* sizes */
|
|
40
|
+
.sm {
|
|
41
|
+
height: 32px;
|
|
42
|
+
padding: 0 12px;
|
|
43
|
+
font-size: 13px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.md {
|
|
47
|
+
height: 36px;
|
|
48
|
+
padding: 0 14px;
|
|
49
|
+
font-size: 14px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.lg {
|
|
53
|
+
height: 42px;
|
|
54
|
+
padding: 0 18px;
|
|
55
|
+
font-size: 15px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.icon {
|
|
59
|
+
height: 36px;
|
|
60
|
+
width: 36px;
|
|
61
|
+
padding: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* block */
|
|
65
|
+
.block {
|
|
66
|
+
width: 100%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* variants */
|
|
70
|
+
.default {
|
|
71
|
+
background: var(--tzz-bg);
|
|
72
|
+
color: var(--tzz-fg);
|
|
73
|
+
border-color: var(--tzz-border);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.primary {
|
|
77
|
+
background: var(--tzz-primary);
|
|
78
|
+
color: var(--tzz-primary-fg);
|
|
79
|
+
border-color: transparent;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.secondary {
|
|
83
|
+
background: rgba(17, 24, 39, 6%);
|
|
84
|
+
color: var(--tzz-fg);
|
|
85
|
+
border-color: rgba(17, 24, 39, 8%);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ghost {
|
|
89
|
+
background: transparent;
|
|
90
|
+
color: var(--tzz-fg);
|
|
91
|
+
border-color: transparent;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.link {
|
|
95
|
+
background: transparent;
|
|
96
|
+
color: var(--tzz-primary);
|
|
97
|
+
border-color: transparent;
|
|
98
|
+
padding-left: 0;
|
|
99
|
+
padding-right: 0;
|
|
100
|
+
height: auto;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.destructive {
|
|
104
|
+
background: var(--tzz-danger);
|
|
105
|
+
color: var(--tzz-danger-fg);
|
|
106
|
+
border-color: transparent;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* states: hover/active (exclude disabled/loading) */
|
|
110
|
+
.button:not([data-disabled='true']):not([data-loading='true']):hover {
|
|
111
|
+
filter: brightness(0.98);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.default:not([data-disabled='true']):not([data-loading='true']):hover {
|
|
115
|
+
background: rgba(17, 24, 39, 3%);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.ghost:not([data-disabled='true']):not([data-loading='true']):hover {
|
|
119
|
+
background: rgba(17, 24, 39, 5%);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.link:not([data-disabled='true']):not([data-loading='true']):hover {
|
|
123
|
+
text-decoration: underline;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.button:not([data-disabled='true']):not([data-loading='true']):active {
|
|
127
|
+
transform: translateY(0.5px);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* focus-visible */
|
|
131
|
+
.button:focus-visible {
|
|
132
|
+
outline: none;
|
|
133
|
+
box-shadow: 0 0 0 4px var(--tzz-ring);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* disabled/loading */
|
|
137
|
+
.button[disabled],
|
|
138
|
+
.button[data-disabled='true'] {
|
|
139
|
+
opacity: 0.55;
|
|
140
|
+
cursor: not-allowed;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* spinner / icon slots */
|
|
144
|
+
.iconSlot {
|
|
145
|
+
display: inline-flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
justify-content: center;
|
|
148
|
+
line-height: 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.spinner {
|
|
152
|
+
width: 1em;
|
|
153
|
+
height: 1em;
|
|
154
|
+
border: 2px solid currentcolor;
|
|
155
|
+
border-right-color: transparent;
|
|
156
|
+
border-radius: 9999px;
|
|
157
|
+
animation: spin 800ms linear infinite;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@keyframes spin {
|
|
161
|
+
to {
|
|
162
|
+
transform: rotate(360deg);
|
|
163
|
+
}
|
|
164
|
+
}
|
package/es/Button/index.d.ts
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type ButtonProps = React.ComponentProps<typeof AntButton> & {
|
|
4
|
-
dangerLight?: boolean;
|
|
5
|
-
};
|
|
6
|
-
export declare const Button: React.FC<ButtonProps>;
|
|
7
|
-
export default Button;
|
|
1
|
+
export { Button } from './button';
|
|
2
|
+
export type { ButtonProps, ButtonSize, ButtonVariant } from './types';
|
package/es/Button/index.js
CHANGED
|
@@ -1,20 +1 @@
|
|
|
1
|
-
|
|
2
|
-
var _excluded = ["dangerLight"];
|
|
3
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
4
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
5
|
-
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
7
|
-
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
8
|
-
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
9
|
-
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
10
|
-
import { Button as AntButton } from 'antd';
|
|
11
|
-
import React from 'react';
|
|
12
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
export var Button = function Button(_ref) {
|
|
14
|
-
var dangerLight = _ref.dangerLight,
|
|
15
|
-
props = _objectWithoutProperties(_ref, _excluded);
|
|
16
|
-
return /*#__PURE__*/_jsx(AntButton, _objectSpread(_objectSpread({}, props), {}, {
|
|
17
|
-
danger: dangerLight !== null && dangerLight !== void 0 ? dangerLight : props.danger
|
|
18
|
-
}));
|
|
19
|
-
};
|
|
20
|
-
export default Button;
|
|
1
|
+
export { Button } from "./button";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type ButtonVariant = 'default' | 'primary' | 'secondary' | 'ghost' | 'link' | 'destructive';
|
|
3
|
+
export type ButtonSize = 'sm' | 'md' | 'lg' | 'icon';
|
|
4
|
+
export type ButtonIconPosition = 'start' | 'end';
|
|
5
|
+
type NativeButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
6
|
+
export interface ButtonProps extends Omit<NativeButtonProps, 'disabled'> {
|
|
7
|
+
/**
|
|
8
|
+
* 视觉变体
|
|
9
|
+
*/
|
|
10
|
+
variant?: ButtonVariant;
|
|
11
|
+
/**
|
|
12
|
+
* 尺寸
|
|
13
|
+
* - icon: 纯图标按钮(建议提供 aria-label)
|
|
14
|
+
*/
|
|
15
|
+
size?: ButtonSize;
|
|
16
|
+
/**
|
|
17
|
+
* 是否撑满父容器宽度(类似 antd block)
|
|
18
|
+
*/
|
|
19
|
+
block?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* 禁用(asChild 场景使用 aria-disabled + 阻止交互)
|
|
22
|
+
*/
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* 加载态:自动禁用交互,并设置 aria-busy
|
|
26
|
+
*/
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* loading 时展示的文案(可选)
|
|
30
|
+
*/
|
|
31
|
+
loadingText?: React.ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* 左侧图标
|
|
34
|
+
*/
|
|
35
|
+
startIcon?: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* 右侧图标
|
|
38
|
+
*/
|
|
39
|
+
endIcon?: React.ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* spinner 所在位置(默认 start)
|
|
42
|
+
*/
|
|
43
|
+
spinnerPlacement?: ButtonIconPosition;
|
|
44
|
+
/**
|
|
45
|
+
* 多态:将 Button 的 className/事件/属性注入到子元素(类似 shadcn asChild)
|
|
46
|
+
* - children 必须是单个 ReactElement
|
|
47
|
+
*/
|
|
48
|
+
asChild?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* icon-only 的可访问性文案(size="icon" 且无可读文本时强烈建议)
|
|
51
|
+
*/
|
|
52
|
+
'aria-label'?: string;
|
|
53
|
+
/**
|
|
54
|
+
* 测试定位
|
|
55
|
+
*/
|
|
56
|
+
'data-testid'?: string;
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 类名合并工具(不引入 clsx/classnames,保持库自包含)
|
|
4
|
+
* 支持:
|
|
5
|
+
* - cn('a', 'b')
|
|
6
|
+
* - cn('a', condition && 'b')
|
|
7
|
+
* - cn(['a', 'b'])
|
|
8
|
+
* - cn({ a: true, b: false })
|
|
9
|
+
*/
|
|
10
|
+
export type ClassValue = string | number | null | undefined | false | ClassValue[] | Record<string, boolean>;
|
|
11
|
+
export declare function cn(...inputs: ClassValue[]): string;
|
|
12
|
+
/**
|
|
13
|
+
* 合并多个 ref(forwardRef + childRef 场景)
|
|
14
|
+
*/
|
|
15
|
+
export declare function composeRefs<T>(...refs: Array<React.Ref<T> | undefined>): (node: T) => void;
|
|
16
|
+
/**
|
|
17
|
+
* 合并事件处理器:先执行 ours,再执行 theirs
|
|
18
|
+
* - 如果 ours 调用了 preventDefault / stopPropagation,theirs 仍可能执行(取决于你是否 early return)
|
|
19
|
+
* 所以建议在 ours 内部对 disabled/loading 做 early return。
|
|
20
|
+
*/
|
|
21
|
+
export declare function mergeHandlers<E extends {
|
|
22
|
+
defaultPrevented?: boolean;
|
|
23
|
+
}>(ours?: (event: E) => void, theirs?: (event: E) => void): (event: E) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Slot:将 props/className/ref 注入到 child(asChild 模式)
|
|
26
|
+
* - 不依赖 @radix-ui/react-slot
|
|
27
|
+
* - 仅支持单个 ReactElement
|
|
28
|
+
*/
|
|
29
|
+
export interface SlotProps extends React.HTMLAttributes<HTMLElement> {
|
|
30
|
+
children: React.ReactElement<any>;
|
|
31
|
+
}
|
|
32
|
+
export declare const Slot: React.ForwardRefExoticComponent<SlotProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
var _excluded = ["children", "className", "style", "onClick", "onKeyDown"];
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
6
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
+
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
8
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
9
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
10
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
11
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
12
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
13
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
14
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
15
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
16
|
+
import * as React from 'react';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 类名合并工具(不引入 clsx/classnames,保持库自包含)
|
|
20
|
+
* 支持:
|
|
21
|
+
* - cn('a', 'b')
|
|
22
|
+
* - cn('a', condition && 'b')
|
|
23
|
+
* - cn(['a', 'b'])
|
|
24
|
+
* - cn({ a: true, b: false })
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export function cn() {
|
|
28
|
+
var classes = [];
|
|
29
|
+
var push = function push(v) {
|
|
30
|
+
if (!v) return;
|
|
31
|
+
if (typeof v === 'string' || typeof v === 'number') {
|
|
32
|
+
classes.push(String(v));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(v)) {
|
|
36
|
+
v.forEach(push);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (_typeof(v) === 'object') {
|
|
40
|
+
for (var _i = 0, _Object$entries = Object.entries(v); _i < _Object$entries.length; _i++) {
|
|
41
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
42
|
+
k = _Object$entries$_i[0],
|
|
43
|
+
ok = _Object$entries$_i[1];
|
|
44
|
+
if (ok) classes.push(k);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
for (var _len = arguments.length, inputs = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
49
|
+
inputs[_key] = arguments[_key];
|
|
50
|
+
}
|
|
51
|
+
inputs.forEach(push);
|
|
52
|
+
return classes.join(' ');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 合并多个 ref(forwardRef + childRef 场景)
|
|
57
|
+
*/
|
|
58
|
+
export function composeRefs() {
|
|
59
|
+
for (var _len2 = arguments.length, refs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
60
|
+
refs[_key2] = arguments[_key2];
|
|
61
|
+
}
|
|
62
|
+
return function (node) {
|
|
63
|
+
for (var _i2 = 0, _refs = refs; _i2 < _refs.length; _i2++) {
|
|
64
|
+
var ref = _refs[_i2];
|
|
65
|
+
if (!ref) continue;
|
|
66
|
+
if (typeof ref === 'function') ref(node);else ref.current = node;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 合并事件处理器:先执行 ours,再执行 theirs
|
|
73
|
+
* - 如果 ours 调用了 preventDefault / stopPropagation,theirs 仍可能执行(取决于你是否 early return)
|
|
74
|
+
* 所以建议在 ours 内部对 disabled/loading 做 early return。
|
|
75
|
+
*/
|
|
76
|
+
export function mergeHandlers(ours, theirs) {
|
|
77
|
+
return function (event) {
|
|
78
|
+
ours === null || ours === void 0 || ours(event);
|
|
79
|
+
if (event.defaultPrevented) return; // ✅ disabled/loading 时不再触发子元素 handler
|
|
80
|
+
theirs === null || theirs === void 0 || theirs(event);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Slot:将 props/className/ref 注入到 child(asChild 模式)
|
|
86
|
+
* - 不依赖 @radix-ui/react-slot
|
|
87
|
+
* - 仅支持单个 ReactElement
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
export var Slot = /*#__PURE__*/React.forwardRef(function Slot(_ref, forwardedRef) {
|
|
91
|
+
var children = _ref.children,
|
|
92
|
+
className = _ref.className,
|
|
93
|
+
style = _ref.style,
|
|
94
|
+
onClick = _ref.onClick,
|
|
95
|
+
onKeyDown = _ref.onKeyDown,
|
|
96
|
+
rest = _objectWithoutProperties(_ref, _excluded);
|
|
97
|
+
if (! /*#__PURE__*/React.isValidElement(children)) return null;
|
|
98
|
+
var childProps = children.props;
|
|
99
|
+
var mergedClassName = cn(childProps.className, className);
|
|
100
|
+
var mergedStyle = _objectSpread(_objectSpread({}, childProps.style || {}), style || {});
|
|
101
|
+
var mergedOnClick = mergeHandlers(onClick, childProps.onClick);
|
|
102
|
+
var mergedOnKeyDown = mergeHandlers(onKeyDown, childProps.onKeyDown);
|
|
103
|
+
var mergedRef = composeRefs(children.ref, forwardedRef);
|
|
104
|
+
return /*#__PURE__*/React.cloneElement(children, _objectSpread(_objectSpread(_objectSpread({}, rest), childProps), {}, {
|
|
105
|
+
className: mergedClassName,
|
|
106
|
+
style: mergedStyle,
|
|
107
|
+
onClick: mergedOnClick,
|
|
108
|
+
onKeyDown: mergedOnKeyDown,
|
|
109
|
+
ref: mergedRef
|
|
110
|
+
}));
|
|
111
|
+
});
|
package/es/index.d.ts
CHANGED
package/es/index.js
CHANGED
package/es/typings.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ButtonProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Button:生产级可复用按钮组件
|
|
5
|
+
* - variant/size/loading/disabled/icon/asChild
|
|
6
|
+
* - 默认 type="button" 避免表单误提交
|
|
7
|
+
* - loading/disabled 统一语义与交互拦截
|
|
8
|
+
* - focus-visible 清晰可见
|
|
9
|
+
*/
|
|
10
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|