@seed-design/react 2.1.0 → 2.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.
Files changed (46) hide show
  1. package/lib/components/DatePicker/DatePicker.cjs +351 -0
  2. package/lib/components/DatePicker/DatePicker.d.ts +39 -0
  3. package/lib/components/DatePicker/DatePicker.d.ts.map +1 -0
  4. package/lib/components/DatePicker/DatePicker.js +345 -0
  5. package/lib/components/DatePicker/DatePicker.test.d.ts +2 -0
  6. package/lib/components/DatePicker/DatePicker.test.d.ts.map +1 -0
  7. package/lib/components/DatePicker/index.cjs +62 -0
  8. package/lib/components/DatePicker/index.d.ts +3 -0
  9. package/lib/components/DatePicker/index.d.ts.map +1 -0
  10. package/lib/components/DatePicker/index.js +3 -0
  11. package/lib/components/QuantityPicker/QuantityPicker.d.ts +13 -1
  12. package/lib/components/QuantityPicker/QuantityPicker.d.ts.map +1 -1
  13. package/lib/components/TimePicker/TimePicker.cjs +97 -0
  14. package/lib/components/TimePicker/TimePicker.d.ts +28 -0
  15. package/lib/components/TimePicker/TimePicker.d.ts.map +1 -0
  16. package/lib/components/TimePicker/TimePicker.js +94 -0
  17. package/lib/components/TimePicker/TimePicker.test.d.ts +2 -0
  18. package/lib/components/TimePicker/TimePicker.test.d.ts.map +1 -0
  19. package/lib/components/TimePicker/index.cjs +3 -0
  20. package/lib/components/TimePicker/index.d.ts +2 -0
  21. package/lib/components/TimePicker/index.d.ts.map +1 -0
  22. package/lib/components/TimePicker/index.js +2 -0
  23. package/lib/components/index.cjs +63 -1
  24. package/lib/components/index.d.ts +2 -0
  25. package/lib/components/index.d.ts.map +1 -1
  26. package/lib/components/index.js +5 -2
  27. package/lib/components/private/WheelPicker.cjs +64 -0
  28. package/lib/components/private/WheelPicker.d.ts +16 -0
  29. package/lib/components/private/WheelPicker.d.ts.map +1 -0
  30. package/lib/components/private/WheelPicker.js +59 -0
  31. package/lib/components/private/WheelPicker.test.d.ts +2 -0
  32. package/lib/components/private/WheelPicker.test.d.ts.map +1 -0
  33. package/lib/index.cjs +63 -1
  34. package/lib/index.js +5 -2
  35. package/package.json +8 -5
  36. package/src/components/DatePicker/DatePicker.test.tsx +234 -0
  37. package/src/components/DatePicker/DatePicker.tsx +574 -0
  38. package/src/components/DatePicker/index.ts +34 -0
  39. package/src/components/QuantityPicker/QuantityPicker.test.tsx +10 -0
  40. package/src/components/QuantityPicker/QuantityPicker.tsx +14 -2
  41. package/src/components/TimePicker/TimePicker.test.tsx +166 -0
  42. package/src/components/TimePicker/TimePicker.tsx +163 -0
  43. package/src/components/TimePicker/index.ts +6 -0
  44. package/src/components/index.ts +2 -0
  45. package/src/components/private/WheelPicker.test.tsx +54 -0
  46. package/src/components/private/WheelPicker.tsx +110 -0
