@mezzanine-ui/react 1.0.0-rc.5 → 1.0.0-rc.7
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/Accordion/Accordion.d.ts +36 -0
- package/Accordion/Accordion.js +37 -1
- package/AlertBanner/AlertBanner.d.ts +30 -1
- package/AlertBanner/AlertBanner.js +56 -12
- package/AutoComplete/AutoComplete.d.ts +75 -11
- package/AutoComplete/AutoComplete.js +145 -25
- package/AutoComplete/AutoCompleteInside.d.ts +54 -0
- package/AutoComplete/AutoCompleteInside.js +17 -0
- package/AutoComplete/useAutoCompleteKeyboard.d.ts +2 -1
- package/AutoComplete/useAutoCompleteKeyboard.js +4 -1
- package/Backdrop/Backdrop.d.ts +33 -1
- package/Backdrop/Backdrop.js +33 -1
- package/Badge/Badge.d.ts +26 -1
- package/Badge/Badge.js +26 -1
- package/Badge/typings.d.ts +15 -0
- package/Breadcrumb/BreadcrumbDropdown.d.ts +1 -1
- package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +1 -1
- package/Breadcrumb/typings.d.ts +1 -1
- package/Button/Button.d.ts +26 -1
- package/Button/Button.js +33 -5
- package/COMPONENTS.md +267 -0
- package/Calendar/useCalendarControls.d.ts +18 -0
- package/Calendar/useCalendarControls.js +18 -0
- package/Card/FourThumbnailCard.js +1 -1
- package/Card/SingleThumbnailCard.js +1 -1
- package/Checkbox/Checkbox.d.ts +30 -1
- package/Checkbox/Checkbox.js +54 -4
- package/Checkbox/CheckboxGroup.d.ts +39 -1
- package/Checkbox/CheckboxGroup.js +49 -3
- package/Cropper/Cropper.d.ts +2 -2
- package/Cropper/Cropper.js +2 -3
- package/Cropper/CropperElement.js +1 -1
- package/Cropper/utils/cropper-calculations.d.ts +1 -1
- package/Cropper/utils/cropper-calculations.js +16 -14
- package/DatePicker/DatePicker.d.ts +36 -2
- package/DatePicker/DatePicker.js +36 -2
- package/DateRangePicker/useDateRangePickerValue.d.ts +24 -0
- package/DateRangePicker/useDateRangePickerValue.js +24 -0
- package/Description/Description.d.ts +1 -1
- package/Description/Description.js +1 -1
- package/Description/DescriptionTitle.d.ts +6 -1
- package/Description/DescriptionTitle.js +2 -2
- package/Drawer/Drawer.d.ts +78 -33
- package/Drawer/Drawer.js +72 -34
- package/Dropdown/Dropdown.d.ts +63 -1
- package/Dropdown/Dropdown.js +203 -9
- package/Dropdown/DropdownItem.d.ts +26 -2
- package/Dropdown/DropdownItem.js +91 -43
- package/Dropdown/DropdownItemCard.d.ts +3 -2
- package/Dropdown/DropdownItemCard.js +8 -5
- package/Dropdown/DropdownStatus.d.ts +2 -2
- package/Dropdown/DropdownStatus.js +3 -2
- package/Dropdown/dropdownKeydownHandler.d.ts +6 -0
- package/Dropdown/dropdownKeydownHandler.js +14 -7
- package/Empty/icons/EmptyMainInitialDataIcon.js +1 -1
- package/Empty/icons/EmptyMainNotificationIcon.js +1 -1
- package/Empty/icons/EmptyMainResultIcon.js +1 -1
- package/Empty/icons/EmptyMainSystemIcon.js +1 -1
- package/FilterArea/Filter.d.ts +25 -2
- package/FilterArea/Filter.js +43 -2
- package/FilterArea/FilterArea.d.ts +43 -4
- package/FilterArea/FilterArea.js +39 -4
- package/FilterArea/FilterAreaContext.d.ts +6 -0
- package/FilterArea/FilterAreaContext.js +6 -0
- package/FilterArea/FilterLine.d.ts +19 -0
- package/FilterArea/FilterLine.js +19 -0
- package/FilterArea/index.d.ts +2 -2
- package/FilterArea/index.js +1 -1
- package/Form/FormField.d.ts +43 -2
- package/Form/FormField.js +40 -3
- package/Form/typings.d.ts +2 -0
- package/Form/useAutoCompleteValueControl.d.ts +20 -0
- package/Form/useAutoCompleteValueControl.js +20 -0
- package/Form/useCheckboxControlValue.d.ts +19 -0
- package/Form/useCheckboxControlValue.js +19 -0
- package/Form/useControlValueState.d.ts +16 -0
- package/Form/useControlValueState.js +16 -0
- package/Form/useCustomControlValue.d.ts +17 -0
- package/Form/useCustomControlValue.js +17 -0
- package/Form/useInputControlValue.d.ts +19 -0
- package/Form/useInputControlValue.js +19 -0
- package/Form/useInputWithClearControlValue.d.ts +20 -0
- package/Form/useInputWithClearControlValue.js +20 -0
- package/Form/useRadioControlValue.d.ts +19 -0
- package/Form/useRadioControlValue.js +19 -0
- package/Form/useSelectValueControl.d.ts +19 -0
- package/Form/useSelectValueControl.js +19 -0
- package/Form/useSwitchControlValue.d.ts +18 -0
- package/Form/useSwitchControlValue.js +18 -0
- package/Icon/Icon.d.ts +23 -1
- package/Icon/Icon.js +23 -1
- package/Input/Input.d.ts +31 -1
- package/Input/Input.js +31 -1
- package/Input/SpinnerButton/SpinnerButton.js +1 -1
- package/Message/Message.d.ts +56 -6
- package/Message/Message.js +59 -52
- package/Modal/Modal.d.ts +76 -4
- package/Modal/Modal.js +117 -6
- package/Modal/ModalBodyForVerification.js +3 -1
- package/Modal/ModalFooter.d.ts +21 -6
- package/Modal/ModalHeader.d.ts +50 -11
- package/Modal/ModalHeader.js +1 -1
- package/Modal/index.d.ts +1 -1
- package/Navigation/Navigation.js +20 -11
- package/Navigation/NavigationHeader.js +2 -1
- package/Navigation/NavigationIconButton.d.ts +8 -0
- package/Navigation/NavigationIconButton.js +2 -2
- package/Navigation/NavigationOption.js +6 -3
- package/Navigation/NavigationOverflowMenu.js +9 -6
- package/Navigation/NavigationUserMenu.js +3 -1
- package/Navigation/context.d.ts +1 -0
- package/NotificationCenter/NotificationCenter.d.ts +59 -9
- package/NotificationCenter/NotificationCenter.js +150 -54
- package/NotificationCenter/NotificationCenterDrawer.d.ts +56 -6
- package/NotificationCenter/NotificationCenterDrawer.js +6 -8
- package/NotificationCenter/index.d.ts +1 -1
- package/OverflowTooltip/OverflowTooltip.js +46 -5
- package/PageFooter/PageFooter.d.ts +1 -1
- package/PageFooter/PageFooter.js +8 -16
- package/Pagination/Pagination.d.ts +38 -1
- package/Pagination/Pagination.js +38 -1
- package/Pagination/PaginationPageSize.js +1 -1
- package/Pagination/usePagination.d.ts +20 -0
- package/Pagination/usePagination.js +20 -0
- package/Progress/Progress.d.ts +24 -1
- package/Progress/Progress.js +24 -1
- package/README.md +82 -0
- package/Radio/Radio.d.ts +35 -1
- package/Radio/Radio.js +50 -12
- package/Radio/RadioGroup.d.ts +2 -2
- package/Radio/RadioGroupContext.d.ts +3 -2
- package/Section/SectionGroup.d.ts +20 -0
- package/Section/SectionGroup.js +16 -0
- package/Section/index.d.ts +2 -0
- package/Section/index.js +1 -0
- package/Select/Select.d.ts +55 -1
- package/Select/Select.js +37 -2
- package/Select/typings.d.ts +9 -0
- package/{Selection/Selection.d.ts → SelectionCard/SelectionCard.d.ts} +23 -11
- package/{Selection/Selection.js → SelectionCard/SelectionCard.js} +14 -14
- package/SelectionCard/SelectionCardGroup.d.ts +28 -0
- package/SelectionCard/index.d.ts +12 -0
- package/SelectionCard/index.js +5 -0
- package/Separator/Separator.d.ts +19 -1
- package/Separator/Separator.js +19 -1
- package/Slider/Slider.d.ts +11 -0
- package/Slider/useSlider.d.ts +21 -0
- package/Spin/Spin.d.ts +14 -5
- package/Spin/Spin.js +6 -8
- package/Stepper/Stepper.d.ts +34 -1
- package/Stepper/Stepper.js +34 -1
- package/Stepper/useStepper.d.ts +18 -0
- package/Stepper/useStepper.js +18 -0
- package/Tab/Tab.d.ts +33 -1
- package/Tab/Tab.js +33 -1
- package/Table/Table.d.ts +51 -3
- package/Table/Table.js +52 -4
- package/Table/hooks/useTableDataSource.d.ts +4 -4
- package/Table/hooks/useTableDataSource.js +1 -1
- package/Tag/Tag.d.ts +27 -1
- package/Tag/Tag.js +27 -1
- package/Tag/typings.d.ts +28 -0
- package/Textarea/Textarea.d.ts +28 -1
- package/Textarea/Textarea.js +25 -1
- package/TimePicker/TimePicker.js +1 -1
- package/TimeRangePicker/TimeRangePicker.js +1 -1
- package/Toggle/Toggle.d.ts +34 -1
- package/Toggle/Toggle.js +34 -1
- package/Tooltip/Tooltip.d.ts +39 -1
- package/Tooltip/Tooltip.js +39 -1
- package/Tooltip/useDelayMouseEnterLeave.d.ts +17 -0
- package/Tooltip/useDelayMouseEnterLeave.js +17 -0
- package/Typography/Typography.d.ts +23 -1
- package/Typography/Typography.js +23 -1
- package/Upload/Upload.d.ts +13 -7
- package/Upload/Upload.js +55 -20
- package/Upload/UploadItem.d.ts +3 -3
- package/Upload/UploadItem.js +11 -8
- package/Upload/UploadPictureCard.d.ts +5 -0
- package/Upload/UploadPictureCard.js +10 -6
- package/Upload/Uploader.d.ts +32 -31
- package/Upload/Uploader.js +10 -9
- package/hooks/useClickAway.d.ts +16 -0
- package/hooks/useClickAway.js +16 -0
- package/hooks/useComposeRefs.d.ts +15 -0
- package/hooks/useComposeRefs.js +15 -0
- package/hooks/useDocumentEscapeKeyDown.d.ts +15 -0
- package/hooks/useDocumentEscapeKeyDown.js +15 -0
- package/hooks/useDocumentEvents.d.ts +16 -0
- package/hooks/useDocumentEvents.js +16 -0
- package/hooks/useDocumentTabKeyDown.d.ts +15 -0
- package/hooks/useDocumentTabKeyDown.js +15 -0
- package/hooks/useIsomorphicLayoutEffect.d.ts +16 -0
- package/hooks/useIsomorphicLayoutEffect.js +16 -0
- package/hooks/useLastCallback.d.ts +15 -0
- package/hooks/useLastCallback.js +15 -0
- package/hooks/useLastValue.d.ts +18 -0
- package/hooks/useLastValue.js +18 -0
- package/hooks/usePreviousValue.d.ts +14 -0
- package/hooks/usePreviousValue.js +14 -0
- package/hooks/useScrollLock.d.ts +6 -4
- package/hooks/useScrollLock.js +6 -4
- package/hooks/useTopStack.d.ts +17 -0
- package/hooks/useTopStack.js +17 -0
- package/hooks/useWindowWidth.d.ts +14 -0
- package/hooks/useWindowWidth.js +14 -0
- package/index.d.ts +6 -4
- package/index.js +3 -2
- package/llms.txt +200 -0
- package/package.json +5 -4
- package/Selection/SelectionGroup.d.ts +0 -28
- package/Selection/index.d.ts +0 -12
- package/Selection/index.js +0 -5
package/FilterArea/Filter.js
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { forwardRef, useMemo } from 'react';
|
|
3
|
+
import { forwardRef, useContext, useMemo, Children, isValidElement, cloneElement } from 'react';
|
|
4
4
|
import { filterAreaClasses, filterAreaPrefix } from '@mezzanine-ui/core/filter-area';
|
|
5
|
+
import FilterAreaContext from './FilterAreaContext.js';
|
|
5
6
|
import cx from 'clsx';
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* 單一篩選條件元件,用於在 FilterLine 中定義欄位的佔位寬度。
|
|
10
|
+
*
|
|
11
|
+
* 使用 6 欄 Grid,`span` 決定欄位佔用幾欄(1–6);
|
|
12
|
+
* `grow` 設為 `true` 時欄位自動填滿整行。
|
|
13
|
+
* 從 FilterAreaContext 繼承 `size`,統一套用至內部的輸入元件。
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* import { Filter } from '@mezzanine-ui/react';
|
|
18
|
+
* import { FormField } from '@mezzanine-ui/react';
|
|
19
|
+
* import Input from '@mezzanine-ui/react/Input';
|
|
20
|
+
*
|
|
21
|
+
* <Filter span={2}>
|
|
22
|
+
* <FormField label="名稱" name="name" layout="horizontal">
|
|
23
|
+
* <Input placeholder="請輸入" />
|
|
24
|
+
* </FormField>
|
|
25
|
+
* </Filter>
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @see {@link FilterLine} 包含 Filter 的行容器
|
|
29
|
+
* @see {@link FilterArea} 管理整個篩選器的容器
|
|
30
|
+
*/
|
|
7
31
|
const Filter = forwardRef(function Filter(props, ref) {
|
|
32
|
+
var _a;
|
|
8
33
|
const { align = 'stretch', children, className, grow = false, minWidth, span = 2, ...rest } = props;
|
|
34
|
+
const { size } = ((_a = useContext(FilterAreaContext)) !== null && _a !== void 0 ? _a : {});
|
|
9
35
|
const filterClassName = useMemo(() => cx(filterAreaClasses.filter, {
|
|
10
36
|
[filterAreaClasses.filterGrow]: grow,
|
|
11
37
|
[filterAreaClasses.filterAlign(align)]: align,
|
|
@@ -17,7 +43,22 @@ const Filter = forwardRef(function Filter(props, ref) {
|
|
|
17
43
|
}),
|
|
18
44
|
...rest.style,
|
|
19
45
|
}), [grow, minWidth, rest.style, span]);
|
|
20
|
-
|
|
46
|
+
const sizedChildren = useMemo(() => {
|
|
47
|
+
if (!size)
|
|
48
|
+
return children;
|
|
49
|
+
return Children.map(children, (formField) => {
|
|
50
|
+
const sizedInputs = Children.map(formField.props.children, (inputChild) => {
|
|
51
|
+
if (!isValidElement(inputChild))
|
|
52
|
+
return inputChild;
|
|
53
|
+
const { size: ownSize } = inputChild.props;
|
|
54
|
+
if (ownSize !== undefined)
|
|
55
|
+
return inputChild;
|
|
56
|
+
return cloneElement(inputChild, { size });
|
|
57
|
+
});
|
|
58
|
+
return cloneElement(formField, {}, sizedInputs);
|
|
59
|
+
});
|
|
60
|
+
}, [children, size]);
|
|
61
|
+
return (jsx("div", { ...rest, ref: ref, className: filterClassName, style: style, children: sizedChildren }));
|
|
21
62
|
});
|
|
22
63
|
|
|
23
64
|
export { Filter as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComponentPropsWithoutRef, ReactElement } from 'react';
|
|
2
|
-
import { FilterAreaActionsAlign, FilterAreaSize } from '@mezzanine-ui/core/filter-area';
|
|
2
|
+
import { FilterAreaActionsAlign, FilterAreaRowAlign, FilterAreaSize } from '@mezzanine-ui/core/filter-area';
|
|
3
3
|
import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
4
4
|
import { FilterLineProps } from './FilterLine';
|
|
5
5
|
export interface FilterAreaProps extends Omit<NativeElementPropsWithoutKeyAndRef<'div'>, 'children' | 'onSubmit' | 'onReset'> {
|
|
@@ -24,14 +24,14 @@ export interface FilterAreaProps extends Omit<NativeElementPropsWithoutKeyAndRef
|
|
|
24
24
|
*/
|
|
25
25
|
onReset?: () => void;
|
|
26
26
|
/**
|
|
27
|
-
* Callback function triggered when the
|
|
27
|
+
* Callback function triggered when the submit button is clicked.
|
|
28
28
|
* FilterArea itself does not manage form state; the parent component should collect
|
|
29
|
-
* filter values and handle submission logic.
|
|
30
|
-
* handled through FormProvider's handleSubmit.
|
|
29
|
+
* filter values and handle submission logic.
|
|
31
30
|
*/
|
|
32
31
|
onSubmit?: () => void;
|
|
33
32
|
/**
|
|
34
33
|
* The text of the reset button.
|
|
34
|
+
* @default 'Reset'
|
|
35
35
|
*/
|
|
36
36
|
resetText?: string;
|
|
37
37
|
/**
|
|
@@ -41,6 +41,7 @@ export interface FilterAreaProps extends Omit<NativeElementPropsWithoutKeyAndRef
|
|
|
41
41
|
size?: FilterAreaSize;
|
|
42
42
|
/**
|
|
43
43
|
* The text of the submit button.
|
|
44
|
+
* @default 'Search'
|
|
44
45
|
*/
|
|
45
46
|
submitText?: string;
|
|
46
47
|
/**
|
|
@@ -48,11 +49,49 @@ export interface FilterAreaProps extends Omit<NativeElementPropsWithoutKeyAndRef
|
|
|
48
49
|
* @default 'button'
|
|
49
50
|
*/
|
|
50
51
|
resetButtonType?: ComponentPropsWithoutRef<'button'>['type'];
|
|
52
|
+
/**
|
|
53
|
+
* The vertical alignment of the row (cross-axis align-items).
|
|
54
|
+
* @default 'center'
|
|
55
|
+
*/
|
|
56
|
+
rowAlign?: FilterAreaRowAlign;
|
|
51
57
|
/**
|
|
52
58
|
* The type of the submit button.
|
|
53
59
|
* @default 'button'
|
|
54
60
|
*/
|
|
55
61
|
submitButtonType?: ComponentPropsWithoutRef<'button'>['type'];
|
|
56
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* 篩選器容器元件,管理多個 FilterLine 的展示與收合。
|
|
65
|
+
*
|
|
66
|
+
* 預設僅顯示第一行(`FilterLine`),多行時自動出現展開/收合切換按鈕。
|
|
67
|
+
* 透過 `size` 統一控制內部所有表單欄位的尺寸;
|
|
68
|
+
* 透過 `actionsAlign` 調整「送出/重設」按鈕區塊的對齊;
|
|
69
|
+
* 透過 `isDirty` 控制重設按鈕的啟用狀態(`false` 時禁用)。
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```tsx
|
|
73
|
+
* import { Filter, FilterArea, FilterLine } from '@mezzanine-ui/react';
|
|
74
|
+
* import { FormField } from '@mezzanine-ui/react';
|
|
75
|
+
* import Input from '@mezzanine-ui/react/Input';
|
|
76
|
+
*
|
|
77
|
+
* <FilterArea
|
|
78
|
+
* submitText="搜尋"
|
|
79
|
+
* resetText="重設"
|
|
80
|
+
* onSubmit={handleSubmit}
|
|
81
|
+
* onReset={handleReset}
|
|
82
|
+
* >
|
|
83
|
+
* <FilterLine>
|
|
84
|
+
* <Filter span={2}>
|
|
85
|
+
* <FormField label="名稱" name="name" layout="horizontal">
|
|
86
|
+
* <Input placeholder="請輸入" />
|
|
87
|
+
* </FormField>
|
|
88
|
+
* </Filter>
|
|
89
|
+
* </FilterLine>
|
|
90
|
+
* </FilterArea>
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @see {@link FilterLine} 用於組成 FilterArea 的單行條件列
|
|
94
|
+
* @see {@link Filter} 包裝單一篩選欄位的元件
|
|
95
|
+
*/
|
|
57
96
|
declare const FilterArea: import("react").ForwardRefExoticComponent<FilterAreaProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
58
97
|
export default FilterArea;
|
package/FilterArea/FilterArea.js
CHANGED
|
@@ -4,11 +4,46 @@ import { forwardRef, useMemo, Children, useState, useCallback } from 'react';
|
|
|
4
4
|
import { filterAreaClasses } from '@mezzanine-ui/core/filter-area';
|
|
5
5
|
import { ChevronUpIcon, ChevronDownIcon } from '@mezzanine-ui/icons';
|
|
6
6
|
import Button from '../Button/Button.js';
|
|
7
|
+
import FilterAreaContext from './FilterAreaContext.js';
|
|
7
8
|
import cx from 'clsx';
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* 篩選器容器元件,管理多個 FilterLine 的展示與收合。
|
|
12
|
+
*
|
|
13
|
+
* 預設僅顯示第一行(`FilterLine`),多行時自動出現展開/收合切換按鈕。
|
|
14
|
+
* 透過 `size` 統一控制內部所有表單欄位的尺寸;
|
|
15
|
+
* 透過 `actionsAlign` 調整「送出/重設」按鈕區塊的對齊;
|
|
16
|
+
* 透過 `isDirty` 控制重設按鈕的啟用狀態(`false` 時禁用)。
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* import { Filter, FilterArea, FilterLine } from '@mezzanine-ui/react';
|
|
21
|
+
* import { FormField } from '@mezzanine-ui/react';
|
|
22
|
+
* import Input from '@mezzanine-ui/react/Input';
|
|
23
|
+
*
|
|
24
|
+
* <FilterArea
|
|
25
|
+
* submitText="搜尋"
|
|
26
|
+
* resetText="重設"
|
|
27
|
+
* onSubmit={handleSubmit}
|
|
28
|
+
* onReset={handleReset}
|
|
29
|
+
* >
|
|
30
|
+
* <FilterLine>
|
|
31
|
+
* <Filter span={2}>
|
|
32
|
+
* <FormField label="名稱" name="name" layout="horizontal">
|
|
33
|
+
* <Input placeholder="請輸入" />
|
|
34
|
+
* </FormField>
|
|
35
|
+
* </Filter>
|
|
36
|
+
* </FilterLine>
|
|
37
|
+
* </FilterArea>
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @see {@link FilterLine} 用於組成 FilterArea 的單行條件列
|
|
41
|
+
* @see {@link Filter} 包裝單一篩選欄位的元件
|
|
42
|
+
*/
|
|
9
43
|
const FilterArea = forwardRef(function FilterArea(props, ref) {
|
|
10
|
-
const { actionsAlign = 'end', children, className, isDirty = true, onReset, onSubmit, resetText = 'Reset', size = 'main', submitText = 'Search', resetButtonType = 'button', submitButtonType = 'button', ...rest } = props;
|
|
44
|
+
const { actionsAlign = 'end', children, className, isDirty = true, onReset, onSubmit, resetText = 'Reset', rowAlign = 'center', size = 'main', submitText = 'Search', resetButtonType = 'button', submitButtonType = 'button', ...rest } = props;
|
|
11
45
|
const filterLines = useMemo(() => Children.toArray(children), [children]);
|
|
46
|
+
const contextValue = useMemo(() => ({ size }), [size]);
|
|
12
47
|
const hasMultipleLines = filterLines.length > 1;
|
|
13
48
|
const [expanded, setExpanded] = useState(false);
|
|
14
49
|
const handleToggleExpanded = useCallback(() => {
|
|
@@ -23,9 +58,9 @@ const FilterArea = forwardRef(function FilterArea(props, ref) {
|
|
|
23
58
|
return (jsxs("div", { className: cx(filterAreaClasses.actions, filterAreaClasses.actionsAlign(actionsAlign), { [filterAreaClasses.actionsExpanded]: expanded }), children: [jsx(Button, { onClick: handleSubmit, size: size, type: submitButtonType, children: submitText }), jsx(Button, { disabled: !isDirty, onClick: onReset, size: size, type: resetButtonType, variant: "base-secondary", children: resetText }), hasMultipleLines && (jsx(Button, { "aria-expanded": expanded, "aria-label": expanded ? 'Collapse filters' : 'Expand filters', icon: expanded ? ChevronUpIcon : ChevronDownIcon, iconType: "icon-only", onClick: handleToggleExpanded, size: size, title: expanded ? 'Collapse filters' : 'Expand filters', type: "button", variant: "base-ghost" }))] }));
|
|
24
59
|
};
|
|
25
60
|
const firstLine = filterLines[0];
|
|
26
|
-
return (jsx("div", { ...rest, ref: ref, className: cx(filterAreaClasses.host, className, {
|
|
27
|
-
|
|
28
|
-
|
|
61
|
+
return (jsx(FilterAreaContext.Provider, { value: contextValue, children: jsx("div", { ...rest, ref: ref, className: cx(filterAreaClasses.host, className, {
|
|
62
|
+
[filterAreaClasses.size(size)]: size,
|
|
63
|
+
}), children: expanded ? (jsxs(Fragment, { children: [filterLines, jsx("div", { className: cx(filterAreaClasses.row, filterAreaClasses.rowAlign(rowAlign)), children: renderAction() })] })) : (jsx(Fragment, { children: firstLine && (jsxs("div", { className: cx(filterAreaClasses.row, filterAreaClasses.rowAlign(rowAlign)), children: [firstLine, renderAction()] })) })) }) }));
|
|
29
64
|
});
|
|
30
65
|
|
|
31
66
|
export { FilterArea as default };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FilterAreaSize } from '@mezzanine-ui/core/filter-area';
|
|
2
|
+
export interface FilterAreaContextValue {
|
|
3
|
+
size?: FilterAreaSize;
|
|
4
|
+
}
|
|
5
|
+
declare const FilterAreaContext: import("react").Context<FilterAreaContextValue | undefined>;
|
|
6
|
+
export default FilterAreaContext;
|
|
@@ -7,5 +7,24 @@ export interface FilterLineProps extends Omit<NativeElementPropsWithoutKeyAndRef
|
|
|
7
7
|
*/
|
|
8
8
|
children: ReactElement<FilterProps> | ReactElement<FilterProps>[];
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* 篩選器中的單行條件列,包含一或多個 Filter 欄位。
|
|
12
|
+
*
|
|
13
|
+
* 透過 Grid 排版將 Filter 子元件橫向排列;
|
|
14
|
+
* 需作為 FilterArea 的直接子元件使用。
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { Filter, FilterLine } from '@mezzanine-ui/react';
|
|
19
|
+
*
|
|
20
|
+
* <FilterLine>
|
|
21
|
+
* <Filter span={2}>...</Filter>
|
|
22
|
+
* <Filter span={3}>...</Filter>
|
|
23
|
+
* </FilterLine>
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see {@link FilterArea} 管理多個 FilterLine 的容器元件
|
|
27
|
+
* @see {@link Filter} 包裝單一篩選欄位的元件
|
|
28
|
+
*/
|
|
10
29
|
declare const FilterLine: import("react").ForwardRefExoticComponent<FilterLineProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
11
30
|
export default FilterLine;
|
package/FilterArea/FilterLine.js
CHANGED
|
@@ -4,6 +4,25 @@ import { forwardRef, useMemo } from 'react';
|
|
|
4
4
|
import { filterAreaClasses } from '@mezzanine-ui/core/filter-area';
|
|
5
5
|
import cx from 'clsx';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* 篩選器中的單行條件列,包含一或多個 Filter 欄位。
|
|
9
|
+
*
|
|
10
|
+
* 透過 Grid 排版將 Filter 子元件橫向排列;
|
|
11
|
+
* 需作為 FilterArea 的直接子元件使用。
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { Filter, FilterLine } from '@mezzanine-ui/react';
|
|
16
|
+
*
|
|
17
|
+
* <FilterLine>
|
|
18
|
+
* <Filter span={2}>...</Filter>
|
|
19
|
+
* <Filter span={3}>...</Filter>
|
|
20
|
+
* </FilterLine>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @see {@link FilterArea} 管理多個 FilterLine 的容器元件
|
|
24
|
+
* @see {@link Filter} 包裝單一篩選欄位的元件
|
|
25
|
+
*/
|
|
7
26
|
const FilterLine = forwardRef(function FilterLine(props, ref) {
|
|
8
27
|
const { className, children, ...rest } = props;
|
|
9
28
|
const lineClassName = useMemo(() => cx(filterAreaClasses.line, className), [className]);
|
package/FilterArea/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
export { default as Filter } from './Filter';
|
|
2
|
+
export type { FilterProps } from './Filter';
|
|
1
3
|
export { default as FilterArea } from './FilterArea';
|
|
2
4
|
export type { FilterAreaProps } from './FilterArea';
|
|
3
5
|
export { default as FilterLine } from './FilterLine';
|
|
4
6
|
export type { FilterLineProps } from './FilterLine';
|
|
5
|
-
export { default as Filter } from './Filter';
|
|
6
|
-
export type { FilterProps } from './Filter';
|
package/FilterArea/index.js
CHANGED
package/Form/FormField.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ControlFieldSlotLayout, FormFieldCounterColor, FormFieldDensity, FormFieldLabelSpacing, FormFieldLayout } from '@mezzanine-ui/core/form';
|
|
1
|
+
import { ControlFieldSlotColumns, ControlFieldSlotLayout, FormFieldCounterColor, FormFieldDensity, FormFieldLabelSpacing, FormFieldLayout } from '@mezzanine-ui/core/form';
|
|
2
2
|
import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
3
3
|
import { IconDefinition } from '@mezzanine-ui/icons';
|
|
4
4
|
import { SeverityWithInfo } from '@mezzanine-ui/system/severity';
|
|
@@ -13,6 +13,12 @@ export interface FormFieldProps extends NativeElementPropsWithoutKeyAndRef<'div'
|
|
|
13
13
|
* @default FormFieldCounterColor.INFO
|
|
14
14
|
*/
|
|
15
15
|
counterColor?: FormFieldCounterColor;
|
|
16
|
+
/**
|
|
17
|
+
* The number of equal-width columns in the control field slot.
|
|
18
|
+
* Children are laid out in a CSS Grid with equal-width columns; items wrap into additional rows as needed.
|
|
19
|
+
* Omit this prop for single-column (default) layout.
|
|
20
|
+
*/
|
|
21
|
+
controlFieldSlotColumns?: ControlFieldSlotColumns;
|
|
16
22
|
/**
|
|
17
23
|
* The layout variant for the control field slot.
|
|
18
24
|
* Controls the visual styling and appearance of the input control area.
|
|
@@ -101,7 +107,42 @@ export interface FormFieldProps extends NativeElementPropsWithoutKeyAndRef<'div'
|
|
|
101
107
|
severity?: SeverityWithInfo;
|
|
102
108
|
}
|
|
103
109
|
/**
|
|
104
|
-
*
|
|
110
|
+
* 表單欄位容器元件,整合標籤、提示文字與錯誤狀態。
|
|
111
|
+
*
|
|
112
|
+
* 透過 `FormControlContext` 將 `disabled`、`required`、`severity` 等狀態向下傳遞給子元件。
|
|
113
|
+
* 支援水平、垂直與延伸三種排版方式,並可選擇性顯示字數計數器與提示文字圖示。
|
|
114
|
+
* 當 `disabled` 為 `true` 時,提示訊息將不會顯示。
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```tsx
|
|
118
|
+
* import FormField from '@mezzanine-ui/react/Form';
|
|
119
|
+
* import { TextField } from '@mezzanine-ui/react/TextField';
|
|
120
|
+
*
|
|
121
|
+
* // 基本水平排版
|
|
122
|
+
* <FormField name="username" label="使用者名稱">
|
|
123
|
+
* <TextField placeholder="請輸入使用者名稱" />
|
|
124
|
+
* </FormField>
|
|
125
|
+
*
|
|
126
|
+
* // 帶有錯誤提示的垂直排版
|
|
127
|
+
* <FormField
|
|
128
|
+
* name="email"
|
|
129
|
+
* label="電子郵件"
|
|
130
|
+
* layout="vertical"
|
|
131
|
+
* severity="error"
|
|
132
|
+
* hintText="電子郵件格式不正確"
|
|
133
|
+
* >
|
|
134
|
+
* <TextField placeholder="請輸入電子郵件" />
|
|
135
|
+
* </FormField>
|
|
136
|
+
*
|
|
137
|
+
* // 帶有字數計數器
|
|
138
|
+
* <FormField name="bio" label="自我介紹" counter="50/200">
|
|
139
|
+
* <TextField placeholder="請輸入自我介紹" />
|
|
140
|
+
* </FormField>
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* @see {@link FormLabel} 表單標籤元件
|
|
144
|
+
* @see {@link FormHintText} 表單提示文字元件
|
|
145
|
+
* @see {@link FormControlContext} 表單控制狀態 Context
|
|
105
146
|
*/
|
|
106
147
|
declare const FormField: import("react").ForwardRefExoticComponent<FormFieldProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
107
148
|
export default FormField;
|
package/Form/FormField.js
CHANGED
|
@@ -7,10 +7,45 @@ import FormLabel from './FormLabel.js';
|
|
|
7
7
|
import cx from 'clsx';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* 表單欄位容器元件,整合標籤、提示文字與錯誤狀態。
|
|
11
|
+
*
|
|
12
|
+
* 透過 `FormControlContext` 將 `disabled`、`required`、`severity` 等狀態向下傳遞給子元件。
|
|
13
|
+
* 支援水平、垂直與延伸三種排版方式,並可選擇性顯示字數計數器與提示文字圖示。
|
|
14
|
+
* 當 `disabled` 為 `true` 時,提示訊息將不會顯示。
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import FormField from '@mezzanine-ui/react/Form';
|
|
19
|
+
* import { TextField } from '@mezzanine-ui/react/TextField';
|
|
20
|
+
*
|
|
21
|
+
* // 基本水平排版
|
|
22
|
+
* <FormField name="username" label="使用者名稱">
|
|
23
|
+
* <TextField placeholder="請輸入使用者名稱" />
|
|
24
|
+
* </FormField>
|
|
25
|
+
*
|
|
26
|
+
* // 帶有錯誤提示的垂直排版
|
|
27
|
+
* <FormField
|
|
28
|
+
* name="email"
|
|
29
|
+
* label="電子郵件"
|
|
30
|
+
* layout="vertical"
|
|
31
|
+
* severity="error"
|
|
32
|
+
* hintText="電子郵件格式不正確"
|
|
33
|
+
* >
|
|
34
|
+
* <TextField placeholder="請輸入電子郵件" />
|
|
35
|
+
* </FormField>
|
|
36
|
+
*
|
|
37
|
+
* // 帶有字數計數器
|
|
38
|
+
* <FormField name="bio" label="自我介紹" counter="50/200">
|
|
39
|
+
* <TextField placeholder="請輸入自我介紹" />
|
|
40
|
+
* </FormField>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @see {@link FormLabel} 表單標籤元件
|
|
44
|
+
* @see {@link FormHintText} 表單提示文字元件
|
|
45
|
+
* @see {@link FormControlContext} 表單控制狀態 Context
|
|
11
46
|
*/
|
|
12
47
|
const FormField = forwardRef(function FormField(props, ref) {
|
|
13
|
-
const { children, className, counter, counterColor, controlFieldSlotLayout = ControlFieldSlotLayout.MAIN, density, disabled = false, fullWidth = false, hintText, hintTextIcon, showHintTextIcon, label, labelInformationIcon, labelInformationText, labelOptionalMarker, labelSpacing = FormFieldLabelSpacing.MAIN, layout = FormFieldLayout.HORIZONTAL, name, required = false, severity = 'info', ...rest } = props;
|
|
48
|
+
const { children, className, counter, counterColor, controlFieldSlotColumns, controlFieldSlotLayout = ControlFieldSlotLayout.MAIN, density, disabled = false, fullWidth = false, hintText, hintTextIcon, showHintTextIcon, label, labelInformationIcon, labelInformationText, labelOptionalMarker, labelSpacing = FormFieldLabelSpacing.MAIN, layout = FormFieldLayout.HORIZONTAL, name, required = false, severity = 'info', ...rest } = props;
|
|
14
49
|
const formControl = {
|
|
15
50
|
disabled,
|
|
16
51
|
fullWidth,
|
|
@@ -28,7 +63,9 @@ const FormField = forwardRef(function FormField(props, ref) {
|
|
|
28
63
|
return (jsx("div", { ...rest, ref: ref, className: cx(formFieldClasses.host, formFieldClasses.layout(layout), densityClass, {
|
|
29
64
|
[formFieldClasses.disabled]: disabled,
|
|
30
65
|
[formFieldClasses.fullWidth]: fullWidth,
|
|
31
|
-
}, className), children: jsxs(FormControlContext.Provider, { value: formControl, children: [label && (jsx(FormLabel, { className: cx(formFieldClasses.labelArea, labelSpacingClass), htmlFor: name, informationIcon: labelInformationIcon, informationText: labelInformationText, labelText: label, optionalMarker: labelOptionalMarker })), jsxs("div", { className: cx(formFieldClasses.dataEntry), children: [jsx("div", { className: cx(`${formFieldClasses.controlFieldSlot}--${controlFieldSlotLayout}
|
|
66
|
+
}, className), children: jsxs(FormControlContext.Provider, { value: formControl, children: [label && (jsx(FormLabel, { className: cx(formFieldClasses.labelArea, labelSpacingClass), htmlFor: name, informationIcon: labelInformationIcon, informationText: labelInformationText, labelText: label, optionalMarker: labelOptionalMarker })), jsxs("div", { className: cx(formFieldClasses.dataEntry), children: [jsx("div", { className: cx(`${formFieldClasses.controlFieldSlot}--${controlFieldSlotLayout}`, controlFieldSlotColumns
|
|
67
|
+
? formFieldClasses.controlFieldSlotColumns(controlFieldSlotColumns)
|
|
68
|
+
: undefined), children: children }), hintText || hintTextIcon || counter ? (jsxs("div", { className: cx(formFieldClasses.hintTextAndCounterArea, {
|
|
32
69
|
[formFieldClasses.hintTextAndCounterArea + '--align-right']: !(hintText || hintTextIcon) && counter,
|
|
33
70
|
}), children: [(hintText || hintTextIcon) && (jsx(FormHintText, { hintText: hintText, hintTextIcon: hintTextIcon, severity: severity, showHintTextIcon: showHintTextIcon })), counter && (jsx("span", { className: cx(formFieldClasses.counter, formFieldClasses.counterColor(counterColor || FormFieldCounterColor.INFO)), children: counter }))] })) : null] })] }) }));
|
|
34
71
|
});
|
package/Form/typings.d.ts
CHANGED
|
@@ -40,4 +40,24 @@ export type AutoCompleteSingleValueControl = AutoCompleteBaseValueControl & {
|
|
|
40
40
|
onChange: (v: SelectValue | null) => SelectValue | null;
|
|
41
41
|
value: SelectValue | null;
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* 管理 AutoComplete 搜尋文字與選取值的受控狀態 Hook。
|
|
45
|
+
*
|
|
46
|
+
* 根據 `mode` 支援單選(`single`)與多選(`multiple`),並內建選項過濾邏輯、
|
|
47
|
+
* 焦點狀態追蹤以及清除功能,回傳完整的操作介面供 AutoComplete 元件使用。
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* import { useAutoCompleteValueControl } from '@mezzanine-ui/react';
|
|
52
|
+
*
|
|
53
|
+
* const control = useAutoCompleteValueControl({
|
|
54
|
+
* mode: 'single',
|
|
55
|
+
* options: [{ id: '1', name: 'Option A' }],
|
|
56
|
+
* disabledOptionsFilter: false,
|
|
57
|
+
* onChange: (option) => console.log(option),
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @see {@link AutoComplete} 搭配的元件
|
|
62
|
+
*/
|
|
43
63
|
export declare const useAutoCompleteValueControl: (props: UseAutoCompleteValueControl) => AutoCompleteMultipleValueControl | AutoCompleteSingleValueControl;
|
|
@@ -94,6 +94,26 @@ function useAutoCompleteBaseValueControl(props) {
|
|
|
94
94
|
value,
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* 管理 AutoComplete 搜尋文字與選取值的受控狀態 Hook。
|
|
99
|
+
*
|
|
100
|
+
* 根據 `mode` 支援單選(`single`)與多選(`multiple`),並內建選項過濾邏輯、
|
|
101
|
+
* 焦點狀態追蹤以及清除功能,回傳完整的操作介面供 AutoComplete 元件使用。
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```tsx
|
|
105
|
+
* import { useAutoCompleteValueControl } from '@mezzanine-ui/react';
|
|
106
|
+
*
|
|
107
|
+
* const control = useAutoCompleteValueControl({
|
|
108
|
+
* mode: 'single',
|
|
109
|
+
* options: [{ id: '1', name: 'Option A' }],
|
|
110
|
+
* disabledOptionsFilter: false,
|
|
111
|
+
* onChange: (option) => console.log(option),
|
|
112
|
+
* });
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @see {@link AutoComplete} 搭配的元件
|
|
116
|
+
*/
|
|
97
117
|
const useAutoCompleteValueControl = (props) => {
|
|
98
118
|
if (props.mode === 'multiple') {
|
|
99
119
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
@@ -8,4 +8,23 @@ export interface UseCheckboxControlValueProps extends UseSwitchControlValueProps
|
|
|
8
8
|
checkboxGroup?: CheckboxGroupControlContextValue;
|
|
9
9
|
value?: string;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* 管理 Checkbox 勾選狀態的受控 Hook。
|
|
13
|
+
*
|
|
14
|
+
* 支援獨立使用與在 CheckboxGroup 內使用兩種情境:
|
|
15
|
+
* 當傳入 `checkboxGroup` 上下文時,會自動從群組的 `value` 陣列中計算出當前勾選狀態。
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* import { useCheckboxControlValue } from '@mezzanine-ui/react';
|
|
20
|
+
*
|
|
21
|
+
* const [checked, handleChange] = useCheckboxControlValue({
|
|
22
|
+
* defaultChecked: false,
|
|
23
|
+
* onChange: (e) => console.log(e.target.checked),
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @see {@link Checkbox} 搭配的元件
|
|
28
|
+
* @see {@link CheckboxGroup} 搭配的元件
|
|
29
|
+
*/
|
|
11
30
|
export declare function useCheckboxControlValue(props: UseCheckboxControlValueProps): readonly [boolean, (event: import("react").ChangeEvent<HTMLInputElement>) => void];
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { useSwitchControlValue } from './useSwitchControlValue.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 管理 Checkbox 勾選狀態的受控 Hook。
|
|
5
|
+
*
|
|
6
|
+
* 支援獨立使用與在 CheckboxGroup 內使用兩種情境:
|
|
7
|
+
* 當傳入 `checkboxGroup` 上下文時,會自動從群組的 `value` 陣列中計算出當前勾選狀態。
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { useCheckboxControlValue } from '@mezzanine-ui/react';
|
|
12
|
+
*
|
|
13
|
+
* const [checked, handleChange] = useCheckboxControlValue({
|
|
14
|
+
* defaultChecked: false,
|
|
15
|
+
* onChange: (e) => console.log(e.target.checked),
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @see {@link Checkbox} 搭配的元件
|
|
20
|
+
* @see {@link CheckboxGroup} 搭配的元件
|
|
21
|
+
*/
|
|
3
22
|
function useCheckboxControlValue(props) {
|
|
4
23
|
const { checkboxGroup, checked: checkedProp, defaultChecked, onChange: onChangeProp, value, } = props;
|
|
5
24
|
const { onChange: onChangeFromGroup } = checkboxGroup || {};
|
|
@@ -3,4 +3,20 @@ export interface UseControlValueStateProps<V> {
|
|
|
3
3
|
equalityFn?: (a: V, b: V) => boolean;
|
|
4
4
|
value?: V;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* 受控/非受控狀態的底層管理 Hook。
|
|
8
|
+
*
|
|
9
|
+
* 接受 `value`(受控)與 `defaultValue`(非受控)兩種模式,
|
|
10
|
+
* 並在 `value` 從外部更新時自動同步內部 state。
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { useControlValueState } from '@mezzanine-ui/react';
|
|
15
|
+
*
|
|
16
|
+
* const [value, setValue, equalityFn] = useControlValueState({
|
|
17
|
+
* defaultValue: '',
|
|
18
|
+
* value: controlledValue,
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
6
22
|
export declare function useControlValueState<V>(props: UseControlValueStateProps<V>): readonly [V, import("react").Dispatch<import("react").SetStateAction<V>>, (a: V, b: V) => boolean];
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 受控/非受控狀態的底層管理 Hook。
|
|
5
|
+
*
|
|
6
|
+
* 接受 `value`(受控)與 `defaultValue`(非受控)兩種模式,
|
|
7
|
+
* 並在 `value` 從外部更新時自動同步內部 state。
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { useControlValueState } from '@mezzanine-ui/react';
|
|
12
|
+
*
|
|
13
|
+
* const [value, setValue, equalityFn] = useControlValueState({
|
|
14
|
+
* defaultValue: '',
|
|
15
|
+
* value: controlledValue,
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
3
19
|
function useControlValueState(props) {
|
|
4
20
|
const { defaultValue, equalityFn = (a, b) => a === b, value: valueProp, } = props;
|
|
5
21
|
const [value, setValue] = useState(() => typeof valueProp !== 'undefined' ? valueProp : defaultValue);
|
|
@@ -2,4 +2,21 @@ import { UseControlValueStateProps } from './useControlValueState';
|
|
|
2
2
|
export interface UseCustomControlValueProps<V> extends UseControlValueStateProps<V> {
|
|
3
3
|
onChange?: (value: V) => void;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* 通用型別受控值管理 Hook。
|
|
7
|
+
*
|
|
8
|
+
* 支援任意型別的受控(`value`)與非受控(`defaultValue`)狀態,
|
|
9
|
+
* 並透過 `equalityFn` 避免不必要的 re-render 與 `onChange` 呼叫。
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { useCustomControlValue } from '@mezzanine-ui/react';
|
|
14
|
+
*
|
|
15
|
+
* const [value, onChange] = useCustomControlValue<number>({
|
|
16
|
+
* defaultValue: 0,
|
|
17
|
+
* value: controlledNumber,
|
|
18
|
+
* onChange: (v) => console.log(v),
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
5
22
|
export declare function useCustomControlValue<V>(props: UseCustomControlValueProps<V>): readonly [V, (nextValue: V) => void, (a: V, b: V) => boolean];
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { useLastCallback } from '../hooks/useLastCallback.js';
|
|
2
2
|
import { useControlValueState } from './useControlValueState.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 通用型別受控值管理 Hook。
|
|
6
|
+
*
|
|
7
|
+
* 支援任意型別的受控(`value`)與非受控(`defaultValue`)狀態,
|
|
8
|
+
* 並透過 `equalityFn` 避免不必要的 re-render 與 `onChange` 呼叫。
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { useCustomControlValue } from '@mezzanine-ui/react';
|
|
13
|
+
*
|
|
14
|
+
* const [value, onChange] = useCustomControlValue<number>({
|
|
15
|
+
* defaultValue: 0,
|
|
16
|
+
* value: controlledNumber,
|
|
17
|
+
* onChange: (v) => console.log(v),
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
4
21
|
function useCustomControlValue(props) {
|
|
5
22
|
const { onChange: onChangeProp } = props;
|
|
6
23
|
const [value, setValue, equalityFn] = useControlValueState(props);
|
|
@@ -4,4 +4,23 @@ export interface UseInputControlValueProps<E extends HTMLInputElement | HTMLText
|
|
|
4
4
|
defaultValue?: string;
|
|
5
5
|
onChange?: ChangeEventHandler<E>;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* 管理 Input/Textarea 文字值的受控狀態 Hook。
|
|
9
|
+
*
|
|
10
|
+
* 支援受控(`value`)與非受控(`defaultValue`)兩種用法,
|
|
11
|
+
* 回傳當前文字值與穩定的 `onChange` 事件處理器。
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { useInputControlValue } from '@mezzanine-ui/react';
|
|
16
|
+
*
|
|
17
|
+
* function MyInput({ value, onChange }) {
|
|
18
|
+
* const [inputValue, handleChange] = useInputControlValue({ value, onChange });
|
|
19
|
+
* return <input value={inputValue} onChange={handleChange} />;
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @see {@link Input} 搭配的元件
|
|
24
|
+
* @see {@link Textarea} 搭配的元件
|
|
25
|
+
*/
|
|
7
26
|
export declare function useInputControlValue<E extends HTMLInputElement | HTMLTextAreaElement>(props: UseInputControlValueProps<E>): readonly [string, (event: import("react").ChangeEvent<E>) => void];
|