@bifrostui/react 2.0.0-alpha.21 → 2.0.0-alpha.23

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.
@@ -82,7 +82,9 @@ const Dialog = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
82
82
  placeholder,
83
83
  InputProps,
84
84
  className,
85
- theme
85
+ theme,
86
+ okButtonProps,
87
+ cancelButtonProps
86
88
  } = _a, others = __objRest(_a, [
87
89
  "open",
88
90
  "onOk",
@@ -95,7 +97,9 @@ const Dialog = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
95
97
  "placeholder",
96
98
  "InputProps",
97
99
  "className",
98
- "theme"
100
+ "theme",
101
+ "okButtonProps",
102
+ "cancelButtonProps"
99
103
  ]);
100
104
  const inputRef = (0, import_react.useRef)(null);
101
105
  const titleId = (0, import_utils.useUniqueId)();
@@ -118,20 +122,23 @@ const Dialog = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
118
122
  };
119
123
  const actionsNode = /* @__PURE__ */ import_react.default.createElement("div", { className: `${prefixCls}-actions` }, !isAlertMode && /* @__PURE__ */ import_react.default.createElement(
120
124
  import_Button.Button,
121
- {
122
- variant: "text",
125
+ __spreadValues({
126
+ variant: "subtle",
127
+ color: "primary",
128
+ size: "full",
123
129
  onClick: handleCancel,
124
- className: `${prefixCls}-actions-btn`
125
- },
130
+ className: `${prefixCls}-actions-btn-cancel`
131
+ }, cancelButtonProps),
126
132
  cancelText || cancel
127
133
  ), /* @__PURE__ */ import_react.default.createElement(
128
134
  import_Button.Button,
129
- {
130
- variant: "text",
135
+ __spreadValues({
136
+ variant: "contained",
131
137
  color: "primary",
138
+ size: "full",
132
139
  onClick: handleOk,
133
- className: `${prefixCls}-actions-btn`
134
- },
140
+ className: `${prefixCls}-actions-btn-ok`
141
+ }, okButtonProps),
135
142
  okText || ok
136
143
  ));
