@moneyforward/mfui-components 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/DateTimeSelection/shared/BasePicker/BasePicker.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/BasePicker/BasePicker.js +4 -4
- package/dist/src/DateTimeSelection/shared/BasePicker/BasePicker.types.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.js +4 -4
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.types.d.ts +8 -0
- package/dist/src/SelectBox/SelectBox.js +46 -8
- package/dist/src/SelectBox/SelectBox.types.d.ts +80 -1
- package/dist/src/SelectBox/hooks/useInfiniteScroll.d.ts +22 -0
- package/dist/src/SelectBox/hooks/useInfiniteScroll.js +65 -0
- package/dist/src/ToggleSwitch/ToggleSwitch.d.ts +9 -0
- package/dist/src/ToggleSwitch/ToggleSwitch.js +32 -0
- package/dist/src/ToggleSwitch/ToggleSwitch.types.d.ts +6 -0
- package/dist/src/ToggleSwitch/ToggleSwitch.types.js +1 -0
- package/dist/src/ToggleSwitch/index.d.ts +2 -0
- package/dist/src/ToggleSwitch/index.js +2 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/utilities/dom/useFixedColumns.js +36 -10
- package/dist/styled-system/css/conditions.js +1 -1
- package/dist/styled-system/jsx/is-valid-prop.js +1 -1
- package/dist/styled-system/recipes/index.d.ts +2 -1
- package/dist/styled-system/recipes/index.js +1 -0
- package/dist/styled-system/recipes/select-box-slot-recipe.d.ts +2 -2
- package/dist/styled-system/recipes/select-box-slot-recipe.js +22 -1
- package/dist/styled-system/recipes/toggle-switch-slot-recipe.d.ts +33 -0
- package/dist/styled-system/recipes/toggle-switch-slot-recipe.js +36 -0
- package/dist/styled-system/types/conditions.d.ts +10 -0
- package/dist/styles.css +221 -17
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -4,4 +4,4 @@ import { type BasePickerProps } from './BasePicker.types';
|
|
|
4
4
|
* This component uses the composition component of the TextBox component. Please refer to the TextBox documentation for their usage.
|
|
5
5
|
* This component extends the props of TextBox component.
|
|
6
6
|
*/
|
|
7
|
-
export declare function BasePicker({ targetDOMNode, enableAutoUnmount, open, onOpenStateChanged, disableAutoOpen, format, baseFormat, customFormatValue, value, defaultValue, onChange, calendarIconButtonProps, renderPopoverContent, onBlur, allowedPlacements, calendarLocale, disabled, ...textBoxProps }: BasePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function BasePicker({ targetDOMNode, enableAutoUnmount, open, onOpenStateChanged, disableAutoOpen, format, baseFormat, customFormatValue, value, defaultValue, onChange, calendarIconButtonProps, renderPopoverContent, onBlur, allowedPlacements, enableViewportConstraint, calendarLocale, disabled, ...textBoxProps }: BasePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -18,7 +18,7 @@ import { getBasePickerLabel } from './constants';
|
|
|
18
18
|
* This component uses the composition component of the TextBox component. Please refer to the TextBox documentation for their usage.
|
|
19
19
|
* This component extends the props of TextBox component.
|
|
20
20
|
*/
|
|
21
|
-
export function BasePicker({ targetDOMNode, enableAutoUnmount = true, open = false, onOpenStateChanged, disableAutoOpen = false, format = 'YYYY/MM/DD', baseFormat = 'YYYY-MM-DD', customFormatValue, value, defaultValue, onChange, calendarIconButtonProps, renderPopoverContent, onBlur, allowedPlacements, calendarLocale = 'ja', disabled, ...textBoxProps }) {
|
|
21
|
+
export function BasePicker({ targetDOMNode, enableAutoUnmount = true, open = false, onOpenStateChanged, disableAutoOpen = false, format = 'YYYY/MM/DD', baseFormat = 'YYYY-MM-DD', customFormatValue, value, defaultValue, onChange, calendarIconButtonProps, renderPopoverContent, onBlur, allowedPlacements, enableViewportConstraint, calendarLocale = 'ja', disabled, ...textBoxProps }) {
|
|
22
22
|
const textBoxRef = useRef(null);
|
|
23
23
|
const basePickerPopoverWrapperRef = useRef(null);
|
|
24
24
|
const triggerRef = useRef(null);
|
|
@@ -93,7 +93,7 @@ export function BasePicker({ targetDOMNode, enableAutoUnmount = true, open = fal
|
|
|
93
93
|
inputValueProps.onBlur(event);
|
|
94
94
|
// Then call the Popover's smart blur detection
|
|
95
95
|
handleTriggerBlur(event);
|
|
96
|
-
} })), open: isBasePickerOpen, targetDOMNode: targetDOMNode, enableAutoUnmount: enableAutoUnmount, minWidth: "min-content", value: value, defaultValue: defaultValue, baseFormat: baseFormat, renderPopoverContent: renderPopoverContent, textBoxRef: textBoxRef, triggerRef: triggerRef, basePickerPopoverWrapperRef: basePickerPopoverWrapperRef, handleOnKeyDown: handleOnKeyDown, pickerPopoverProps: pickerPopoverProps, allowedPlacements: allowedPlacements, onOpenStateChanged: toggleBasePicker, onBlur: onBlur }) }));
|
|
96
|
+
} })), open: isBasePickerOpen, targetDOMNode: targetDOMNode, enableAutoUnmount: enableAutoUnmount, minWidth: "min-content", value: value, defaultValue: defaultValue, baseFormat: baseFormat, renderPopoverContent: renderPopoverContent, textBoxRef: textBoxRef, triggerRef: triggerRef, basePickerPopoverWrapperRef: basePickerPopoverWrapperRef, handleOnKeyDown: handleOnKeyDown, pickerPopoverProps: pickerPopoverProps, allowedPlacements: allowedPlacements, enableViewportConstraint: enableViewportConstraint, onOpenStateChanged: toggleBasePicker, onBlur: onBlur }) }));
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
99
|
* Internal Popover component that needs access to BasePickerContext.
|
|
@@ -101,7 +101,7 @@ export function BasePicker({ targetDOMNode, enableAutoUnmount = true, open = fal
|
|
|
101
101
|
* which requires being within a BasePickerProvider. Creating it as an internal
|
|
102
102
|
* component ensures proper context access while keeping the logic encapsulated.
|
|
103
103
|
*/
|
|
104
|
-
function InternalPopover({ value, defaultValue, textBoxRef, triggerRef, baseFormat = 'YYYY-MM-DD', renderPopoverContent, basePickerPopoverWrapperRef, handleOnKeyDown, pickerPopoverProps, onOpenStateChanged, allowedPlacements, ...popoverProps }) {
|
|
104
|
+
function InternalPopover({ value, defaultValue, textBoxRef, triggerRef, baseFormat = 'YYYY-MM-DD', renderPopoverContent, basePickerPopoverWrapperRef, handleOnKeyDown, pickerPopoverProps, onOpenStateChanged, allowedPlacements, enableViewportConstraint, ...popoverProps }) {
|
|
105
105
|
// Always call the hook at the top level to ensure consistent hook order
|
|
106
106
|
const { viewingValue, setViewingValue, setPendingFocusDate } = useBasePickerContext();
|
|
107
107
|
const handlePopoverOpen = useCallback((event) => {
|
|
@@ -129,7 +129,7 @@ function InternalPopover({ value, defaultValue, textBoxRef, triggerRef, baseForm
|
|
|
129
129
|
// Set the pending focus date to ensure proper focus after month change
|
|
130
130
|
setPendingFocusDate(formattedDate);
|
|
131
131
|
}, [textBoxRef, triggerRef, value, defaultValue, baseFormat, setViewingValue, setPendingFocusDate]);
|
|
132
|
-
return (_jsx(Popover, { enableAutomaticPortalTargetResolution: true, enableViewportConstraint: false, enableAutoFocusOnPopover: false, ...popoverProps, renderContent: () => (_jsx("div", { ref: basePickerPopoverWrapperRef, className: "mfui-BasePicker__popoverWrapper", onKeyDown: handleOnKeyDown, children: renderPopoverContent({
|
|
132
|
+
return (_jsx(Popover, { enableAutomaticPortalTargetResolution: true, enableViewportConstraint: enableViewportConstraint ?? false, enableAutoFocusOnPopover: false, ...popoverProps, renderContent: () => (_jsx("div", { ref: basePickerPopoverWrapperRef, className: "mfui-BasePicker__popoverWrapper", onKeyDown: handleOnKeyDown, children: renderPopoverContent({
|
|
133
133
|
viewingValue,
|
|
134
134
|
setViewingValue,
|
|
135
135
|
value: pickerPopoverProps.value,
|
|
@@ -118,4 +118,4 @@ export type BasePickerProps = {
|
|
|
118
118
|
* @see https://en.wikipedia.org/wiki/ISO_8601
|
|
119
119
|
*/
|
|
120
120
|
baseFormat?: string;
|
|
121
|
-
} & Omit<TextBoxProps, 'value' | 'defaultValue' | 'onChange'> & Pick<PopoverProps, 'allowedPlacements'>;
|
|
121
|
+
} & Omit<TextBoxProps, 'value' | 'defaultValue' | 'onChange'> & Pick<PopoverProps, 'allowedPlacements' | 'enableViewportConstraint'>;
|
|
@@ -3,4 +3,4 @@ import { type BaseRangePickerProps } from './BaseRangePicker.types';
|
|
|
3
3
|
* BaseRangePicker component
|
|
4
4
|
* A generic component for selecting a range of dates with configurable format
|
|
5
5
|
*/
|
|
6
|
-
export declare function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format, onOpenStateChanged, disableAutoOpen, onBlur, allowedPlacements, enableClearButton, clearButtonProps, minDate, maxDate, enableAutoUnmount, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale, }: BaseRangePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format, onOpenStateChanged, disableAutoOpen, onBlur, allowedPlacements, enableViewportConstraint, enableClearButton, clearButtonProps, minDate, maxDate, enableAutoUnmount, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale, }: BaseRangePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -9,7 +9,7 @@ import { dayjs } from '../../../utilities/date/dayjs';
|
|
|
9
9
|
/**
|
|
10
10
|
* Internal component that has access to BaseRangePickerContext
|
|
11
11
|
*/
|
|
12
|
-
function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid, format, enableClearButton, clearButtonProps, targetDOMNode, onBlur, isOpen, open, close, onOpenStateChanged, enableAutoUnmount, allowedPlacements, minWidth, renderPopoverContent, startInputProps = {}, endInputProps = {}, calendarLocale = 'ja', }) {
|
|
12
|
+
function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid, format, enableClearButton, clearButtonProps, targetDOMNode, onBlur, isOpen, open, close, onOpenStateChanged, enableAutoUnmount, allowedPlacements, enableViewportConstraint, minWidth, renderPopoverContent, startInputProps = {}, endInputProps = {}, calendarLocale = 'ja', }) {
|
|
13
13
|
const { setPendingFocusDate, temporaryStart, temporaryEnd, viewingMonth, setViewingMonth } = useBaseRangePickerContext();
|
|
14
14
|
// Custom onOpen handler to prevent auto-focus when clicking on input elements
|
|
15
15
|
// and implement smart focus management for calendar dates
|
|
@@ -49,7 +49,7 @@ function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid
|
|
|
49
49
|
setPendingFocusDate(formattedDate);
|
|
50
50
|
open();
|
|
51
51
|
}, [startInputRef, endInputRef, temporaryStart, temporaryEnd, setPendingFocusDate, open, format]);
|
|
52
|
-
return (_jsx(Popover, { enableAutomaticPortalTargetResolution: true, open: isOpen, allowedPlacements: allowedPlacements, minWidth: minWidth, renderTrigger: ({ setTriggerRef, handleTriggerBlur, handleTriggerKeyDown, openPopover }) => (_jsx(BaseRangePickerTrigger, { ref: setTriggerRef, disabled: disabled, invalid: invalid, format: format, enableClearValue: enableClearButton, clearButtonProps: clearButtonProps, startInputRef: startInputRef, endInputRef: endInputRef, startInputProps: startInputProps, endInputProps: endInputProps, popoverOpenFunction: openPopover, calendarLocale: calendarLocale, onBlur: handleTriggerBlur, onKeyDown: handleTriggerKeyDown })), renderContent: () => renderPopoverContent({
|
|
52
|
+
return (_jsx(Popover, { enableAutomaticPortalTargetResolution: true, open: isOpen, allowedPlacements: allowedPlacements, enableViewportConstraint: enableViewportConstraint, minWidth: minWidth, renderTrigger: ({ setTriggerRef, handleTriggerBlur, handleTriggerKeyDown, openPopover }) => (_jsx(BaseRangePickerTrigger, { ref: setTriggerRef, disabled: disabled, invalid: invalid, format: format, enableClearValue: enableClearButton, clearButtonProps: clearButtonProps, startInputRef: startInputRef, endInputRef: endInputRef, startInputProps: startInputProps, endInputProps: endInputProps, popoverOpenFunction: openPopover, calendarLocale: calendarLocale, onBlur: handleTriggerBlur, onKeyDown: handleTriggerKeyDown })), renderContent: () => renderPopoverContent({
|
|
53
53
|
viewingValue: viewingMonth,
|
|
54
54
|
setViewingValue: setViewingMonth,
|
|
55
55
|
value: [temporaryStart, temporaryEnd],
|
|
@@ -61,12 +61,12 @@ function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid
|
|
|
61
61
|
* BaseRangePicker component
|
|
62
62
|
* A generic component for selecting a range of dates with configurable format
|
|
63
63
|
*/
|
|
64
|
-
export function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format = 'YYYY/MM/DD', onOpenStateChanged, disableAutoOpen = false, onBlur, allowedPlacements, enableClearButton = false, clearButtonProps, minDate, maxDate, enableAutoUnmount = true, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale = 'ja', }) {
|
|
64
|
+
export function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format = 'YYYY/MM/DD', onOpenStateChanged, disableAutoOpen = false, onBlur, allowedPlacements, enableViewportConstraint, enableClearButton = false, clearButtonProps, minDate, maxDate, enableAutoUnmount = true, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale = 'ja', }) {
|
|
65
65
|
const { isOpen, open, close } = useDisclosure({ value: false });
|
|
66
66
|
const startInputRef = useRef(null);
|
|
67
67
|
const endInputRef = useRef(null);
|
|
68
68
|
// Default renderPopoverContent implementation for backward compatibility
|
|
69
69
|
const defaultRenderPopoverContent = useCallback(() => _jsx(BaseRangePickerPopover, { calendarLocale: calendarLocale }), [calendarLocale]);
|
|
70
70
|
const finalRenderPopoverContent = renderPopoverContent ?? defaultRenderPopoverContent;
|
|
71
|
-
return (_jsx(BaseRangePickerProvider, { value: value, defaultValue: defaultValue, disabled: disabled, format: format, isOpen: isOpen, open: open, close: close, disableAutoOpen: disableAutoOpen, minDate: minDate, maxDate: maxDate, onChange: onChange, children: _jsx(InternalBaseRangePicker, { startInputRef: startInputRef, endInputRef: endInputRef, disabled: disabled, invalid: invalid, format: format, enableClearButton: enableClearButton, clearButtonProps: clearButtonProps, targetDOMNode: targetDOMNode, allowedPlacements: allowedPlacements, enableAutoUnmount: enableAutoUnmount, minWidth: minWidth, startInputProps: startInputProps, endInputProps: endInputProps, isOpen: isOpen, open: open, close: close, renderPopoverContent: finalRenderPopoverContent, calendarLocale: calendarLocale, onBlur: onBlur, onOpenStateChanged: onOpenStateChanged }) }));
|
|
71
|
+
return (_jsx(BaseRangePickerProvider, { value: value, defaultValue: defaultValue, disabled: disabled, format: format, isOpen: isOpen, open: open, close: close, disableAutoOpen: disableAutoOpen, minDate: minDate, maxDate: maxDate, onChange: onChange, children: _jsx(InternalBaseRangePicker, { startInputRef: startInputRef, endInputRef: endInputRef, disabled: disabled, invalid: invalid, format: format, enableClearButton: enableClearButton, clearButtonProps: clearButtonProps, targetDOMNode: targetDOMNode, allowedPlacements: allowedPlacements, enableViewportConstraint: enableViewportConstraint, enableAutoUnmount: enableAutoUnmount, minWidth: minWidth, startInputProps: startInputProps, endInputProps: endInputProps, isOpen: isOpen, open: open, close: close, renderPopoverContent: finalRenderPopoverContent, calendarLocale: calendarLocale, onBlur: onBlur, onOpenStateChanged: onOpenStateChanged }) }));
|
|
72
72
|
}
|
|
@@ -128,6 +128,14 @@ export type BaseRangePickerProps = {
|
|
|
128
128
|
* ```
|
|
129
129
|
*/
|
|
130
130
|
allowedPlacements?: PopoverProps['allowedPlacements'];
|
|
131
|
+
/**
|
|
132
|
+
* Whether to enable viewport constraint for the popover.
|
|
133
|
+
* When true, the popover will be constrained within the visible viewport and a maxHeight is applied.
|
|
134
|
+
* Set to true when the picker is inside a constrained container like SidePane.
|
|
135
|
+
*
|
|
136
|
+
* @default true
|
|
137
|
+
*/
|
|
138
|
+
enableViewportConstraint?: PopoverProps['enableViewportConstraint'];
|
|
131
139
|
/**
|
|
132
140
|
* Whether to enable the clear button functionality.
|
|
133
141
|
* When enabled, a clear button will appear when there are values in the date inputs.
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import { Dropdown as DropdownIcon } from '@moneyforward/mfui-icons-react';
|
|
4
|
+
import { Dropdown as DropdownIcon, Error } from '@moneyforward/mfui-icons-react';
|
|
5
5
|
import { ClearButton } from '../shared/ClearButton';
|
|
6
6
|
import { FocusIndicator } from '../FocusIndicator';
|
|
7
7
|
import { selectBoxSlotRecipe } from '../../styled-system/recipes';
|
|
8
8
|
import { Typography } from '../Typography';
|
|
9
9
|
import { SearchBox } from '../SearchBox';
|
|
10
|
+
import { Button } from '../Button';
|
|
11
|
+
import { ProgressIndicator } from '../ProgressIndicator';
|
|
10
12
|
import { useSearchBox } from './hooks/useSearchBox';
|
|
11
13
|
import { Skeleton } from '../Skeleton';
|
|
12
14
|
import { useFocusTrap } from '../utilities/dom/useFocusTrap';
|
|
@@ -22,6 +24,7 @@ import { SelectBoxOptionComponent } from './SelectBoxOption';
|
|
|
22
24
|
import { SelectBoxOptionGroup } from './SelectBoxOptionGroup';
|
|
23
25
|
import { isOptionDisabled } from './utils/isSelectableOption';
|
|
24
26
|
import { useVirtualizedOptions } from './hooks/useVirtualizedOptions';
|
|
27
|
+
import { useInfiniteScroll } from './hooks/useInfiniteScroll';
|
|
25
28
|
const DEFAULT_SCROLL_OPTIONS = {
|
|
26
29
|
behavior: 'auto',
|
|
27
30
|
block: 'center',
|
|
@@ -35,7 +38,7 @@ const SKELETON_ITEM_COUNT = 4;
|
|
|
35
38
|
export const SelectBox = forwardRef((props, ref) => {
|
|
36
39
|
const { id, triggerProps, clearButtonProps, enableClearButton = false, size, options = [], defaultValue,
|
|
37
40
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
38
|
-
placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, onBlur, value, enableSearchOptions = false, notFoundMessage, searchBoxProps, loading = false, onSearchOptions, renderDisplayValue, renderOption, renderPopoverContent, enableAutoScrollToSelectedOption, popoverContentProps, popoverWrapperProps, onOpenStateChanged, showGroupOptionDivider, triggerWrapperProps, enableAutoUnmount = true, enableConstrainedPopoverWidth = false, enableVirtualization = false, virtualizationOptions, } = props;
|
|
41
|
+
placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, onBlur, value, enableSearchOptions = false, notFoundMessage, searchBoxProps, loading = false, onSearchOptions, renderDisplayValue, renderOption, renderPopoverContent, enableAutoScrollToSelectedOption, popoverContentProps, popoverWrapperProps, onOpenStateChanged, showGroupOptionDivider, triggerWrapperProps, enableAutoUnmount = true, enableConstrainedPopoverWidth = false, enableVirtualization = false, virtualizationOptions, infiniteScroll, } = props;
|
|
39
42
|
const classes = selectBoxSlotRecipe({ size, showGroupOptionDivider });
|
|
40
43
|
const triggerRef = useRef(null);
|
|
41
44
|
const listBoxId = useId();
|
|
@@ -117,6 +120,25 @@ export const SelectBox = forwardRef((props, ref) => {
|
|
|
117
120
|
estimateSize: virtualizationOptions?.estimateSize,
|
|
118
121
|
overscan: virtualizationOptions?.overscan,
|
|
119
122
|
});
|
|
123
|
+
// Extract infinite scroll configuration with defaults
|
|
124
|
+
const baseEnabledInfiniteScroll = infiniteScroll?.enabled ?? false;
|
|
125
|
+
const onLoadMore = infiniteScroll?.onLoadMore;
|
|
126
|
+
const hasNextPage = infiniteScroll?.hasNextPage ?? true;
|
|
127
|
+
const hasPreviousPage = infiniteScroll?.hasPreviousPage ?? true;
|
|
128
|
+
const infiniteScrollThreshold = infiniteScroll?.threshold ?? 100;
|
|
129
|
+
const infiniteScrollErrorMessage = infiniteScroll?.errorMessage ?? '読み込みに失敗しました';
|
|
130
|
+
const infiniteScrollRetryButtonText = infiniteScroll?.retryButtonText ?? '再読み込み';
|
|
131
|
+
// Initialize infinite scroll
|
|
132
|
+
const { isLoading: isInfiniteScrollLoading, error: infiniteScrollError, handleScroll: handleInfiniteScroll, retryLoad: retryInfiniteScroll, } = useInfiniteScroll({
|
|
133
|
+
onLoadMore,
|
|
134
|
+
hasNextPage,
|
|
135
|
+
hasPreviousPage,
|
|
136
|
+
}, {
|
|
137
|
+
enabled: baseEnabledInfiniteScroll,
|
|
138
|
+
threshold: infiniteScrollThreshold,
|
|
139
|
+
});
|
|
140
|
+
// Disable infinite scroll when there's an error
|
|
141
|
+
const enableInfiniteScroll = baseEnabledInfiniteScroll && !infiniteScrollError;
|
|
120
142
|
const handleSelectOption = useCallback((option) => {
|
|
121
143
|
if (isOptionDisabled(option))
|
|
122
144
|
return;
|
|
@@ -266,7 +288,23 @@ export const SelectBox = forwardRef((props, ref) => {
|
|
|
266
288
|
: options.length > 0
|
|
267
289
|
? notFoundMessage
|
|
268
290
|
: emptyMessage }) })), [classes.emptyMessage, enableSearchOptions, searchText, notFoundMessage, options.length, emptyMessage]);
|
|
269
|
-
|
|
291
|
+
// Render infinite scroll error message with retry button
|
|
292
|
+
const renderInfiniteScrollError = useCallback(() => infiniteScrollError ? (_jsxs("li", { className: cx(classes.infiniteScrollError, 'mfui-SelectBox__infiniteScrollError'), role: "alert", children: [_jsxs("div", { className: cx(classes.infiniteScrollErrorMessage, 'mfui-SelectBox__infiniteScrollErrorMessage'), "aria-live": "polite", children: [_jsx(Error, { "aria-hidden": true, className: cx(classes.infiniteScrollErrorIcon, 'mfui-SelectBox__infiniteScrollErrorIcon') }), _jsx(Typography, { variant: "body", children: infiniteScrollErrorMessage })] }), _jsx("div", { className: cx(classes.infiniteScrollErrorButton, 'mfui-SelectBox__infiniteScrollErrorButton'), children: _jsx(Button, { size: "small", onClick: (event) => {
|
|
293
|
+
event.stopPropagation();
|
|
294
|
+
retryInfiniteScroll();
|
|
295
|
+
}, children: infiniteScrollRetryButtonText }) })] })) : null, [
|
|
296
|
+
infiniteScrollError,
|
|
297
|
+
classes.infiniteScrollError,
|
|
298
|
+
classes.infiniteScrollErrorIcon,
|
|
299
|
+
classes.infiniteScrollErrorMessage,
|
|
300
|
+
classes.infiniteScrollErrorButton,
|
|
301
|
+
retryInfiniteScroll,
|
|
302
|
+
infiniteScrollErrorMessage,
|
|
303
|
+
infiniteScrollRetryButtonText,
|
|
304
|
+
]);
|
|
305
|
+
// Render infinite scroll loading indicator
|
|
306
|
+
const renderInfiniteScrollLoading = useCallback(() => isInfiniteScrollLoading && enableInfiniteScroll ? (_jsx("div", { className: cx(classes.infiniteScrollLoading, 'mfui-SelectBox__infiniteScrollLoading'), children: _jsx(ProgressIndicator, {}) })) : null, [isInfiniteScrollLoading, enableInfiniteScroll, classes.infiniteScrollLoading]);
|
|
307
|
+
const optionsNode = (_jsx("div", { ref: scrollWrapperRef, className: cx(classes.scrollWrapper, 'mfui-SelectBox__scrollWrapper'), onScroll: enableInfiniteScroll ? handleInfiniteScroll : undefined, children: _jsx("ul", { role: "listbox", id: listBoxId, className: cx(classes.listBox, 'mfui-SelectBox__listBox'), tabIndex: -1, style: isVirtualized
|
|
270
308
|
? {
|
|
271
309
|
height: `${String(totalSize)}px`,
|
|
272
310
|
position: 'relative',
|
|
@@ -275,10 +313,10 @@ export const SelectBox = forwardRef((props, ref) => {
|
|
|
275
313
|
? Array.from({ length: SKELETON_ITEM_COUNT }).map((_, index) => (_jsx("li", { className: cx(classes.skeletonItem, 'mfui-SelectBox__skeletonItem'), children: _jsx(Skeleton, {}) }, index)))
|
|
276
314
|
: isVirtualized && virtualItems.length > 0
|
|
277
315
|
? // Virtualized rendering with group support
|
|
278
|
-
renderVirtualizedItems()
|
|
316
|
+
[...renderVirtualizedItems(), renderInfiniteScrollLoading(), renderInfiniteScrollError()].filter(Boolean)
|
|
279
317
|
: filteredOptions.length > 0
|
|
280
|
-
? renderNonVirtualizedItems()
|
|
281
|
-
: renderEmptyMessage() }) }));
|
|
318
|
+
? [...renderNonVirtualizedItems(), renderInfiniteScrollLoading(), renderInfiniteScrollError()].filter(Boolean)
|
|
319
|
+
: [renderEmptyMessage(), renderInfiniteScrollError()].filter(Boolean) }) }));
|
|
282
320
|
const handleClearValue = () => {
|
|
283
321
|
setLocalSelectedOption(null);
|
|
284
322
|
onChange?.(null);
|
|
@@ -290,7 +328,7 @@ export const SelectBox = forwardRef((props, ref) => {
|
|
|
290
328
|
return (_jsx(Popover, { open: isOptionPanelOpen, targetDOMNode: targetDOMNode, minWidth: popoverContentProps?.minWidth, maxHeight: popoverWrapperProps?.maxHeight, allowedPlacements: popoverContentProps?.allowedPlacements, enableConstrainedContentWidth: enableConstrainedPopoverWidth, renderContent: () => (_jsx("div", { ref: optionPanelRef, id: listBoxId, className: cx(classes.optionPanel, 'mfui-SelectBox__optionPanel', popoverContentProps?.className), tabIndex: -1, onKeyDown: handleKeyDownMenu, children: renderPopoverContent ? (renderPopoverContent({ searchNode, optionsNode })) : (_jsxs(_Fragment, { children: [searchNode, optionsNode] })) })), renderTrigger: ({ setTriggerRef, togglePopover, isPopoverOpen, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, ...triggerWrapperProps, className: cx(classes.triggerWrapper, 'mfui-SelectBox__triggerWrapper', triggerWrapperProps?.className), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, id: id, type: "button", role: "combobox", disabled: disabled, "aria-label": triggerProps?.['aria-label'], "aria-controls": listBoxId, "aria-expanded": isPopoverOpen, "aria-haspopup": "listbox", "aria-invalid": invalid, className: cx(classes.trigger, 'mfui-SelectBox__trigger', triggerProps?.className), "data-placeholder": !!placeholder && !localSelectedOption, "data-selected": !!localSelectedOption, "data-mfui-has-clear-button": showClearButton, onClick: togglePopover, onKeyDown: (event) => {
|
|
291
329
|
handleTypeAhead(event.nativeEvent);
|
|
292
330
|
handleTriggerKeyDown(event);
|
|
293
|
-
}, onBlur: handleTriggerBlur, children: [_jsx("span", { "data-mfui-content": "select-box-trigger-display-value", children: renderTriggerLabel() }), _jsx(DropdownIcon, {}), _jsx("input", { ref: ref, type: "hidden", value: localSelectedOption?.value ?? '', name: name, disabled: disabled })] }) }), showClearButton ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-SelectBox__clearButtonWrapper'), children: _jsx(ClearButton, { size: size === 'small' ? 'small' : 'default', "aria-label": clearButtonProps?.['aria-label'] ?? '値をクリアする', "data-mfui-content": "select-box-clear-button", onClick: (event) => {
|
|
331
|
+
}, onBlur: handleTriggerBlur, children: [_jsx("span", { "data-mfui-content": "select-box-trigger-display-value", children: renderTriggerLabel() }), _jsx(DropdownIcon, {}), _jsx("input", { ref: ref, type: "hidden", value: localSelectedOption?.value ?? '', name: name, disabled: disabled })] }) }), showClearButton ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-SelectBox__clearButtonWrapper'), children: _jsx(ClearButton, { size: size === 'small' ? 'small' : size === 'large' ? 'large' : 'default', "aria-label": clearButtonProps?.['aria-label'] ?? '値をクリアする', "data-mfui-content": "select-box-clear-button", onClick: (event) => {
|
|
294
332
|
event.stopPropagation();
|
|
295
333
|
handleClearValue();
|
|
296
334
|
} }) })) : null] })), contentProps: { className: classes.popover }, enableAutoUnmount: enableAutoUnmount, onOpenStateChanged: toggleOptionPanel, onBlur: onBlur }));
|
|
@@ -3,8 +3,61 @@ import { type VirtualizerOptions } from '@tanstack/react-virtual';
|
|
|
3
3
|
import { type SelectBoxSlotRecipeVariant } from '../../styled-system/recipes';
|
|
4
4
|
import { type SearchBoxProps } from '../SearchBox';
|
|
5
5
|
import { type PopoverProps } from '../Popover';
|
|
6
|
+
import { type InfiniteScrollDirection } from './hooks/useInfiniteScroll';
|
|
6
7
|
type AllowedValueTypes = string | number | undefined;
|
|
7
8
|
export type VirtualOptionTypes = Pick<VirtualizerOptions<HTMLElement, Element>, 'estimateSize' | 'overscan'>;
|
|
9
|
+
export type InfiniteScrollConfig = {
|
|
10
|
+
/**
|
|
11
|
+
* Enable infinite scroll functionality.
|
|
12
|
+
* When enabled, additional options can be loaded dynamically when scrolling.
|
|
13
|
+
*
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Callback executed when more options need to be loaded.
|
|
19
|
+
* Called when user scrolls near the top or bottom of the options list.
|
|
20
|
+
*
|
|
21
|
+
* @param direction - The direction of loading ('forward' for bottom, 'backward' for top)
|
|
22
|
+
*/
|
|
23
|
+
onLoadMore?: (direction: InfiniteScrollDirection) => Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Whether there are more options available to load in the forward direction (bottom).
|
|
26
|
+
* Used to determine if infinite scroll should trigger when scrolling down.
|
|
27
|
+
*
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
hasNextPage?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Whether there are more options available to load in the backward direction (top).
|
|
33
|
+
* Used to determine if infinite scroll should trigger when scrolling up.
|
|
34
|
+
*
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
hasPreviousPage?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The scroll threshold in pixels for triggering infinite scroll.
|
|
40
|
+
* When the scroll position is within this distance from the top or bottom,
|
|
41
|
+
* the onLoadMore callback will be triggered.
|
|
42
|
+
*
|
|
43
|
+
* @default 100
|
|
44
|
+
*/
|
|
45
|
+
threshold?: number;
|
|
46
|
+
/**
|
|
47
|
+
* The error message to display when loading fails.
|
|
48
|
+
* This message supports internationalization.
|
|
49
|
+
*
|
|
50
|
+
* @default "Failed to load data"
|
|
51
|
+
*/
|
|
52
|
+
errorMessage?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The text for the retry button when loading fails.
|
|
55
|
+
* This message supports internationalization.
|
|
56
|
+
*
|
|
57
|
+
* @default "Retry"
|
|
58
|
+
*/
|
|
59
|
+
retryButtonText?: string;
|
|
60
|
+
};
|
|
8
61
|
export type BasedAdditionalProps = Record<string, unknown>;
|
|
9
62
|
export type SelectBoxOption<T extends AllowedValueTypes = string, AdditionalProps extends BasedAdditionalProps = BasedAdditionalProps> = ({
|
|
10
63
|
/**
|
|
@@ -351,5 +404,31 @@ export type SelectBoxProps<T extends AllowedValueTypes = string, AdditionalProps
|
|
|
351
404
|
* ```
|
|
352
405
|
*/
|
|
353
406
|
virtualizationOptions?: VirtualOptionTypes;
|
|
407
|
+
/**
|
|
408
|
+
* Infinite scroll configuration.
|
|
409
|
+
* When provided, enables infinite scroll functionality for loading additional options dynamically.
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```tsx
|
|
413
|
+
* <SelectBox
|
|
414
|
+
* infiniteScroll={{
|
|
415
|
+
* enabled: true,
|
|
416
|
+
* onLoadMore: async (direction) => {
|
|
417
|
+
* if (direction === 'forward') {
|
|
418
|
+
* const nextOptions = await loadNextPage();
|
|
419
|
+
* setOptions(prev => [...prev, ...nextOptions]);
|
|
420
|
+
* } else {
|
|
421
|
+
* const prevOptions = await loadPreviousPage();
|
|
422
|
+
* setOptions(prev => [...prevOptions, ...prev]);
|
|
423
|
+
* }
|
|
424
|
+
* },
|
|
425
|
+
* hasNextPage: hasMore,
|
|
426
|
+
* hasPreviousPage: hasPrevious,
|
|
427
|
+
* threshold: 50 // Trigger when within 50px of edges
|
|
428
|
+
* }}
|
|
429
|
+
* />
|
|
430
|
+
* ```
|
|
431
|
+
*/
|
|
432
|
+
infiniteScroll?: InfiniteScrollConfig;
|
|
354
433
|
};
|
|
355
|
-
export {};
|
|
434
|
+
export { type InfiniteScrollDirection } from './hooks/useInfiniteScroll';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type InfiniteScrollDirection = 'forward' | 'backward';
|
|
2
|
+
export type InfiniteScrollCallbacks = {
|
|
3
|
+
onLoadMore?: (direction: InfiniteScrollDirection) => Promise<void>;
|
|
4
|
+
hasNextPage?: boolean;
|
|
5
|
+
hasPreviousPage?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type InfiniteScrollError = {
|
|
8
|
+
direction: InfiniteScrollDirection;
|
|
9
|
+
error: Error;
|
|
10
|
+
};
|
|
11
|
+
export type UseInfiniteScrollOptions = {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
threshold?: number;
|
|
14
|
+
};
|
|
15
|
+
export type UseInfiniteScrollReturn = {
|
|
16
|
+
isLoading: boolean;
|
|
17
|
+
error: InfiniteScrollError | null;
|
|
18
|
+
handleScroll: (event: React.UIEvent<HTMLElement>) => void;
|
|
19
|
+
retryLoad: () => void;
|
|
20
|
+
clearError: () => void;
|
|
21
|
+
};
|
|
22
|
+
export declare const useInfiniteScroll: (callbacks: InfiniteScrollCallbacks, options?: UseInfiniteScrollOptions) => UseInfiniteScrollReturn;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
|
+
export const useInfiniteScroll = (callbacks, options = {}) => {
|
|
3
|
+
const { enabled = true, threshold = 100 } = options;
|
|
4
|
+
const { onLoadMore, hasNextPage = true, hasPreviousPage = true } = callbacks;
|
|
5
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
const isLoadingRef = useRef(false);
|
|
8
|
+
const executeLoad = useCallback(async (direction, forceLoad = false) => {
|
|
9
|
+
if ((!enabled && !forceLoad) || !onLoadMore || isLoadingRef.current)
|
|
10
|
+
return;
|
|
11
|
+
if (direction === 'forward' && !hasNextPage)
|
|
12
|
+
return;
|
|
13
|
+
if (direction === 'backward' && !hasPreviousPage)
|
|
14
|
+
return;
|
|
15
|
+
try {
|
|
16
|
+
isLoadingRef.current = true;
|
|
17
|
+
setIsLoading(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
await onLoadMore(direction);
|
|
20
|
+
}
|
|
21
|
+
catch (error_) {
|
|
22
|
+
const errorObject = error_ instanceof Error ? error_ : new Error('Unknown error');
|
|
23
|
+
setError({ direction, error: errorObject });
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
isLoadingRef.current = false;
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
}
|
|
29
|
+
}, [enabled, onLoadMore, hasNextPage, hasPreviousPage]);
|
|
30
|
+
const handleScroll = useCallback((event) => {
|
|
31
|
+
if (!enabled || isLoadingRef.current)
|
|
32
|
+
return;
|
|
33
|
+
const target = event.currentTarget;
|
|
34
|
+
const { scrollTop, scrollHeight, clientHeight } = target;
|
|
35
|
+
const nearTop = scrollTop <= threshold;
|
|
36
|
+
const nearBottom = scrollTop + clientHeight >= scrollHeight - threshold;
|
|
37
|
+
if (nearBottom && hasNextPage) {
|
|
38
|
+
executeLoad('forward').catch(() => {
|
|
39
|
+
// Error handling is done within executeLoad
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else if (nearTop && hasPreviousPage) {
|
|
43
|
+
executeLoad('backward').catch(() => {
|
|
44
|
+
// Error handling is done within executeLoad
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}, [enabled, threshold, hasNextPage, hasPreviousPage, executeLoad]);
|
|
48
|
+
const retryLoad = useCallback(() => {
|
|
49
|
+
if (error) {
|
|
50
|
+
executeLoad(error.direction, true).catch(() => {
|
|
51
|
+
// Error handling is done within executeLoad
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}, [error, executeLoad]);
|
|
55
|
+
const clearError = useCallback(() => {
|
|
56
|
+
setError(null);
|
|
57
|
+
}, []);
|
|
58
|
+
return {
|
|
59
|
+
isLoading,
|
|
60
|
+
error,
|
|
61
|
+
handleScroll,
|
|
62
|
+
retryLoad,
|
|
63
|
+
clearError,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The general-purpose ToggleSwitch component.
|
|
3
|
+
* This component provides a switch to toggle between ON and OFF states.
|
|
4
|
+
*
|
|
5
|
+
* This component extends the props of `<input type="checkbox">` element.
|
|
6
|
+
*
|
|
7
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
|
|
8
|
+
*/
|
|
9
|
+
export declare const ToggleSwitch: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useState } from 'react';
|
|
4
|
+
import { cx } from '../../styled-system/css';
|
|
5
|
+
import { toggleSwitchSlotRecipe } from '../../styled-system/recipes';
|
|
6
|
+
import { FocusIndicator } from '../FocusIndicator/FocusIndicator';
|
|
7
|
+
/**
|
|
8
|
+
* The general-purpose ToggleSwitch component.
|
|
9
|
+
* This component provides a switch to toggle between ON and OFF states.
|
|
10
|
+
*
|
|
11
|
+
* This component extends the props of `<input type="checkbox">` element.
|
|
12
|
+
*
|
|
13
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
|
|
14
|
+
*/
|
|
15
|
+
export const ToggleSwitch = forwardRef(({ className, checked, defaultChecked, disabled, onChange, ...props }, ref) => {
|
|
16
|
+
const [localChecked, setLocalChecked] = useState(!!defaultChecked);
|
|
17
|
+
const isControlled = checked !== undefined;
|
|
18
|
+
const classes = toggleSwitchSlotRecipe();
|
|
19
|
+
const isChecked = isControlled ? checked : localChecked;
|
|
20
|
+
return (_jsx(FocusIndicator, { children: _jsxs("span", { className: cx(classes.root, 'mfui-ToggleSwitch__root', className), children: [_jsx("span", { className: cx(classes.handle, 'mfui-ToggleSwitch__handle'), "data-mfui-content": "toggle-handle" }), _jsx("input", { ref: ref, type: "checkbox", role: "switch", checked: isChecked, disabled: disabled, className: cx(classes.input, 'mfui-ToggleSwitch__input'), "data-mfui-content": "toggle-input", onChange: (event) => {
|
|
21
|
+
onChange?.(event);
|
|
22
|
+
// If the event is defaultPrevented in "onChange" prop via "e.preventDefault()", do not update local
|
|
23
|
+
// states.
|
|
24
|
+
if (event.defaultPrevented) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!isControlled) {
|
|
28
|
+
setLocalChecked(event.target.checked);
|
|
29
|
+
}
|
|
30
|
+
}, ...props })] }) }));
|
|
31
|
+
});
|
|
32
|
+
ToggleSwitch.displayName = 'ToggleSwitch';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -37,19 +37,45 @@ export const useFixedColumns = ({ enableRowSelection = false, columnIndex, leftF
|
|
|
37
37
|
if (!cellRef.current)
|
|
38
38
|
return;
|
|
39
39
|
const cell = cellRef.current;
|
|
40
|
+
const row = cell.closest('tr');
|
|
40
41
|
const table = cell.closest('table');
|
|
41
|
-
if (!table)
|
|
42
|
+
if (!row || !table)
|
|
42
43
|
return;
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
const calculatePosition = () => {
|
|
45
|
+
const cells = [...row.querySelectorAll('th, td')];
|
|
46
|
+
const cellIndex = cells.indexOf(cell);
|
|
47
|
+
if (cellIndex === -1)
|
|
48
|
+
return;
|
|
49
|
+
// Calculate left position by summing widths of previous cells
|
|
50
|
+
let leftPosition = 0;
|
|
51
|
+
for (let i = 0; i < cellIndex; i++) {
|
|
52
|
+
const cellElement = cells[i];
|
|
53
|
+
if (cellElement) {
|
|
54
|
+
leftPosition += cellElement.offsetWidth;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Calculate right position by summing widths of following cells
|
|
58
|
+
let rightPosition = 0;
|
|
59
|
+
for (let i = cellIndex + 1; i < cells.length; i++) {
|
|
60
|
+
const cellElement = cells[i];
|
|
61
|
+
if (cellElement) {
|
|
62
|
+
rightPosition += cellElement.offsetWidth;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
setCellRect({
|
|
66
|
+
left: leftPosition,
|
|
67
|
+
right: rightPosition,
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
calculatePosition();
|
|
71
|
+
// Recalculate on table resize (e.g., when content changes after sorting)
|
|
72
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
73
|
+
calculatePosition();
|
|
52
74
|
});
|
|
75
|
+
resizeObserver.observe(table);
|
|
76
|
+
return () => {
|
|
77
|
+
resizeObserver.disconnect();
|
|
78
|
+
};
|
|
53
79
|
}, []);
|
|
54
80
|
useLayoutEffect(() => {
|
|
55
81
|
if (isCheckboxColumn && leftFixedColumnIndex !== undefined) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { withoutSpace } from '../helpers.js';
|
|
2
|
-
const conditionsStr = "_hover,_focus,_focusWithin,_focusVisible,_disabled,_active,_visited,_target,_readOnly,_readWrite,_empty,_checked,_enabled,_expanded,_highlighted,_complete,_incomplete,_dragging,_before,_after,_firstLetter,_firstLine,_marker,_selection,_file,_backdrop,_first,_last,_only,_even,_odd,_firstOfType,_lastOfType,_onlyOfType,_peerFocus,_peerHover,_peerActive,_peerFocusWithin,_peerFocusVisible,_peerDisabled,_peerChecked,_peerInvalid,_peerExpanded,_peerPlaceholderShown,_groupFocus,_groupHover,_groupActive,_groupFocusWithin,_groupFocusVisible,_groupDisabled,_groupChecked,_groupExpanded,_groupInvalid,_indeterminate,_required,_valid,_invalid,_autofill,_inRange,_outOfRange,_placeholder,_placeholderShown,_pressed,_selected,_grabbed,_underValue,_overValue,_atValue,_default,_optional,_open,_closed,_fullscreen,_loading,_hidden,_current,_currentPage,_currentStep,_today,_unavailable,_rangeStart,_rangeEnd,_now,_topmost,_motionReduce,_motionSafe,_print,_landscape,_portrait,_dark,_light,_osDark,_osLight,_highContrast,_lessContrast,_moreContrast,_ltr,_rtl,_scrollbar,_scrollbarThumb,_scrollbarTrack,_horizontal,_vertical,_icon,_starting,_noscript,_invertedColors,_notDisabled,_notFocused,_focusable,_focusInputInside,_hasCheckboxInside,_hasDisabledCheckboxInside,_hasFocusedCheckboxInside,_hasFocusedCheckedCheckboxInside,_hasCheckedCheckboxInside,_hasRadioButtonInside,_hasFocusedRadioButtonInside,_hasFocusedCheckedRadioButtonInside,_hasDisabledRadioButtonInside,_hasCheckedRadioButtonInside,sm,smOnly,smDown,lg,lgOnly,lgDown,smToLg,base";
|
|
2
|
+
const conditionsStr = "_hover,_focus,_focusWithin,_focusVisible,_disabled,_active,_visited,_target,_readOnly,_readWrite,_empty,_checked,_enabled,_expanded,_highlighted,_complete,_incomplete,_dragging,_before,_after,_firstLetter,_firstLine,_marker,_selection,_file,_backdrop,_first,_last,_only,_even,_odd,_firstOfType,_lastOfType,_onlyOfType,_peerFocus,_peerHover,_peerActive,_peerFocusWithin,_peerFocusVisible,_peerDisabled,_peerChecked,_peerInvalid,_peerExpanded,_peerPlaceholderShown,_groupFocus,_groupHover,_groupActive,_groupFocusWithin,_groupFocusVisible,_groupDisabled,_groupChecked,_groupExpanded,_groupInvalid,_indeterminate,_required,_valid,_invalid,_autofill,_inRange,_outOfRange,_placeholder,_placeholderShown,_pressed,_selected,_grabbed,_underValue,_overValue,_atValue,_default,_optional,_open,_closed,_fullscreen,_loading,_hidden,_current,_currentPage,_currentStep,_today,_unavailable,_rangeStart,_rangeEnd,_now,_topmost,_motionReduce,_motionSafe,_print,_landscape,_portrait,_dark,_light,_osDark,_osLight,_highContrast,_lessContrast,_moreContrast,_ltr,_rtl,_scrollbar,_scrollbarThumb,_scrollbarTrack,_horizontal,_vertical,_icon,_starting,_noscript,_invertedColors,_notDisabled,_notFocused,_focusable,_focusInputInside,_hasCheckboxInside,_hasDisabledCheckboxInside,_hasFocusedCheckboxInside,_hasFocusedCheckedCheckboxInside,_hasCheckedCheckboxInside,_hasRadioButtonInside,_hasFocusedRadioButtonInside,_hasFocusedCheckedRadioButtonInside,_hasDisabledRadioButtonInside,_hasCheckedRadioButtonInside,_hasToggleSwitchInside,_hasFocusedToggleSwitchInside,_hasFocusedCheckedToggleSwitchInside,_hasDisabledToggleSwitchInside,_hasCheckedToggleSwitchInside,sm,smOnly,smDown,lg,lgOnly,lgDown,smToLg,base";
|
|
3
3
|
const conditions = new Set(conditionsStr.split(','));
|
|
4
4
|
const conditionRegex = /^@|&|&$/;
|
|
5
5
|
export function isCondition(value) {
|