@bifrostui/react 2.0.0-alpha.22 → 2.0.0-alpha.24

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.
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
2
  import { DatePickerProps } from './DatePicker.types';
3
- declare const DatePicker: React.ForwardRefExoticComponent<Omit<DatePickerProps<"div", Omit<import("../Picker").PickerProps<"div", import("..").DrawerProps>, "value" | "defaultValue" | "onChange" | "onClose" | "onConfirm">>, "ref"> & React.RefAttributes<HTMLDivElement>>;
3
+ declare const DatePicker: React.ForwardRefExoticComponent<Omit<DatePickerProps<"div", Omit<import("../Picker").PickerProps<"div", import("..").DrawerProps>, "defaultValue" | "onChange" | "value" | "onClose" | "onConfirm">>, "ref"> & React.RefAttributes<HTMLDivElement>>;
4
4
  export default DatePicker;
@@ -33,11 +33,11 @@ export interface DialogProps extends Omit<ModalProps, 'title' | 'content'> {
33
33
  /**
34
34
  * 透传给内部Button组件的属性
35
35
  */
36
- okButtonProps?: Partial<ButtonProps>;
36
+ okButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
37
37
  /**
38
38
  * 透传给内部Button组件的属性
39
39
  */
40
- cancelButtonProps?: Partial<ButtonProps>;
40
+ cancelButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
41
41
  /**
42
42
  * 确认按钮文本内容
43
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)(() => {
@@ -43,10 +43,12 @@ xhs-page {
43
43
  }
44
44
  .bui-dialog-actions {
45
45
  display: flex;
46
- gap: var(--bui-spacing-lg);
47
46
  align-items: center;
48
47
  justify-content: center;
49
48
  }
49
+ .bui-dialog-actions > * + * {
50
+ margin-left: var(--bui-spacing-lg);
51
+ }
50
52
  .bui-dialog-input {
51
53
  margin-bottom: 18px;
52
54
  }
@@ -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
+ }, keyof import("@bifrostui/types").ICommonProps | "open" | "container" | "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,
@@ -33,11 +33,11 @@ export interface DialogProps extends Omit<ModalProps, 'title' | 'content'> {
33
33
  /**
34
34
  * 透传给内部Button组件的属性
35
35
  */
36
- okButtonProps?: Partial<ButtonProps>;
36
+ okButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
37
37
  /**
38
38
  * 透传给内部Button组件的属性
39
39
  */
40
- cancelButtonProps?: Partial<ButtonProps>;
40
+ cancelButtonProps?: Partial<ButtonProps> & Record<`data-${string}`, string>;
41
41
  /**
42
42
  * 确认按钮文本内容
43
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(() => {
@@ -43,10 +43,12 @@ xhs-page {
43
43
  }
44
44
  .bui-dialog-actions {
45
45
  display: flex;
46
- gap: var(--bui-spacing-lg);
47
46
  align-items: center;
48
47
  justify-content: center;
49
48
  }
49
+ .bui-dialog-actions > * + * {
50
+ margin-left: var(--bui-spacing-lg);
51
+ }
50
52
  .bui-dialog-input {
51
53
  margin-bottom: 18px;
52
54
  }
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
+ }, keyof import("@bifrostui/types").ICommonProps | "open" | "container" | "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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifrostui/react",
3
- "version": "2.0.0-alpha.22",
3
+ "version": "2.0.0-alpha.24",
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/icons": "2.0.0-alpha.22",
47
- "@bifrostui/styles": "2.0.0-alpha.22",
48
- "@bifrostui/utils": "2.0.0-alpha.22",
49
- "@bifrostui/types": "2.0.0-alpha.22"
46
+ "@bifrostui/icons": "2.0.0-alpha.24",
47
+ "@bifrostui/styles": "2.0.0-alpha.24",
48
+ "@bifrostui/utils": "2.0.0-alpha.24",
49
+ "@bifrostui/types": "2.0.0-alpha.24"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@tarojs/components": "^3.0.0",