137
144
  const inputNode = isPromptMode && /* @__PURE__ */ import_react.default.createElement(
@@ -148,12 +155,12 @@ const Dialog = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
148
155
  const ariaDescribedBy = content ? contentId : void 0;
149
156
  return /* @__PURE__ */ import_react.default.createElement(
150
157
  import_Modal.default,
151
- __spreadProps(__spreadValues({}, others), {
158
+ __spreadValues({
152
159
  open,
153
160
  ref,
154
161
  className: (0, import_clsx.default)(prefixCls, `${prefixCls}-${type}`, className),
155
162
  onClose: handleCancel
156
- }),
163
+ }, others),
157
164
  /* @__PURE__ */ import_react.default.createElement(
158
165
  "div",
159
166
  {
@@ -1,6 +1,7 @@
1
1
  import React, { ReactNode } from 'react';
2
2
  import { ModalProps } from '../Modal/Modal.types';
3
3
  import { InputProps } from '../Input/Input.types';
4
+ import { ButtonProps } from '../Button/Button.types';
4
5
  import { ThemeProps } from '../ThemeProvider/ThemeProvider.types';
5
6
  /**
6
7
  * 对话框类型
@@ -29,6 +30,14 @@ export interface DialogProps extends Omit<ModalProps, 'title' | 'content'> {
29
30
  * 透传给内部Input组件的属性
30
31
  */
31
32
  InputProps?: Partial<InputProps>;
33
+ /**
34
+ * 透传给内部Button组件的属性
35
+ */
36
+ okButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
37
+ /**
38
+ * 透传给内部Button组件的属性
39
+ */
40
+ cancelButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
32
41
  /**
33
42
  * 确认按钮文本内容
34
43
  */
@@ -92,7 +92,7 @@ const formatProps = (props) => {
92
92
  const DialogGenerator = (options) => {
93
93
  const dialogFragment = import_utils.isMini ? document.createElement("div") : document.createDocumentFragment();
94
94
  const rootElement = (0, import_utils.getRootContainer)(options == null ? void 0 : options.container);
95
- rootElement.appendChild(dialogFragment);
95
+ rootElement == null ? void 0 : rootElement.appendChild(dialogFragment);
96
96
  const DialogWrapper = () => {
97
97
  const _a = options, { onOk, onCancel } = _a, rest = __objRest(_a, ["onOk", "onCancel"]);
98
98
  const close = (0, import_react.useCallback)(() => {
@@ -1,17 +1,9 @@
1
1
  :root,
2
2
  page,
3
3
  xhs-page {
4
- --bui-dialog-max-width: 300px;
5
- --bui-dialog-border-radius: var(--bui-shape-radius-drawer);
6
- --bui-dialog-title-padding: 0 40px 9px;
7
- --bui-dialog-content-padding: 0 24px;
8
- --bui-dialog-actions-margin: 15px 0 0 0;
9
- --bui-dialog-button-height: 53px;
10
- --bui-dialog-button-line-height: 25px;
11
- --bui-dialog-button-padding: 12px 0 13px;
12
- --bui-dialog-button-font-size: 17px;
13
- --bui-dialog-button-border-left: 1px solid rgba(0, 0, 0, 0.05);
14
- --bui-dialog-button-active-bg-color: rgba(54, 57, 64, 0.05);
4
+ --bui-dialog-width: 300px;
5
+ --bui-dialog-border-radius: var(--bui-radius-8);
6
+ --bui-dialog-padding: 24px;
15
7
  }
16
8
  .bui-dialog {
17
9
  display: flex;
@@ -19,60 +11,42 @@ xhs-page {
19
11
  justify-content: center;
20
12
  }
21
13
  .bui-dialog-container {
22
- padding-top: 21px;
23
- min-width: var(--bui-dialog-max-width);
14
+ padding: var(--bui-dialog-padding);
15
+ position: relative;
16
+ width: var(--bui-dialog-width);
24
17
  margin: 0 auto;
25
18
  border-radius: var(--bui-dialog-border-radius);
26
19
  background-clip: padding-box;
27
20
  background-color: var(--bui-color-bg-view);
28
- line-height: 21px;
29
21
  }
30
22
  .bui-dialog-title {
31
- padding: var(--bui-dialog-title-padding);
23
+ padding-bottom: 12px;
32
24
  font-size: var(--bui-title-size-2);
33
25
  text-align: center;
34
26
  color: var(--bui-color-fg-default);
35
- font-weight: var(--bui-font-weight-medium);
36
- }
37
- .bui-dialog-title + .bui-dialog-actions {
38
- margin-top: var(--bui-spacing-sm);
27
+ font-weight: var(--bui-font-weight-semibold);
28
+ line-height: 1.4;
29
+ margin: 0;
39
30
  }
40
31
  .bui-dialog-content {
41
- padding: var(--bui-dialog-content-padding);
32
+ padding-bottom: 18px;
42
33
  color: var(--bui-color-fg-muted);
43
34
  font-size: var(--bui-title-size-4);
44
35
  text-align: center;
36
+ line-height: 1.5;
45
37
  }
46
38
  .bui-dialog-content:first-child {
47
39
  padding-top: 0;
48
40
  }
41
+ .bui-dialog-content:last-child {
42
+ padding-bottom: 0;
43
+ }
49
44
  .bui-dialog-actions {
50
- margin: var(--bui-dialog-actions-margin);
51
- border-top: 1px solid rgba(0, 0, 0, 0.05);
52
45
  display: flex;
53
- }
54
- .bui-dialog-actions-btn {
55
- flex: 1;
56
- display: block;
57
- width: 100%;
58
- height: var(--bui-dialog-button-height);
59
- line-height: var(--bui-dialog-button-line-height);
60
- padding: var(--bui-dialog-button-padding);
61
- font-size: var(--bui-dialog-button-font-size);
62
- border: 0;
63
- outline: 0;
64
- border-left: var(--bui-dialog-button-border-left);
65
- border-radius: 0;
66
- text-align: center;
67
- box-sizing: border-box;
68
- }
69
- .bui-dialog-actions-btn:first-child {
70
- border: none;
71
- }
72
- .bui-dialog-actions-btn:active {
73
- background-color: var(--bui-dialog-button-active-bg-color);
46
+ gap: var(--bui-spacing-lg);
47
+ align-items: center;
48
+ justify-content: center;
74
49
  }
75
50
  .bui-dialog-input {
76
- width: calc(100% - 48px);
77
- margin: 14px 24px 0;
51
+ margin-bottom: 18px;
78
52
  }
@@ -75,6 +75,8 @@ const Modal = /* @__PURE__ */ React.forwardRef((props, ref) => {
75
75
  container,
76
76
  disablePortal = false,
77
77
  disableScrollLock = false,
78
+ disableAutoFocus = false,
79
+ disableRestoreFocus = false,
78
80
  hideBackdrop = false,
79
81
  onClose,
80
82
  keepMounted = false
@@ -86,6 +88,8 @@ const Modal = /* @__PURE__ */ React.forwardRef((props, ref) => {
86
88
  "container",
87
89
  "disablePortal",
88
90
  "disableScrollLock",
91
+ "disableAutoFocus",
92
+ "disableRestoreFocus",
89
93
  "hideBackdrop",
90
94
  "onClose",
91
95
  "keepMounted"
@@ -100,7 +104,9 @@ const Modal = /* @__PURE__ */ React.forwardRef((props, ref) => {
100
104
  } = (0, import_useModal.useModal)(__spreadProps(__spreadValues({}, props), {
101
105
  container,
102
106
  disableScrollLock,
103
- children: React.isValidElement(children) ? children : void 0,
107
+ disableAutoFocus,
108
+ disableRestoreFocus,
109
+ children,
104
110
  open,
105
111
  onClose,
106
112
  rootRef: ref
@@ -10,7 +10,9 @@ declare const Modal: React.ForwardRefExoticComponent<Omit<ViewProps & {
10
10
  disableScrollLock?: boolean;
11
11
  disablePortal?: boolean;
12
12
  keepMounted?: boolean;
13
+ disableAutoFocus?: boolean;
14
+ disableRestoreFocus?: boolean;
13
15
  } & import("@bifrostui/types").ICommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
14
16
  ref?: React.Ref<HTMLDivElement>;
15
- }, "open" | "container" | keyof import("@bifrostui/types").ICommonProps | "disablePortal" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock" | "keepMounted">, "ref"> & React.RefAttributes<HTMLDivElement>>;
17
+ }, "open" | "container" | keyof import("@bifrostui/types").ICommonProps | "disablePortal" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock" | "keepMounted" | "disableAutoFocus" | "disableRestoreFocus">, "ref"> & React.RefAttributes<HTMLDivElement>>;
16
18
  export default Modal;
@@ -39,6 +39,19 @@ export type ModalProps<D extends React.ElementType = 'div', P = {}> = OverridePr
39
39
  * 是否保持挂载状态
40
40
  */
41
41
  keepMounted?: boolean;
42
+ /**
43
+ * 是否禁用自动焦点管理
44
+ * 仅web端生效
45
+ * @default false
46
+ *
47
+ */
48
+ disableAutoFocus?: boolean;
49
+ /**
50
+ * 是否禁用在关闭时恢复焦点到触发元素
51
+ * 仅web端生效
52
+ * @default false
53
+ */
54
+ disableRestoreFocus?: boolean;
42
55
  };
43
56
  defaultComponent: D;
44
57
  }, D>;
@@ -2,11 +2,14 @@ import React from 'react';
2
2
  export interface UseModalParameters {
3
3
  container?: Element | (() => Element | null) | null;
4
4
  disableScrollLock?: boolean;
5
+ disableAutoFocus?: boolean;
6
+ disableRestoreFocus?: boolean;
5
7
  onClose?: (event: React.SyntheticEvent<Element, Event>, detail?: {
6
8
  from: 'backdropClick' | 'escapeKeyDown';
7
9
  }) => void;
8
10
  open: boolean;
9
11
  rootRef?: React.Ref<Element>;
12
+ children?: React.ReactElement;
10
13
  [key: string]: any;
11
14
  }
12
15
  export interface UseModalReturnValue {
@@ -53,6 +53,8 @@ function useModal(parameters) {
53
53
  const {
54
54
  container,
55
55
  disableScrollLock = false,
56
+ disableAutoFocus = false,
57
+ disableRestoreFocus = false,
56
58
  onClose,
57
59
  open,
58
60
  rootRef,
@@ -67,6 +69,7 @@ function useModal(parameters) {
67
69
  const handleRef = (0, import_utils.useForkRef)(modalRef, rootRef);
68
70
  const [exited, setExited] = (0, import_react.useState)(!open);
69
71
  const hasTransition = getHasTransition(children);
72
+ const lastFocusedElement = (0, import_react.useRef)(null);
70
73
  let ariaHiddenProp = true;
71
74
  if (parameters["aria-hidden"] === "false" || parameters["aria-hidden"] === false) {
72
75
  ariaHiddenProp = false;
@@ -120,6 +123,38 @@ function useModal(parameters) {
120
123
  handleClose();
121
124
  }
122
125
  }, [open, handleClose, hasTransition, handleOpen]);
126
+ (0, import_react.useEffect)(() => {
127
+ if (open && modalRef.current) {
128
+ const doc = modalRef.current.ownerDocument || document;
129
+ if (!lastFocusedElement.current) {
130
+ lastFocusedElement.current = doc.activeElement;
131
+ }
132
+ if (!disableAutoFocus) {
133
+ if (!modalRef.current.contains(doc.activeElement)) {
134
+ if (!modalRef.current.hasAttribute("tabIndex")) {
135
+ modalRef.current.setAttribute("tabIndex", "-1");
136
+ }
137
+ const autoFocusElement = modalRef.current.querySelector(
138
+ '[autofocus], [tabindex="-1"]'
139
+ );
140
+ if (autoFocusElement) {
141
+ autoFocusElement.focus();
142
+ } else {
143
+ modalRef.current.focus();
144
+ }
145
+ }
146
+ }
147
+ return () => {
148
+ if (!disableRestoreFocus && lastFocusedElement.current) {
149
+ if (lastFocusedElement.current && typeof lastFocusedElement.current.focus === "function") {
150
+ lastFocusedElement.current.focus();
151
+ }
152
+ lastFocusedElement.current = null;
153
+ }
154
+ };
155
+ }
156
+ return void 0;
157
+ }, [open, disableAutoFocus, disableRestoreFocus, children]);
123
158
  const createHandleBackdropClick = (backdropHandlers = {}) => (event) => {
124
159
  var _a;
125
160
  (_a = backdropHandlers.onClick) == null ? void 0 : _a.call(backdropHandlers, event);
@@ -87,7 +87,7 @@ const Toast = (props) => {
87
87
  const _a = options, { container } = _a, restOptions = __objRest(_a, ["container"]);
88
88
  const rootWrapper = document.createElement("div");
89
89
  const rootElement = (0, import_utils.getRootContainer)(container);
90
- rootElement.appendChild(rootWrapper);
90
+ rootElement == null ? void 0 : rootElement.appendChild(rootWrapper);
91
91
  const ToastComponent = () => {
92
92
  const _a2 = restOptions, { duration, multiple, onClose, onExited } = _a2, others = __objRest(_a2, ["duration", "multiple", "onClose", "onExited"]);
93
93
  const [open, setOpen] = (0, import_react.useState)(true);
@@ -164,7 +164,6 @@ const UseToastComponent = (props) => {
164
164
  ]);
165
165
  const options = __spreadValues(__spreadValues({}, defaultProps), formatProps(restProps));
166
166
  const _b = options, { duration, multiple, onClose, container } = _b, others = __objRest(_b, ["duration", "multiple", "onClose", "container"]);
167
- const rootElement = (0, import_utils.getRootContainer)(container);
168
167
  const timerRef = (0, import_react.useRef)(null);
169
168
  const close = () => {
170
169
  onSetOpenFalse == null ? void 0 : onSetOpenFalse();
@@ -192,7 +191,7 @@ const UseToastComponent = (props) => {
192
191
  onEnd == null ? void 0 : onEnd();
193
192
  onExited == null ? void 0 : onExited();
194
193
  };
195
- return /* @__PURE__ */ import_react.default.createElement(import_Portal.default, { container: rootElement }, /* @__PURE__ */ import_react.default.createElement(
194
+ return /* @__PURE__ */ import_react.default.createElement(import_Portal.default, { container }, /* @__PURE__ */ import_react.default.createElement(
196
195
  import_Toast.default,
197
196
  __spreadProps(__spreadValues({}, others), {
198
197
  open,
@@ -46,7 +46,7 @@ export interface ToastProps extends FadeProps {
46
46
  * 渲染Toast的根容器
47
47
  * @default document.body
48
48
  */
49
- container?: HTMLElement | (() => HTMLElement);
49
+ container?: HTMLElement | (() => HTMLElement) | null;
50
50
  /**
51
51
  * 关闭时的回调函数
52
52
  */
@@ -52,7 +52,9 @@ const Dialog = /* @__PURE__ */ React.forwardRef((props, ref) => {
52
52
  placeholder,
53
53
  InputProps,
54
54
  className,
55
- theme
55
+ theme,
56
+ okButtonProps,
57
+ cancelButtonProps
56
58
  } = _a, others = __objRest(_a, [
57
59
  "open",
58
60
  "onOk",
@@ -65,7 +67,9 @@ const Dialog = /* @__PURE__ */ React.forwardRef((props, ref) => {
65
67
  "placeholder",
66
68
  "InputProps",
67
69
  "className",
68
- "theme"
70
+ "theme",
71
+ "okButtonProps",
72
+ "cancelButtonProps"
69
73
  ]);
70
74
  const inputRef = useRef(null);
71
75
  const titleId = useUniqueId();
@@ -88,20 +92,23 @@ const Dialog = /* @__PURE__ */ React.forwardRef((props, ref) => {
88
92
  };
89
93
  const actionsNode = /* @__PURE__ */ React.createElement("div", { className: `${prefixCls}-actions` }, !isAlertMode && /* @__PURE__ */ React.createElement(
90
94
  Button,
91
- {
92
- variant: "text",
95
+ __spreadValues({
96
+ variant: "subtle",
97
+ color: "primary",
98
+ size: "full",
93
99
  onClick: handleCancel,
94
- className: `${prefixCls}-actions-btn`
95
- },
100
+ className: `${prefixCls}-actions-btn-cancel`
101
+ }, cancelButtonProps),
96
102
  cancelText || cancel
97
103
  ), /* @__PURE__ */ React.createElement(
98
104
  Button,
99
- {
100
- variant: "text",
105
+ __spreadValues({
106
+ variant: "contained",
101
107
  color: "primary",
108
+ size: "full",
102
109
  onClick: handleOk,
103
- className: `${prefixCls}-actions-btn`
104
- },
110
+ className: `${prefixCls}-actions-btn-ok`
111
+ }, okButtonProps),
105
112
  okText || ok
106
113
  ));
107
114
  const inputNode = isPromptMode && /* @__PURE__ */ React.createElement(
@@ -118,12 +125,12 @@ const Dialog = /* @__PURE__ */ React.forwardRef((props, ref) => {
118
125
  const ariaDescribedBy = content ? contentId : void 0;
119
126
  return /* @__PURE__ */ React.createElement(
120
127
  Modal,
121
- __spreadProps(__spreadValues({}, others), {
128
+ __spreadValues({
122
129
  open,
123
130
  ref,
124
131
  className: clsx(prefixCls, `${prefixCls}-${type}`, className),
125
132
  onClose: handleCancel
126
- }),
133
+ }, others),
127
134
  /* @__PURE__ */ React.createElement(
128
135
  "div",
129
136
  {
@@ -1,6 +1,7 @@
1
1
  import React, { ReactNode } from 'react';
2
2
  import { ModalProps } from '../Modal/Modal.types';
3
3
  import { InputProps } from '../Input/Input.types';
4
+ import { ButtonProps } from '../Button/Button.types';
4
5
  import { ThemeProps } from '../ThemeProvider/ThemeProvider.types';
5
6
  /**
6
7
  * 对话框类型
@@ -29,6 +30,14 @@ export interface DialogProps extends Omit<ModalProps, 'title' | 'content'> {
29
30
  * 透传给内部Input组件的属性
30
31
  */
31
32
  InputProps?: Partial<InputProps>;
33
+ /**
34
+ * 透传给内部Button组件的属性
35
+ */
36
+ okButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
37
+ /**
38
+ * 透传给内部Button组件的属性
39
+ */
40
+ cancelButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
32
41
  /**
33
42
  * 确认按钮文本内容
34
43
  */
@@ -62,7 +62,7 @@ const formatProps = (props) => {
62
62
  const DialogGenerator = (options) => {
63
63
  const dialogFragment = isMini ? document.createElement("div") : document.createDocumentFragment();
64
64
  const rootElement = getRootContainer(options == null ? void 0 : options.container);
65
- rootElement.appendChild(dialogFragment);
65
+ rootElement == null ? void 0 : rootElement.appendChild(dialogFragment);
66
66
  const DialogWrapper = () => {
67
67
  const _a = options, { onOk, onCancel } = _a, rest = __objRest(_a, ["onOk", "onCancel"]);
68
68
  const close = useCallback(() => {
@@ -1,17 +1,9 @@
1
1
  :root,
2
2
  page,
3
3
  xhs-page {
4
- --bui-dialog-max-width: 300px;
5
- --bui-dialog-border-radius: var(--bui-shape-radius-drawer);
6
- --bui-dialog-title-padding: 0 40px 9px;
7
- --bui-dialog-content-padding: 0 24px;
8
- --bui-dialog-actions-margin: 15px 0 0 0;
9
- --bui-dialog-button-height: 53px;
10
- --bui-dialog-button-line-height: 25px;
11
- --bui-dialog-button-padding: 12px 0 13px;
12
- --bui-dialog-button-font-size: 17px;
13
- --bui-dialog-button-border-left: 1px solid rgba(0, 0, 0, 0.05);
14
- --bui-dialog-button-active-bg-color: rgba(54, 57, 64, 0.05);
4
+ --bui-dialog-width: 300px;
5
+ --bui-dialog-border-radius: var(--bui-radius-8);
6
+ --bui-dialog-padding: 24px;
15
7
  }
16
8
  .bui-dialog {
17
9
  display: flex;
@@ -19,60 +11,42 @@ xhs-page {
19
11
  justify-content: center;
20
12
  }
21
13
  .bui-dialog-container {
22
- padding-top: 21px;
23
- min-width: var(--bui-dialog-max-width);
14
+ padding: var(--bui-dialog-padding);
15
+ position: relative;
16
+ width: var(--bui-dialog-width);
24
17
  margin: 0 auto;
25
18
  border-radius: var(--bui-dialog-border-radius);
26
19
  background-clip: padding-box;
27
20
  background-color: var(--bui-color-bg-view);
28
- line-height: 21px;
29
21
  }
30
22
  .bui-dialog-title {
31
- padding: var(--bui-dialog-title-padding);
23
+ padding-bottom: 12px;
32
24
  font-size: var(--bui-title-size-2);
33
25
  text-align: center;
34
26
  color: var(--bui-color-fg-default);
35
- font-weight: var(--bui-font-weight-medium);
36
- }
37
- .bui-dialog-title + .bui-dialog-actions {
38
- margin-top: var(--bui-spacing-sm);
27
+ font-weight: var(--bui-font-weight-semibold);
28
+ line-height: 1.4;
29
+ margin: 0;
39
30
  }
40
31
  .bui-dialog-content {
41
- padding: var(--bui-dialog-content-padding);
32
+ padding-bottom: 18px;
42
33
  color: var(--bui-color-fg-muted);
43
34
  font-size: var(--bui-title-size-4);
44
35
  text-align: center;
36
+ line-height: 1.5;
45
37
  }
46
38
  .bui-dialog-content:first-child {
47
39
  padding-top: 0;
48
40
  }
41
+ .bui-dialog-content:last-child {
42
+ padding-bottom: 0;
43
+ }
49
44
  .bui-dialog-actions {
50
- margin: var(--bui-dialog-actions-margin);
51
- border-top: 1px solid rgba(0, 0, 0, 0.05);
52
45
  display: flex;
53
- }
54
- .bui-dialog-actions-btn {
55
- flex: 1;
56
- display: block;
57
- width: 100%;
58
- height: var(--bui-dialog-button-height);
59
- line-height: var(--bui-dialog-button-line-height);
60
- padding: var(--bui-dialog-button-padding);
61
- font-size: var(--bui-dialog-button-font-size);
62
- border: 0;
63
- outline: 0;
64
- border-left: var(--bui-dialog-button-border-left);
65
- border-radius: 0;
66
- text-align: center;
67
- box-sizing: border-box;
68
- }
69
- .bui-dialog-actions-btn:first-child {
70
- border: none;
71
- }
72
- .bui-dialog-actions-btn:active {
73
- background-color: var(--bui-dialog-button-active-bg-color);
46
+ gap: var(--bui-spacing-lg);
47
+ align-items: center;
48
+ justify-content: center;
74
49
  }
75
50
  .bui-dialog-input {
76
- width: calc(100% - 48px);
77
- margin: 14px 24px 0;
51
+ margin-bottom: 18px;
78
52
  }
package/es/Modal/Modal.js CHANGED
@@ -45,6 +45,8 @@ const Modal = /* @__PURE__ */ React.forwardRef((props, ref) => {
45
45
  container,
46
46
  disablePortal = false,
47
47
  disableScrollLock = false,
48
+ disableAutoFocus = false,
49
+ disableRestoreFocus = false,
48
50
  hideBackdrop = false,
49
51
  onClose,
50
52
  keepMounted = false
@@ -56,6 +58,8 @@ const Modal = /* @__PURE__ */ React.forwardRef((props, ref) => {
56
58
  "container",
57
59
  "disablePortal",
58
60
  "disableScrollLock",
61
+ "disableAutoFocus",
62
+ "disableRestoreFocus",
59
63
  "hideBackdrop",
60
64
  "onClose",
61
65
  "keepMounted"
@@ -70,7 +74,9 @@ const Modal = /* @__PURE__ */ React.forwardRef((props, ref) => {
70
74
  } = useModal(__spreadProps(__spreadValues({}, props), {
71
75
  container,
72
76
  disableScrollLock,
73
- children: React.isValidElement(children) ? children : void 0,
77
+ disableAutoFocus,
78
+ disableRestoreFocus,
79
+ children,
74
80
  open,
75
81
  onClose,
76
82
  rootRef: ref
@@ -10,7 +10,9 @@ declare const Modal: React.ForwardRefExoticComponent<Omit<ViewProps & {
10
10
  disableScrollLock?: boolean;
11
11
  disablePortal?: boolean;
12
12
  keepMounted?: boolean;
13
+ disableAutoFocus?: boolean;
14
+ disableRestoreFocus?: boolean;
13
15
  } & import("@bifrostui/types").ICommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
14
16
  ref?: React.Ref<HTMLDivElement>;
15
- }, "open" | "container" | keyof import("@bifrostui/types").ICommonProps | "disablePortal" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock" | "keepMounted">, "ref"> & React.RefAttributes<HTMLDivElement>>;
17
+ }, "open" | "container" | keyof import("@bifrostui/types").ICommonProps | "disablePortal" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock" | "keepMounted" | "disableAutoFocus" | "disableRestoreFocus">, "ref"> & React.RefAttributes<HTMLDivElement>>;
16
18
  export default Modal;
@@ -39,6 +39,19 @@ export type ModalProps<D extends React.ElementType = 'div', P = {}> = OverridePr
39
39
  * 是否保持挂载状态
40
40
  */
41
41
  keepMounted?: boolean;
42
+ /**
43
+ * 是否禁用自动焦点管理
44
+ * 仅web端生效
45
+ * @default false
46
+ *
47
+ */
48
+ disableAutoFocus?: boolean;
49
+ /**
50
+ * 是否禁用在关闭时恢复焦点到触发元素
51
+ * 仅web端生效
52
+ * @default false
53
+ */
54
+ disableRestoreFocus?: boolean;
42
55
  };
43
56
  defaultComponent: D;
44
57
  }, D>;
@@ -2,11 +2,14 @@ import React from 'react';
2
2
  export interface UseModalParameters {
3
3
  container?: Element | (() => Element | null) | null;
4
4
  disableScrollLock?: boolean;
5
+ disableAutoFocus?: boolean;
6
+ disableRestoreFocus?: boolean;
5
7
  onClose?: (event: React.SyntheticEvent<Element, Event>, detail?: {
6
8
  from: 'backdropClick' | 'escapeKeyDown';
7
9
  }) => void;
8
10
  open: boolean;
9
11
  rootRef?: React.Ref<Element>;
12
+ children?: React.ReactElement;
10
13
  [key: string]: any;
11
14
  }
12
15
  export interface UseModalReturnValue {
@@ -33,6 +33,8 @@ function useModal(parameters) {
33
33
  const {
34
34
  container,
35
35
  disableScrollLock = false,
36
+ disableAutoFocus = false,
37
+ disableRestoreFocus = false,
36
38
  onClose,
37
39
  open,
38
40
  rootRef,
@@ -47,6 +49,7 @@ function useModal(parameters) {
47
49
  const handleRef = useForkRef(modalRef, rootRef);
48
50
  const [exited, setExited] = useState(!open);
49
51
  const hasTransition = getHasTransition(children);
52
+ const lastFocusedElement = useRef(null);
50
53
  let ariaHiddenProp = true;
51
54
  if (parameters["aria-hidden"] === "false" || parameters["aria-hidden"] === false) {
52
55
  ariaHiddenProp = false;
@@ -100,6 +103,38 @@ function useModal(parameters) {
100
103
  handleClose();
101
104
  }
102
105
  }, [open, handleClose, hasTransition, handleOpen]);
106
+ useEffect(() => {
107
+ if (open && modalRef.current) {
108
+ const doc = modalRef.current.ownerDocument || document;
109
+ if (!lastFocusedElement.current) {
110
+ lastFocusedElement.current = doc.activeElement;
111
+ }
112
+ if (!disableAutoFocus) {
113
+ if (!modalRef.current.contains(doc.activeElement)) {
114
+ if (!modalRef.current.hasAttribute("tabIndex")) {
115
+ modalRef.current.setAttribute("tabIndex", "-1");
116
+ }
117
+ const autoFocusElement = modalRef.current.querySelector(
118
+ '[autofocus], [tabindex="-1"]'
119
+ );
120
+ if (autoFocusElement) {
121
+ autoFocusElement.focus();
122
+ } else {
123
+ modalRef.current.focus();
124
+ }
125
+ }
126
+ }
127
+ return () => {
128
+ if (!disableRestoreFocus && lastFocusedElement.current) {
129
+ if (lastFocusedElement.current && typeof lastFocusedElement.current.focus === "function") {
130
+ lastFocusedElement.current.focus();
131
+ }
132
+ lastFocusedElement.current = null;
133
+ }
134
+ };
135
+ }
136
+ return void 0;
137
+ }, [open, disableAutoFocus, disableRestoreFocus, children]);
103
138
  const createHandleBackdropClick = (backdropHandlers = {}) => (event) => {
104
139
  var _a;
105
140
  (_a = backdropHandlers.onClick) == null ? void 0 : _a.call(backdropHandlers, event);
@@ -62,7 +62,7 @@ const Toast = (props) => {
62
62
  const _a = options, { container } = _a, restOptions = __objRest(_a, ["container"]);
63
63
  const rootWrapper = document.createElement("div");
64
64
  const rootElement = getRootContainer(container);
65
- rootElement.appendChild(rootWrapper);
65
+ rootElement == null ? void 0 : rootElement.appendChild(rootWrapper);
66
66
  const ToastComponent = () => {
67
67
  const _a2 = restOptions, { duration, multiple, onClose, onExited } = _a2, others = __objRest(_a2, ["duration", "multiple", "onClose", "onExited"]);
68
68
  const [open, setOpen] = useState(true);
@@ -139,7 +139,6 @@ const UseToastComponent = (props) => {
139
139
  ]);
140
140
  const options = __spreadValues(__spreadValues({}, defaultProps), formatProps(restProps));
141
141
  const _b = options, { duration, multiple, onClose, container } = _b, others = __objRest(_b, ["duration", "multiple", "onClose", "container"]);
142
- const rootElement = getRootContainer(container);
143
142
  const timerRef = useRef(null);
144
143
  const close = () => {
145
144
  onSetOpenFalse == null ? void 0 : onSetOpenFalse();
@@ -167,7 +166,7 @@ const UseToastComponent = (props) => {
167
166
  onEnd == null ? void 0 : onEnd();
168
167
  onExited == null ? void 0 : onExited();
169
168
  };
170
- return /* @__PURE__ */ React.createElement(Portal, { container: rootElement }, /* @__PURE__ */ React.createElement(
169
+ return /* @__PURE__ */ React.createElement(Portal, { container }, /* @__PURE__ */ React.createElement(
171
170
  ToastView,
172
171
  __spreadProps(__spreadValues({}, others), {
173
172
  open,
@@ -46,7 +46,7 @@ export interface ToastProps extends FadeProps {
46
46
  * 渲染Toast的根容器
47
47
  * @default document.body
48
48
  */
49
- container?: HTMLElement | (() => HTMLElement);
49
+ container?: HTMLElement | (() => HTMLElement) | null;
50
50
  /**
51
51
  * 关闭时的回调函数
52
52
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifrostui/react",
3
- "version": "2.0.0-alpha.21",
3
+ "version": "2.0.0-alpha.23",
4
4
  "description": "React components for building mobile application",
5
5
  "homepage": "http://bui.taopiaopiao.com",
6
6
  "license": "MIT",
@@ -43,10 +43,10 @@
43
43
  "clsx": "^2.1.1",
44
44
  "dayjs": "^1.11.7",
45
45
  "swiper": "^8.1.5",
46
- "@bifrostui/styles": "2.0.0-alpha.21",
47
- "@bifrostui/types": "2.0.0-alpha.21",
48
- "@bifrostui/utils": "2.0.0-alpha.21",
49
- "@bifrostui/icons": "2.0.0-alpha.21"
46
+ "@bifrostui/icons": "2.0.0-alpha.23",
47
+ "@bifrostui/utils": "2.0.0-alpha.23",
48
+ "@bifrostui/styles": "2.0.0-alpha.23",
49
+ "@bifrostui/types": "2.0.0-alpha.23"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@tarojs/components": "^3.0.0",