@@ -0,0 +1,166 @@
1
+ import { act, fireEvent, render } from "@testing-library/react";
2
+ import { afterAll, beforeAll, describe, expect, it, jest, mock } from "bun:test";
3
+ import { TimePicker } from "./TimePicker";
4
+
5
+ describe("TimePicker", () => {
6
+ const originalScrollTo = HTMLElement.prototype.scrollTo;
7
+ const originalMatchMedia = window.matchMedia;
8
+ const originalRequestAnimationFrame = window.requestAnimationFrame;
9
+ const originalCancelAnimationFrame = window.cancelAnimationFrame;
10
+
11
+ beforeAll(() => {
12
+ HTMLElement.prototype.scrollTo = function scrollTo(options) {
13
+ if (typeof options === "object" && options.top !== undefined) {
14
+ this.scrollTop = options.top;
15
+ }
16
+ };
17
+ window.matchMedia = mock(() => ({
18
+ matches: false,
19
+ media: "",
20
+ onchange: null,
21
+ addListener: () => {},
22
+ removeListener: () => {},
23
+ addEventListener: () => {},
24
+ removeEventListener: () => {},
25
+ dispatchEvent: () => false,
26
+ }));
27
+ window.requestAnimationFrame = (callback) => {
28
+ callback(0);
29
+ return 1;
30
+ };
31
+ window.cancelAnimationFrame = () => {};
32
+ });
33
+
34
+ afterAll(() => {
35
+ HTMLElement.prototype.scrollTo = originalScrollTo;
36
+ window.matchMedia = originalMatchMedia;
37
+ window.requestAnimationFrame = originalRequestAnimationFrame;
38
+ window.cancelAnimationFrame = originalCancelAnimationFrame;
39
+ });
40
+
41
+ it("기본 접근성 이름과 12시간제 컬럼을 제공한다", () => {
42
+ const { getByRole, getAllByRole } = render(
43
+ <TimePicker defaultValue={{ hour: 10, minute: 5 }} />,
44
+ );
45
+
46
+ expect(getByRole("group")).toHaveAccessibleName("시간 선택");
47
+ const columns = getAllByRole("spinbutton");
48
+ expect(columns.map((column) => column.getAttribute("aria-label"))).toEqual([
49
+ "오전/오후",
50
+ "시",
51
+ "분",
52
+ ]);
53
+ expect(columns.map((column) => column.getAttribute("aria-valuetext"))).toEqual([
54
+ "오전",
55
+ "10",
56
+ "05",
57
+ ]);
58
+ });
59
+
60
+ it("locale에 따라 컬럼 순서를 변경한다", () => {
61
+ const { getAllByRole, getByRole } = render(<TimePicker locale="en-US" />);
62
+
63
+ expect(getByRole("group")).toHaveAccessibleName("Select time");
64
+ expect(getAllByRole("spinbutton").map((column) => column.getAttribute("aria-label"))).toEqual([
65
+ "Hour",
66
+ "Minute",
67
+ "AM/PM",
68
+ ]);
69
+ });
70
+
71
+ it("일본어 locale에 맞는 기본 접근성 이름을 제공한다", () => {
72
+ const { getAllByRole, getByRole } = render(<TimePicker locale="ja-JP" />);
73
+
74
+ expect(getByRole("group")).toHaveAccessibleName("時刻を選択");
75
+ expect(getAllByRole("spinbutton").map((column) => column.getAttribute("aria-label"))).toEqual([
76
+ "午前/午後",
77
+ "時",
78
+ "分",
79
+ ]);
80
+ });
81
+
82
+ it("기본 접근성 이름을 명시적인 값으로 재정의한다", () => {
83
+ const { getAllByRole, getByRole } = render(
84
+ <TimePicker
85
+ locale="en-US"
86
+ aria-label="Appointment time"
87
+ periodAriaLabel="Period"
88
+ hourAriaLabel="Hours"
89
+ minuteAriaLabel="Minutes"
90
+ />,
91
+ );
92
+
93
+ expect(getByRole("group")).toHaveAccessibleName("Appointment time");
94
+ expect(getAllByRole("spinbutton").map((column) => column.getAttribute("aria-label"))).toEqual([
95
+ "Hours",
96
+ "Minutes",
97
+ "Period",
98
+ ]);
99
+ });
100
+
101
+ it("controlled minute도 가장 가까운 minuteStep으로 반올림한다", () => {
102
+ const { getAllByRole } = render(<TimePicker value={{ hour: 9, minute: 13 }} minuteStep={5} />);
103
+ const minuteColumn = getAllByRole("spinbutton")[2];
104
+
105
+ expect(minuteColumn).toHaveAttribute("aria-valuetext", "15");
106
+ expect(minuteColumn.querySelector('[data-wheel-picker-value="13"]')).toBeNull();
107
+ });
108
+
109
+ it("Wheel 정착 후 TimePickerValue를 전달한다", () => {
110
+ jest.useFakeTimers();
111
+ try {
112
+ const onValueChange = mock(() => {});
113
+ const { getAllByRole } = render(
114
+ <TimePicker
115
+ value={{ hour: 10, minute: 13 }}
116
+ minuteStep={10}
117
+ onValueChange={onValueChange}
118
+ />,
119
+ );
120
+ const hourColumn = getAllByRole("spinbutton")[1];
121
+
122
+ fireEvent.keyDown(hourColumn, { key: "ArrowDown" });
123
+ act(() => jest.advanceTimersByTime(120));
124
+
125
+ expect(onValueChange).toHaveBeenCalledWith({ hour: 11, minute: 10 });
126
+ } finally {
127
+ jest.useRealTimers();
128
+ }
129
+ });
130
+
131
+ it("Hour가 12시 경계를 지나면 Period를 부드럽게 전환한다", () => {
132
+ jest.useFakeTimers();
133
+ const previousScrollTo = HTMLElement.prototype.scrollTo;
134
+ const smoothScrolls: Array<{ element: HTMLElement; top: number }> = [];
135
+ try {
136
+ HTMLElement.prototype.scrollTo = function scrollTo(options) {
137
+ if (typeof options !== "object" || options.top === undefined) return;
138
+
139
+ this.scrollTop = options.top;
140
+ if (options.behavior === "smooth") {
141
+ smoothScrolls.push({ element: this, top: options.top });
142
+ }
143
+ };
144
+ const { getAllByRole } = render(<TimePicker defaultValue={{ hour: 11, minute: 0 }} />);
145
+ const [periodColumn, hourColumn] = getAllByRole("spinbutton");
146
+
147
+ fireEvent.keyDown(hourColumn, { key: "ArrowDown" });
148
+ act(() => jest.advanceTimersByTime(120));
149
+
150
+ expect(periodColumn).toHaveAttribute("aria-valuetext", "오후");
151
+ expect(smoothScrolls).toContainEqual({ element: periodColumn, top: 44 });
152
+ } finally {
153
+ HTMLElement.prototype.scrollTo = previousScrollTo;
154
+ jest.useRealTimers();
155
+ }
156
+ });
157
+
158
+ it("disabled는 컬럼을 tab 순서에서 제외한다", () => {
159
+ const { getAllByRole } = render(<TimePicker disabled />);
160
+
161
+ for (const column of getAllByRole("spinbutton")) {
162
+ expect(column).toHaveAttribute("aria-disabled", "true");
163
+ expect(column).toHaveAttribute("tabindex", "-1");
164
+ }
165
+ });
166
+ });
@@ -0,0 +1,163 @@
1
+ "use client";
2
+
3
+ import { timePicker } from "@seed-design/css/recipes/time-picker";
4
+ import { useTimePicker, type UseTimePickerProps } from "@seed-design/react-time-picker";
5
+ import clsx from "clsx";
6
+ import * as React from "react";
7
+ import {
8
+ InternalWheelPickerColumn,
9
+ InternalWheelPickerRoot,
10
+ type InternalWheelPickerRootProps,
11
+ } from "../private/WheelPicker";
12
+
13
+ const ITEM_SIZE = 44;
14
+ const VISIBLE_ITEM_COUNT = 5;
15
+ const DEFAULT_ARIA_LABELS = {
16
+ ko: {
17
+ root: "시간 선택",
18
+ period: "오전/오후",
19
+ hour: "시",
20
+ minute: "분",
21
+ },
22
+ en: {
23
+ root: "Select time",
24
+ period: "AM/PM",
25
+ hour: "Hour",
26
+ minute: "Minute",
27
+ },
28
+ ja: {
29
+ root: "時刻を選択",
30
+ period: "午前/午後",
31
+ hour: "時",
32
+ minute: "分",
33
+ },
34
+ } as const;
35
+
36
+ function getDefaultAriaLabels(locale: string | string[] | undefined) {
37
+ const [canonicalLocale = "ko-KR"] = Intl.getCanonicalLocales(locale ?? "ko-KR");
38
+ const language = new Intl.Locale(canonicalLocale).language;
39
+
40
+ if (language === "ko") return DEFAULT_ARIA_LABELS.ko;
41
+ if (language === "ja") return DEFAULT_ARIA_LABELS.ja;
42
+ return DEFAULT_ARIA_LABELS.en;
43
+ }
44
+
45
+ const classNames = timePicker();
46
+
47
+ const columnClassNames = {
48
+ period: classNames.periodColumn,
49
+ hour: classNames.hourColumn,
50
+ minute: classNames.minuteColumn,
51
+ };
52
+
53
+ export interface TimePickerProps
54
+ extends UseTimePickerProps,
55
+ Omit<
56
+ InternalWheelPickerRootProps,
57
+ | keyof UseTimePickerProps
58
+ | "asChild"
59
+ | "children"
60
+ | "columnsClassName"
61
+ | "fogSize"
62
+ | "itemSize"
63
+ | "readOnly"
64
+ | "scrollFogClassName"
65
+ | "selectionIndicatorClassName"
66
+ | "visibleItemCount"
67
+ > {
68
+ /**
69
+ * 오전·오후 컬럼의 접근성 이름입니다.
70
+ * 지정하지 않으면 `locale`에 맞는 기본값을 사용합니다.
71
+ */
72
+ periodAriaLabel?: string;
73
+
74
+ /**
75
+ * 시 컬럼의 접근성 이름입니다.
76
+ * 지정하지 않으면 `locale`에 맞는 기본값을 사용합니다.
77
+ */
78
+ hourAriaLabel?: string;
79
+
80
+ /**
81
+ * 분 컬럼의 접근성 이름입니다.
82
+ * 지정하지 않으면 `locale`에 맞는 기본값을 사용합니다.
83
+ */
84
+ minuteAriaLabel?: string;
85
+ }
86
+
87
+ /**
88
+ * 휠을 스크롤하여 오전·오후, 시, 분을 선택하는 12시간제 시간 선택 컴포넌트입니다.
89
+ *
90
+ * 값은 오전·오후를 별도 필드로 나누지 않고 24시간 형식의 `TimePickerValue`로 주고받습니다.
91
+ */
92
+ export const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
93
+ (
94
+ {
95
+ value,
96
+ defaultValue,
97
+ onValueChange,
98
+ minuteStep,
99
+ locale,
100
+ disabled,
101
+ periodAriaLabel,
102
+ hourAriaLabel,
103
+ minuteAriaLabel,
104
+ className,
105
+ "aria-label": ariaLabel,
106
+ "aria-labelledby": ariaLabelledby,
107
+ ...props
108
+ },
109
+ ref,
110
+ ) => {
111
+ const api = useTimePicker({
112
+ value,
113
+ defaultValue,
114
+ onValueChange,
115
+ minuteStep,
116
+ locale,
117
+ disabled,
118
+ });
119
+ const defaultAriaLabels = getDefaultAriaLabels(locale);
120
+ const ariaLabels = {
121
+ period: periodAriaLabel ?? defaultAriaLabels.period,
122
+ hour: hourAriaLabel ?? defaultAriaLabels.hour,
123
+ minute: minuteAriaLabel ?? defaultAriaLabels.minute,
124
+ };
125
+
126
+ return (
127
+ <InternalWheelPickerRoot
128
+ ref={ref}
129
+ itemSize={ITEM_SIZE}
130
+ visibleItemCount={VISIBLE_ITEM_COUNT}
131
+ disabled={disabled}
132
+ aria-label={ariaLabelledby ? ariaLabel : (ariaLabel ?? defaultAriaLabels.root)}
133
+ aria-labelledby={ariaLabelledby}
134
+ className={clsx(classNames.root, className)}
135
+ columnsClassName={classNames.columns}
136
+ scrollFogClassName={classNames.scrollFog}
137
+ selectionIndicatorClassName={classNames.selectionIndicator}
138
+ {...props}
139
+ >
140
+ {api.columnOrder.map((type) => {
141
+ const column = api.columns[type];
142
+
143
+ return (
144
+ <InternalWheelPickerColumn
145
+ key={type}
146
+ aria-label={ariaLabels[type]}
147
+ className={columnClassNames[type]}
148
+ itemClassName={classNames.item}
149
+ options={column.options}
150
+ value={column.value}
151
+ onValueChange={column.onValueChange}
152
+ loop={column.loop}
153
+ valueChangeBehavior={type === "period" ? "smooth" : "auto"}
154
+ />
155
+ );
156
+ })}
157
+ </InternalWheelPickerRoot>
158
+ );
159
+ },
160
+ );
161
+ TimePicker.displayName = "TimePicker";
162
+
163
+ export type { MinuteStep, TimePickerValue } from "@seed-design/react-time-picker";
@@ -0,0 +1,6 @@
1
+ export {
2
+ TimePicker,
3
+ type MinuteStep,
4
+ type TimePickerProps,
5
+ type TimePickerValue,
6
+ } from "./TimePicker";
@@ -20,6 +20,7 @@ export * from "./ContentPlaceholder";
20
20
  export * from "./ContextualFloatingButton";
