diff --git a/lib/src/components/time_picker.dart b/lib/src/components/time_picker.dart index e786a6e4..f1256d11 100644 --- a/lib/src/components/time_picker.dart +++ b/lib/src/components/time_picker.dart @@ -485,10 +485,15 @@ class _ShadTimePickerState extends State { @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); - final effectiveAxis = widget.axis ?? Axis.horizontal; - final effectiveSpacing = widget.spacing ?? 0; - final effectiveRunSpacing = widget.runSpacing ?? 0; - final effectiveJumpToNextField = widget.jumpToNextFieldWhenFilled ?? true; + final effectiveAxis = + widget.axis ?? theme.timePickerTheme.axis ?? Axis.horizontal; + final effectiveSpacing = + widget.spacing ?? theme.timePickerTheme.spacing ?? 0; + final effectiveRunSpacing = + widget.runSpacing ?? theme.timePickerTheme.runSpacing ?? 0; + final effectiveJumpToNextField = widget.jumpToNextFieldWhenFilled ?? + theme.timePickerTheme.jumpToNextFieldWhenFilled ?? + true; final effectiveHourLabel = widget.hourLabel ?? const Text('Hours'); final effectiveMinuteLabel = widget.minuteLabel ?? const Text('Minutes'); final effectiveSecondLabel = widget.secondLabel ?? const Text('Seconds'); @@ -503,16 +508,25 @@ class _ShadTimePickerState extends State { widget.secondPlaceholder ?? defaultPlaceholder; final effectivePeriodPlaceholder = widget.periodPlaceholder; - final effectiveAlignment = widget.alignment ?? WrapAlignment.center; - final effectiveRunAlignment = widget.runAlignment ?? WrapAlignment.center; - final effectiveCrossAxisAlignment = - widget.crossAxisAlignment ?? WrapCrossAlignment.center; - - final effectivePeriodHeight = widget.periodHeight ?? 50; - final effectivePeriodMinWidth = widget.periodMinWidth ?? 65; - final effectiveGap = widget.gap ?? 2; - final effectiveFieldWidth = widget.fieldWidth ?? 58; + final effectiveAlignment = widget.alignment ?? + theme.timePickerTheme.alignment ?? + WrapAlignment.center; + final effectiveRunAlignment = widget.runAlignment ?? + theme.timePickerTheme.runAlignment ?? + WrapAlignment.center; + final effectiveCrossAxisAlignment = widget.crossAxisAlignment ?? + theme.timePickerTheme.crossAxisAlignment ?? + WrapCrossAlignment.center; + + final effectivePeriodHeight = + widget.periodHeight ?? theme.timePickerTheme.periodHeight ?? 50; + final effectivePeriodMinWidth = + widget.periodMinWidth ?? theme.timePickerTheme.periodMinWidth ?? 65; + final effectiveGap = widget.gap ?? theme.timePickerTheme.gap ?? 2; + final effectiveFieldWidth = + widget.fieldWidth ?? theme.timePickerTheme.fieldWidth ?? 58; final effectiveFieldPadding = widget.fieldPadding ?? + theme.timePickerTheme.fieldPadding ?? const EdgeInsets.symmetric(horizontal: 12, vertical: 8); final effectiveStyle = theme.textTheme.muted @@ -521,10 +535,13 @@ class _ShadTimePickerState extends State { fontSize: 16, height: 24 / 16, ) + .merge(theme.timePickerTheme.style) .merge(widget.style); - final effectiveLabelStyle = - theme.textTheme.small.copyWith(fontSize: 12).merge(widget.labelStyle); + final effectiveLabelStyle = theme.textTheme.small + .copyWith(fontSize: 12) + .merge(theme.timePickerTheme.labelStyle) + .merge(widget.labelStyle); return Wrap( direction: effectiveAxis, @@ -793,8 +810,9 @@ class ShadTimePickerTextEditingController extends TextEditingController { fontSize: 16, height: 24 / 16, ); - final effectivePlaceholderStyle = - defaultPlaceholderStyle.merge(placeholderStyle); + final effectivePlaceholderStyle = defaultPlaceholderStyle + .merge(theme.timePickerTheme.placeholderStyle) + .merge(placeholderStyle); final intValue = int.tryParse(value.text); if (intValue == null) return const TextSpan(); diff --git a/lib/src/theme/components/time_picker.dart b/lib/src/theme/components/time_picker.dart index e69de29b..05dbc3d0 100644 --- a/lib/src/theme/components/time_picker.dart +++ b/lib/src/theme/components/time_picker.dart @@ -0,0 +1,213 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +@immutable +class ShadTimePickerTheme { + const ShadTimePickerTheme({ + this.merge = true, + this.axis, + this.spacing, + this.runSpacing, + this.jumpToNextFieldWhenFilled, + this.alignment, + this.runAlignment, + this.crossAxisAlignment, + this.initialDayPeriod, + this.periodHeight, + this.periodMinWidth, + this.gap, + this.style, + this.placeholderStyle, + this.labelStyle, + this.fieldWidth, + this.fieldPadding, + }); + + final bool merge; + + /// {@macro ShadTimePicker.axis} + final Axis? axis; + + /// {@macro ShadTimePicker.spacing} + final double? spacing; + + /// {@macro ShadTimePicker.runSpacing} + final double? runSpacing; + + /// {@macro ShadTimePicker.jumpToNextFieldWhenFilled} + final bool? jumpToNextFieldWhenFilled; + + /// {@macro ShadTimePicker.alignment} + final WrapAlignment? alignment; + + /// {@macro ShadTimePicker.runAlignment} + final WrapAlignment? runAlignment; + + /// {@macro ShadTimePicker.crossAxisAlignment} + final WrapCrossAlignment? crossAxisAlignment; + + /// {@macro ShadTimePicker.initialDayPeriod} + final DayPeriod? initialDayPeriod; + + /// {@macro ShadTimePicker.periodHeight} + final double? periodHeight; + + /// {@macro ShadTimePicker.periodMinWidth} + final double? periodMinWidth; + + /// {@macro ShadTimePicker.gap} + final double? gap; + + /// {@macro ShadTimePicker.style} + final TextStyle? style; + + /// {@macro ShadTimePicker.placeholderStyle} + final TextStyle? placeholderStyle; + + /// {@macro ShadTimePicker.labelStyle} + final TextStyle? labelStyle; + + /// {@macro ShadTimePicker.fieldWidth} + final double? fieldWidth; + + /// {@macro ShadTimePicker.fieldPadding} + final EdgeInsets? fieldPadding; + + static ShadTimePickerTheme lerp( + ShadTimePickerTheme a, + ShadTimePickerTheme b, + double t, + ) { + if (identical(a, b)) return a; + return ShadTimePickerTheme( + merge: b.merge, + axis: t < 0.5 ? a.axis : b.axis, + spacing: lerpDouble(a.spacing, b.spacing, t), + runSpacing: lerpDouble(a.runSpacing, b.runSpacing, t), + jumpToNextFieldWhenFilled: + t < 0.5 ? a.jumpToNextFieldWhenFilled : b.jumpToNextFieldWhenFilled, + alignment: b.alignment ?? a.alignment, + runAlignment: b.runAlignment ?? a.runAlignment, + crossAxisAlignment: b.crossAxisAlignment ?? a.crossAxisAlignment, + initialDayPeriod: b.initialDayPeriod ?? a.initialDayPeriod, + periodHeight: b.periodHeight ?? a.periodHeight, + periodMinWidth: b.periodMinWidth ?? a.periodMinWidth, + gap: b.gap ?? a.gap, + style: b.style ?? a.style, + placeholderStyle: b.placeholderStyle ?? a.placeholderStyle, + labelStyle: b.labelStyle ?? a.labelStyle, + fieldWidth: b.fieldWidth ?? a.fieldWidth, + fieldPadding: b.fieldPadding ?? a.fieldPadding, + ); + } + + ShadTimePickerTheme copyWith({ + bool? merge, + Axis? axis, + double? spacing, + double? runSpacing, + bool? jumpToNextFieldWhenFilled, + WrapAlignment? alignment, + WrapAlignment? runAlignment, + WrapCrossAlignment? crossAxisAlignment, + DayPeriod? initialDayPeriod, + double? periodHeight, + double? periodMinWidth, + double? gap, + TextStyle? style, + TextStyle? placeholderStyle, + TextStyle? labelStyle, + double? fieldWidth, + EdgeInsets? fieldPadding, + }) { + return ShadTimePickerTheme( + merge: merge ?? this.merge, + axis: axis ?? this.axis, + spacing: spacing ?? this.spacing, + runSpacing: runSpacing ?? this.runSpacing, + jumpToNextFieldWhenFilled: + jumpToNextFieldWhenFilled ?? this.jumpToNextFieldWhenFilled, + alignment: alignment ?? this.alignment, + runAlignment: runAlignment ?? this.runAlignment, + crossAxisAlignment: crossAxisAlignment ?? this.crossAxisAlignment, + initialDayPeriod: initialDayPeriod ?? this.initialDayPeriod, + periodHeight: periodHeight ?? this.periodHeight, + periodMinWidth: periodMinWidth ?? this.periodMinWidth, + gap: gap ?? this.gap, + style: style ?? this.style, + placeholderStyle: placeholderStyle ?? this.placeholderStyle, + labelStyle: labelStyle ?? this.labelStyle, + fieldWidth: fieldWidth ?? this.fieldWidth, + fieldPadding: fieldPadding ?? this.fieldPadding, + ); + } + + ShadTimePickerTheme mergeWith(ShadTimePickerTheme? other) { + if (other == null) return this; + if (!other.merge) return other; + return copyWith( + axis: other.axis, + spacing: other.spacing, + runSpacing: other.runSpacing, + jumpToNextFieldWhenFilled: other.jumpToNextFieldWhenFilled, + alignment: other.alignment, + runAlignment: other.runAlignment, + crossAxisAlignment: other.crossAxisAlignment, + initialDayPeriod: other.initialDayPeriod, + periodHeight: other.periodHeight, + periodMinWidth: other.periodMinWidth, + gap: other.gap, + style: other.style, + placeholderStyle: other.placeholderStyle, + labelStyle: other.labelStyle, + fieldWidth: other.fieldWidth, + fieldPadding: other.fieldPadding, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ShadTimePickerTheme && + other.merge == merge && + other.axis == axis && + other.spacing == spacing && + other.runSpacing == runSpacing && + other.jumpToNextFieldWhenFilled == jumpToNextFieldWhenFilled && + other.alignment == alignment && + other.runAlignment == runAlignment && + other.crossAxisAlignment == crossAxisAlignment && + other.initialDayPeriod == initialDayPeriod && + other.periodHeight == periodHeight && + other.periodMinWidth == periodMinWidth && + other.gap == gap && + other.style == style && + other.placeholderStyle == placeholderStyle && + other.labelStyle == labelStyle && + other.fieldWidth == fieldWidth && + other.fieldPadding == fieldPadding; + } + + @override + int get hashCode { + return merge.hashCode ^ + axis.hashCode ^ + spacing.hashCode ^ + runSpacing.hashCode ^ + jumpToNextFieldWhenFilled.hashCode ^ + alignment.hashCode ^ + runAlignment.hashCode ^ + crossAxisAlignment.hashCode ^ + initialDayPeriod.hashCode ^ + periodHeight.hashCode ^ + periodMinWidth.hashCode ^ + gap.hashCode ^ + style.hashCode ^ + placeholderStyle.hashCode ^ + labelStyle.hashCode ^ + fieldWidth.hashCode ^ + fieldPadding.hashCode; + } +} diff --git a/lib/src/theme/data.dart b/lib/src/theme/data.dart index 4b6ae611..c0d4d95d 100644 --- a/lib/src/theme/data.dart +++ b/lib/src/theme/data.dart @@ -26,6 +26,7 @@ import 'package:shadcn_ui/src/theme/components/slider.dart'; import 'package:shadcn_ui/src/theme/components/switch.dart'; import 'package:shadcn_ui/src/theme/components/table.dart'; import 'package:shadcn_ui/src/theme/components/tabs.dart'; +import 'package:shadcn_ui/src/theme/components/time_picker.dart'; import 'package:shadcn_ui/src/theme/components/toast.dart'; import 'package:shadcn_ui/src/theme/components/tooltip.dart'; import 'package:shadcn_ui/src/theme/text_theme/theme.dart'; @@ -86,6 +87,7 @@ class ShadThemeData extends ShadBaseTheme { ShadContextMenuTheme? contextMenuTheme, ShadCalendarTheme? calendarTheme, ShadDatePickerTheme? datePickerTheme, + ShadTimePickerTheme? timePickerTheme, }) { final effectiveRadius = radius ?? const BorderRadius.all(Radius.circular(6)); @@ -225,6 +227,8 @@ class ShadThemeData extends ShadBaseTheme { calendarTheme: effectiveVariant.calendarTheme().mergeWith(calendarTheme), datePickerTheme: effectiveVariant.datePickerTheme().mergeWith(datePickerTheme), + timePickerTheme: + effectiveVariant.timePickerTheme().mergeWith(timePickerTheme), ); } @@ -276,6 +280,7 @@ class ShadThemeData extends ShadBaseTheme { required super.contextMenuTheme, required super.calendarTheme, required super.datePickerTheme, + required super.timePickerTheme, }); static ShadThemeData lerp(ShadThemeData a, ShadThemeData b, double t) { @@ -374,6 +379,8 @@ class ShadThemeData extends ShadBaseTheme { ShadCalendarTheme.lerp(a.calendarTheme, b.calendarTheme, t), datePickerTheme: ShadDatePickerTheme.lerp(a.datePickerTheme, b.datePickerTheme, t), + timePickerTheme: + ShadTimePickerTheme.lerp(a.timePickerTheme, b.timePickerTheme, t), ); } @@ -428,7 +435,8 @@ class ShadThemeData extends ShadBaseTheme { other.tabsTheme == tabsTheme && other.contextMenuTheme == contextMenuTheme && other.calendarTheme == calendarTheme && - other.datePickerTheme == datePickerTheme; + other.datePickerTheme == datePickerTheme && + other.timePickerTheme == timePickerTheme; } @override @@ -479,7 +487,8 @@ class ShadThemeData extends ShadBaseTheme { tabsTheme.hashCode ^ contextMenuTheme.hashCode ^ calendarTheme.hashCode ^ - datePickerTheme.hashCode; + datePickerTheme.hashCode ^ + timePickerTheme.hashCode; } ShadThemeData copyWith({ @@ -530,6 +539,7 @@ class ShadThemeData extends ShadBaseTheme { ShadContextMenuTheme? contextMenuTheme, ShadCalendarTheme? calendarTheme, ShadDatePickerTheme? datePickerTheme, + ShadTimePickerTheme? timePickerTheme, }) { return ShadThemeData( colorScheme: colorScheme ?? this.colorScheme, @@ -584,6 +594,7 @@ class ShadThemeData extends ShadBaseTheme { contextMenuTheme: contextMenuTheme ?? this.contextMenuTheme, calendarTheme: calendarTheme ?? this.calendarTheme, datePickerTheme: datePickerTheme ?? this.datePickerTheme, + timePickerTheme: timePickerTheme ?? this.timePickerTheme, ); } } diff --git a/lib/src/theme/themes/base.dart b/lib/src/theme/themes/base.dart index a213619d..cbc77de7 100644 --- a/lib/src/theme/themes/base.dart +++ b/lib/src/theme/themes/base.dart @@ -24,6 +24,7 @@ import 'package:shadcn_ui/src/theme/components/slider.dart'; import 'package:shadcn_ui/src/theme/components/switch.dart'; import 'package:shadcn_ui/src/theme/components/table.dart'; import 'package:shadcn_ui/src/theme/components/tabs.dart'; +import 'package:shadcn_ui/src/theme/components/time_picker.dart'; import 'package:shadcn_ui/src/theme/components/toast.dart'; import 'package:shadcn_ui/src/theme/components/tooltip.dart'; import 'package:shadcn_ui/src/theme/text_theme/theme.dart'; @@ -80,6 +81,7 @@ abstract class ShadBaseTheme { required this.contextMenuTheme, required this.calendarTheme, required this.datePickerTheme, + required this.timePickerTheme, }); final ShadColorScheme colorScheme; @@ -129,6 +131,7 @@ abstract class ShadBaseTheme { final ShadContextMenuTheme contextMenuTheme; final ShadCalendarTheme calendarTheme; final ShadDatePickerTheme datePickerTheme; + final ShadTimePickerTheme timePickerTheme; } @immutable @@ -173,4 +176,5 @@ abstract class ShadThemeVariant { ShadContextMenuTheme contextMenuTheme(); ShadCalendarTheme calendarTheme(); ShadDatePickerTheme datePickerTheme(); + ShadTimePickerTheme timePickerTheme(); } diff --git a/lib/src/theme/themes/default_theme_no_secondary_border_variant.dart b/lib/src/theme/themes/default_theme_no_secondary_border_variant.dart index 7b84f939..9d174bf1 100644 --- a/lib/src/theme/themes/default_theme_no_secondary_border_variant.dart +++ b/lib/src/theme/themes/default_theme_no_secondary_border_variant.dart @@ -32,6 +32,7 @@ import 'package:shadcn_ui/src/theme/components/slider.dart'; import 'package:shadcn_ui/src/theme/components/switch.dart'; import 'package:shadcn_ui/src/theme/components/table.dart'; import 'package:shadcn_ui/src/theme/components/tabs.dart'; +import 'package:shadcn_ui/src/theme/components/time_picker.dart'; import 'package:shadcn_ui/src/theme/components/toast.dart'; import 'package:shadcn_ui/src/theme/components/tooltip.dart'; import 'package:shadcn_ui/src/theme/text_theme/text_styles_default.dart'; @@ -867,4 +868,32 @@ class ShadDefaultThemeNoSecondaryBorderVariant extends ShadThemeVariant { iconSrc: LucideIcons.calendar, ); } + + @override + ShadTimePickerTheme timePickerTheme() { + return ShadTimePickerTheme( + axis: Axis.horizontal, + spacing: 0, + runSpacing: 0, + jumpToNextFieldWhenFilled: true, + alignment: WrapAlignment.center, + runAlignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + gap: 2, + style: effectiveTextTheme.muted.copyWith( + color: colorScheme.foreground, + fontSize: 16, + height: 24 / 16, + ), + placeholderStyle: defaultTextTheme.large.copyWith( + fontWeight: FontWeight.w500, + color: colorScheme.foreground, + ), + labelStyle: defaultTextTheme.small.copyWith(fontSize: 12), + fieldWidth: 58, + fieldPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + periodHeight: 50, + periodMinWidth: 65, + ); + } } diff --git a/lib/src/theme/themes/default_theme_variant.dart b/lib/src/theme/themes/default_theme_variant.dart index 02466911..4f414b47 100644 --- a/lib/src/theme/themes/default_theme_variant.dart +++ b/lib/src/theme/themes/default_theme_variant.dart @@ -32,6 +32,7 @@ import 'package:shadcn_ui/src/theme/components/slider.dart'; import 'package:shadcn_ui/src/theme/components/switch.dart'; import 'package:shadcn_ui/src/theme/components/table.dart'; import 'package:shadcn_ui/src/theme/components/tabs.dart'; +import 'package:shadcn_ui/src/theme/components/time_picker.dart'; import 'package:shadcn_ui/src/theme/components/toast.dart'; import 'package:shadcn_ui/src/theme/components/tooltip.dart'; import 'package:shadcn_ui/src/theme/text_theme/text_styles_default.dart'; @@ -840,4 +841,32 @@ class ShadDefaultThemeVariant extends ShadThemeVariant { iconSrc: LucideIcons.calendar, ); } + + @override + ShadTimePickerTheme timePickerTheme() { + return ShadTimePickerTheme( + axis: Axis.horizontal, + spacing: 0, + runSpacing: 0, + jumpToNextFieldWhenFilled: true, + alignment: WrapAlignment.center, + runAlignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + gap: 2, + style: effectiveTextTheme.muted.copyWith( + color: colorScheme.foreground, + fontSize: 16, + height: 24 / 16, + ), + placeholderStyle: defaultTextTheme.large.copyWith( + fontWeight: FontWeight.w500, + color: colorScheme.foreground, + ), + labelStyle: defaultTextTheme.small.copyWith(fontSize: 12), + fieldWidth: 58, + fieldPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + periodHeight: 50, + periodMinWidth: 65, + ); + } }