Picker: fix the dark theme styles
This commit is contained in:
@@ -47,13 +47,17 @@ export default function DatePickerBottomSheet({
|
|||||||
label: (i + 1).toString(),
|
label: (i + 1).toString(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const years = Array.from({ length: 21 }, (_, i) => {
|
const currentYear = new Date().getFullYear();
|
||||||
const year = new Date().getFullYear() - 10 + i;
|
const years = Array.from(
|
||||||
return {
|
{ length: currentYear - 10 - 1900 + 1 },
|
||||||
value: year.toString(),
|
(_, i) => {
|
||||||
label: year.toString(),
|
const year = 1900 + i;
|
||||||
};
|
return {
|
||||||
});
|
value: year.toString(),
|
||||||
|
label: year.toString(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
month: months,
|
month: months,
|
||||||
@@ -145,10 +149,12 @@ export default function DatePickerBottomSheet({
|
|||||||
style={{
|
style={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
backgroundColor: "#ffffff",
|
backgroundColor: "var(--secondary-background)",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: 250,
|
height: 250,
|
||||||
|
border: "1px solid var(--border)",
|
||||||
|
borderRadius: "8px",
|
||||||
}}
|
}}
|
||||||
aria-selected={false}
|
aria-selected={false}
|
||||||
>
|
>
|
||||||
@@ -156,8 +162,8 @@ export default function DatePickerBottomSheet({
|
|||||||
name="month"
|
name="month"
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
borderRight: "1px solid #e5e7eb",
|
borderRight: "1px solid var(--border)",
|
||||||
backgroundColor: "#ffffff",
|
backgroundColor: "var(--secondary-background)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{pickerOptions.month.map((option) => (
|
{pickerOptions.month.map((option) => (
|
||||||
@@ -166,16 +172,22 @@ export default function DatePickerBottomSheet({
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontWeight: selected ? "600" : "400",
|
fontWeight: selected ? "600" : "400",
|
||||||
color: selected ? "#171717" : "#9ca3af",
|
color: selected
|
||||||
|
? "var(--foreground)"
|
||||||
|
: "var(--text-color-gray)",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
padding: "9px 0",
|
padding: "9px 0",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
opacity: selected ? 1 : 0.6,
|
opacity: selected ? 1 : 0.7,
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
lineHeight: "1.2",
|
lineHeight: "1.2",
|
||||||
height: 36,
|
height: 36,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
backgroundColor: selected ? "#f3f4f6" : "transparent",
|
backgroundColor: selected
|
||||||
|
? "var(--background)"
|
||||||
|
: "transparent",
|
||||||
|
borderRadius: selected ? "4px" : "0",
|
||||||
|
margin: selected ? "2px 4px" : "0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
@@ -188,8 +200,8 @@ export default function DatePickerBottomSheet({
|
|||||||
name="day"
|
name="day"
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
borderRight: "1px solid #e5e7eb",
|
borderRight: "1px solid var(--border)",
|
||||||
backgroundColor: "#ffffff",
|
backgroundColor: "var(--secondary-background)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{pickerOptions.day.map((option) => (
|
{pickerOptions.day.map((option) => (
|
||||||
@@ -198,16 +210,22 @@ export default function DatePickerBottomSheet({
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontWeight: selected ? "600" : "400",
|
fontWeight: selected ? "600" : "400",
|
||||||
color: selected ? "#171717" : "#9ca3af",
|
color: selected
|
||||||
|
? "var(--foreground)"
|
||||||
|
: "var(--text-color-gray)",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
padding: "9px 0",
|
padding: "9px 0",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
opacity: selected ? 1 : 0.6,
|
opacity: selected ? 1 : 0.7,
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
lineHeight: "1.2",
|
lineHeight: "1.2",
|
||||||
height: 36,
|
height: 36,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
backgroundColor: selected ? "#f3f4f6" : "transparent",
|
backgroundColor: selected
|
||||||
|
? "var(--background)"
|
||||||
|
: "transparent",
|
||||||
|
borderRadius: selected ? "4px" : "0",
|
||||||
|
margin: selected ? "2px 4px" : "0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
@@ -220,7 +238,7 @@ export default function DatePickerBottomSheet({
|
|||||||
name="year"
|
name="year"
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: "#ffffff",
|
backgroundColor: "var(--secondary-background)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{pickerOptions.year.map((option) => (
|
{pickerOptions.year.map((option) => (
|
||||||
@@ -229,16 +247,22 @@ export default function DatePickerBottomSheet({
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontWeight: selected ? "600" : "400",
|
fontWeight: selected ? "600" : "400",
|
||||||
color: selected ? "#171717" : "#9ca3af",
|
color: selected
|
||||||
|
? "var(--foreground)"
|
||||||
|
: "var(--text-color-gray)",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
padding: "9px 0",
|
padding: "9px 0",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
opacity: selected ? 1 : 0.6,
|
opacity: selected ? 1 : 0.7,
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
lineHeight: "1.2",
|
lineHeight: "1.2",
|
||||||
height: 36,
|
height: 36,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
backgroundColor: selected ? "#f3f4f6" : "transparent",
|
backgroundColor: selected
|
||||||
|
? "var(--background)"
|
||||||
|
: "transparent",
|
||||||
|
borderRadius: selected ? "4px" : "0",
|
||||||
|
margin: selected ? "2px 4px" : "0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
|
|||||||
224
src/components/WheelPicker/Picker.module.css
Normal file
224
src/components/WheelPicker/Picker.module.css
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Picker Component Theme-Aware Styling
|
||||||
|
*
|
||||||
|
* This CSS module provides enhanced dark theme support for the Picker component.
|
||||||
|
* Features include:
|
||||||
|
* - Automatic theme detection using CSS variables
|
||||||
|
* - Enhanced visual feedback for selected items
|
||||||
|
* - Smooth animations and transitions
|
||||||
|
* - Accessibility improvements (focus states, high contrast)
|
||||||
|
* - Responsive design for mobile devices
|
||||||
|
* - Reduced motion support for accessibility
|
||||||
|
*
|
||||||
|
* The styling automatically adapts to both light and dark themes using CSS variables
|
||||||
|
* defined in the application's theme system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.pickerContainer {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: var(--secondary-background);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerContainer:hover {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 1px var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerColumn {
|
||||||
|
flex: 1;
|
||||||
|
border-right: 1px solid var(--border);
|
||||||
|
background-color: var(--secondary-background);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerColumn:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem {
|
||||||
|
height: 36px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 2px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem:hover {
|
||||||
|
background-color: var(--background);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem.selected {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--foreground);
|
||||||
|
opacity: 1;
|
||||||
|
background-color: var(--background);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 2px 4px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem.unselected {
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--text-color-gray);
|
||||||
|
opacity: 0.7;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItemText {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 9px 0;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
width: 100%;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme specific enhancements */
|
||||||
|
[data-theme="dark"] .pickerContainer {
|
||||||
|
background-color: var(--secondary-background);
|
||||||
|
border-color: var(--border);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pickerContainer:hover {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 1px var(--primary), 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pickerColumn {
|
||||||
|
background-color: var(--secondary-background);
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pickerItem.selected {
|
||||||
|
background-color: var(--background);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pickerItem:hover {
|
||||||
|
background-color: var(--background);
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .pickerItem.unselected {
|
||||||
|
color: var(--text-color-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme specific enhancements */
|
||||||
|
[data-theme="light"] .pickerContainer {
|
||||||
|
background-color: var(--secondary-background);
|
||||||
|
border-color: var(--border);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .pickerContainer:hover {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 1px var(--primary), 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .pickerItem.selected {
|
||||||
|
background-color: var(--background);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .pickerItem:hover {
|
||||||
|
background-color: var(--background);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .pickerItem.unselected {
|
||||||
|
color: var(--text-color-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accessibility enhancements */
|
||||||
|
.pickerItem:focus-visible {
|
||||||
|
outline: 2px solid var(--primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem:focus-visible.selected {
|
||||||
|
outline-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation for smooth transitions */
|
||||||
|
@keyframes pickerItemSelect {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem.selected {
|
||||||
|
animation: pickerItemSelect 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive design */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.pickerItemText {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem {
|
||||||
|
height: 32px;
|
||||||
|
margin: 1px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem.selected {
|
||||||
|
margin: 1px 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* High contrast mode support */
|
||||||
|
@media (prefers-contrast: high) {
|
||||||
|
.pickerContainer {
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem.selected {
|
||||||
|
border: 2px solid var(--primary);
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: var(--secondary-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem.unselected {
|
||||||
|
color: var(--foreground);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduced motion support */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.pickerItem,
|
||||||
|
.pickerContainer,
|
||||||
|
.pickerItemText {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem.selected {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerItem:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
CSSProperties,
|
CSSProperties,
|
||||||
HTMLProps,
|
HTMLProps,
|
||||||
MutableRefObject,
|
MutableRefObject,
|
||||||
createContext,
|
createContext,
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useMemo,
|
useMemo,
|
||||||
useReducer,
|
useReducer,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
import styles from "../Picker.module.css";
|
||||||
|
|
||||||
const DEFAULT_HEIGHT = 216;
|
const DEFAULT_HEIGHT = 216;
|
||||||
const DEFAULT_ITEM_HEIGHT = 36;
|
const DEFAULT_ITEM_HEIGHT = 36;
|
||||||
@@ -160,9 +161,9 @@ function PickerRoot<TType extends PickerValue>(props: PickerRootProps<TType>) {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
maskImage:
|
maskImage:
|
||||||
"linear-gradient(to top, transparent, transparent 5%, white 20%, white 80%, transparent 95%, transparent)",
|
"linear-gradient(to top, transparent, transparent 5%, var(--foreground) 20%, var(--foreground) 80%, transparent 95%, transparent)",
|
||||||
WebkitMaskImage:
|
WebkitMaskImage:
|
||||||
"linear-gradient(to top, transparent, transparent 5%, white 20%, white 80%, transparent 95%, transparent)",
|
"linear-gradient(to top, transparent, transparent 5%, var(--foreground) 20%, var(--foreground) 80%, transparent 95%, transparent)",
|
||||||
}),
|
}),
|
||||||
[height]
|
[height]
|
||||||
);
|
);
|
||||||
@@ -194,6 +195,7 @@ function PickerRoot<TType extends PickerValue>(props: PickerRootProps<TType>) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
className={styles.pickerContainer}
|
||||||
style={{
|
style={{
|
||||||
...containerStyle,
|
...containerStyle,
|
||||||
...style,
|
...style,
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
CSSProperties,
|
CSSProperties,
|
||||||
HTMLProps,
|
HTMLProps,
|
||||||
createContext,
|
createContext,
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
import styles from "../Picker.module.css";
|
||||||
import { usePickerActions, usePickerData } from "./Picker";
|
import { usePickerActions, usePickerData } from "./Picker";
|
||||||
|
|
||||||
interface PickerColumnProps extends HTMLProps<HTMLDivElement> {
|
interface PickerColumnProps extends HTMLProps<HTMLDivElement> {
|
||||||
@@ -255,6 +256,7 @@ function PickerColumn({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
className={styles.pickerColumn}
|
||||||
style={{
|
style={{
|
||||||
...columnStyle,
|
...columnStyle,
|
||||||
...style,
|
...style,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
HTMLProps,
|
HTMLProps,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
import styles from "../Picker.module.css";
|
||||||
import { usePickerActions, usePickerData } from "./Picker";
|
import { usePickerActions, usePickerData } from "./Picker";
|
||||||
import { useColumnData } from "./PickerColumn";
|
import { useColumnData } from "./PickerColumn";
|
||||||
|
|
||||||
@@ -45,12 +46,19 @@ function PickerItem({ style, children, value, ...restProps }: PickerItemProps) {
|
|||||||
[itemHeight]
|
[itemHeight]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getItemClassName = useCallback((selected: boolean) => {
|
||||||
|
return `${styles.pickerItem} ${selected ? styles.selected : styles.unselected}`;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
pickerActions.change(key, value);
|
pickerActions.change(key, value);
|
||||||
}, [pickerActions, key, value]);
|
}, [pickerActions, key, value]);
|
||||||
|
|
||||||
|
const isSelected = pickerValue[key] === value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
className={getItemClassName(isSelected)}
|
||||||
style={{
|
style={{
|
||||||
...itemStyle,
|
...itemStyle,
|
||||||
...style,
|
...style,
|
||||||
@@ -60,8 +68,12 @@ function PickerItem({ style, children, value, ...restProps }: PickerItemProps) {
|
|||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{isFunction(children)
|
{isFunction(children)
|
||||||
? children({ selected: pickerValue[key] === value })
|
? children({ selected: isSelected })
|
||||||
: children}
|
: (
|
||||||
|
<div className={styles.pickerItemText}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user