21
21
  export * from "./ControlChip";
22
22
  export * from "./Count";
23
+ export * from "./DatePicker";
23
24
  export * from "./Dialog";
24
25
  export * from "./Divider";
25
26
  export * from "./ExtendedActionSheet";
@@ -80,5 +81,6 @@ export * from "./Tabs";
80
81
  export * from "./TagGroup";
81
82
  export * from "./Text";
82
83
  export * from "./TextField";
84
+ export * from "./TimePicker";
83
85
  export * from "./ToggleButton";
84
86
  export * from "./VisuallyHidden";
@@ -0,0 +1,54 @@
1
+ import { render } from "@testing-library/react";
2
+ import { describe, expect, it } from "bun:test";
3
+ import { InternalWheelPickerColumn, InternalWheelPickerRoot } from "./WheelPicker";
4
+
5
+ const options = [
6
+ { value: "a", label: "A" },
7
+ { value: "b", label: "B" },
8
+ ];
9
+
10
+ describe("InternalWheelPicker", () => {
11
+ it("여러 컬럼을 하나의 ScrollFog와 Selection Indicator로 조합한다", () => {
12
+ const { container, getAllByRole, getByRole } = render(
13
+ <InternalWheelPickerRoot
14
+ aria-label="내부 휠 피커"
15
+ itemSize={40}
16
+ visibleItemCount={5}
17
+ fogSize={32}
18
+ className="root"
19
+ columnsClassName="columns"
20
+ scrollFogClassName="fog"
21
+ selectionIndicatorClassName="indicator"
22
+ >
23
+ <InternalWheelPickerColumn aria-label="첫 번째" options={options} />
24
+ <InternalWheelPickerColumn aria-label="두 번째" options={options} />
25
+ </InternalWheelPickerRoot>,
26
+ );
27
+
28
+ const root = getByRole("group");
29
+ const fogs = container.querySelectorAll("[data-wheel-picker-scroll-fog]");
30
+ const indicator = container.querySelector("[data-wheel-picker-indicator]");
31
+ const columns = container.querySelector("[data-wheel-picker-columns]");
32
+ const [firstColumn] = getAllByRole("spinbutton");
33
+
34
+ expect(root).toHaveClass("seed-wheel-picker__root", "root");
35
+ expect(root).toHaveStyle({
36
+ "--seed-wheel-picker-item-size": "40px",
37
+ "--seed-wheel-picker-visible-item-count": "5",
38
+ "--seed-wheel-picker-viewport-size": "200px",
39
+ "--seed-wheel-picker-center-offset": "80px",
40
+ });
41
+ expect(fogs).toHaveLength(1);
42
+ expect(fogs[0]).toHaveClass("seed-wheel-picker__scrollFog", "fog");
43
+ expect(fogs[0]).toHaveStyle({
44
+ "--scroll-fog-size-top": "32px",
45
+ "--scroll-fog-size-bottom": "32px",
46
+ });
47
+ expect(indicator).toHaveClass("seed-wheel-picker__selectionIndicator", "indicator");
48
+ expect(indicator).toHaveAttribute("aria-hidden", "true");
49
+ expect(columns).toHaveClass("seed-wheel-picker__columns", "columns");
50
+ expect(getAllByRole("spinbutton")).toHaveLength(2);
51
+ expect(firstColumn).toHaveClass("seed-wheel-picker__column");
52
+ expect(firstColumn.firstElementChild).toHaveClass("seed-wheel-picker__item");
53
+ });
54
+ });
@@ -0,0 +1,110 @@
1
+ "use client";
2
+
3
+ import { wheelPicker } from "@seed-design/css/recipes/wheel-picker";
4
+ import { Primitive } from "@seed-design/react-primitive";
5
+ import * as HeadlessWheelPicker from "@seed-design/react-wheel-picker";
6
+ import clsx from "clsx";
7
+ import * as React from "react";
8
+ import { ScrollFog, type ScrollFogProps } from "../ScrollFog/ScrollFog";
9
+
10
+ const classNames = wheelPicker();
11
+
12
+ type WheelPickerCssProperties = React.CSSProperties & {
13
+ "--seed-wheel-picker-item-size"?: string;
14
+ "--seed-wheel-picker-visible-item-count"?: number;
15
+ "--seed-wheel-picker-viewport-size"?: string;
16
+ "--seed-wheel-picker-center-offset"?: string;
17
+ };
18
+
19
+ export interface InternalWheelPickerRootProps
20
+ extends Omit<HeadlessWheelPicker.WheelPickerRootProps, "asChild" | "children"> {
21
+ children: React.ReactNode;
22
+ columnsClassName?: string;
23
+ scrollFogClassName?: string;
24
+ selectionIndicatorClassName?: string;
25
+ fogSize?: ScrollFogProps["size"];
26
+ }
27
+
28
+ export const InternalWheelPickerRoot = React.forwardRef<
29
+ HTMLDivElement,
30
+ InternalWheelPickerRootProps
31
+ >(
32
+ (
33
+ {
34
+ children,
35
+ className,
36
+ columnsClassName,
37
+ scrollFogClassName,
38
+ selectionIndicatorClassName,
39
+ fogSize,
40
+ itemSize,
41
+ visibleItemCount,
42
+ style,
43
+ ...props
44
+ },
45
+ ref,
46
+ ) => {
47
+ const centerOffset = ((visibleItemCount - 1) / 2) * itemSize;
48
+ const wheelPickerStyle: WheelPickerCssProperties = {
49
+ ...style,
50
+ "--seed-wheel-picker-item-size": `${itemSize}px`,
51
+ "--seed-wheel-picker-visible-item-count": visibleItemCount,
52
+ "--seed-wheel-picker-viewport-size": `${itemSize * visibleItemCount}px`,
53
+ "--seed-wheel-picker-center-offset": `${centerOffset}px`,
54
+ };
55
+
56
+ return (
57
+ <HeadlessWheelPicker.WheelPickerRoot
58
+ ref={ref}
59
+ itemSize={itemSize}
60
+ visibleItemCount={visibleItemCount}
61
+ className={clsx(classNames.root, className)}
62
+ style={wheelPickerStyle}
63
+ {...props}
64
+ >
65
+ <Primitive.div
66
+ aria-hidden
67
+ className={clsx(classNames.selectionIndicator, selectionIndicatorClassName)}
68
+ data-wheel-picker-indicator=""
69
+ />
70
+ <ScrollFog
71
+ className={clsx(classNames.scrollFog, scrollFogClassName)}
72
+ placement={["top", "bottom"]}
73
+ size={fogSize}
74
+ hideScrollBar
75
+ data-wheel-picker-scroll-fog=""
76
+ >
77
+ <Primitive.div
78
+ className={clsx(classNames.columns, columnsClassName)}
79
+ data-wheel-picker-columns=""
80
+ >
81
+ {children}
82
+ </Primitive.div>
83
+ </ScrollFog>
84
+ </HeadlessWheelPicker.WheelPickerRoot>
85
+ );
86
+ },
87
+ );
88
+ InternalWheelPickerRoot.displayName = "InternalWheelPickerRoot";
89
+
90
+ export interface InternalWheelPickerColumnProps
91
+ extends Omit<HeadlessWheelPicker.WheelPickerColumnProps, "asChild" | "renderOption"> {
92
+ itemClassName?: string;
93
+ }
94
+
95
+ export const InternalWheelPickerColumn = React.forwardRef<
96
+ HTMLDivElement,
97
+ InternalWheelPickerColumnProps
98
+ >(({ className, itemClassName, ...props }, ref) => (
99
+ <HeadlessWheelPicker.WheelPickerColumn
100
+ ref={ref}
101
+ className={clsx(classNames.column, className)}
102
+ renderOption={(option, optionProps) => (
103
+ <Primitive.div className={clsx(classNames.item, itemClassName)} {...optionProps}>
104
+ {option.label}
105
+ </Primitive.div>
106
+ )}
107
+ {...props}
108
+ />
109
+ ));
110
+ InternalWheelPickerColumn.displayName = "InternalWheelPickerColumn";