사용하지 않는 파일 정리 전 백업 (Phase 10 완료 후 상태)
This commit is contained in:
368
lib/widgets/shadcn/shad_date_picker.dart
Normal file
368
lib/widgets/shadcn/shad_date_picker.dart
Normal file
@@ -0,0 +1,368 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
class SuperportShadDatePicker extends StatefulWidget {
|
||||
final String label;
|
||||
final DateTime? value;
|
||||
final ValueChanged<DateTime?>? onChanged;
|
||||
final DateTime? firstDate;
|
||||
final DateTime? lastDate;
|
||||
final String dateFormat;
|
||||
final String? placeholder;
|
||||
final bool enabled;
|
||||
final bool required;
|
||||
final String? errorText;
|
||||
final String? helperText;
|
||||
final bool allowClear;
|
||||
final DatePickerMode mode;
|
||||
|
||||
const SuperportShadDatePicker({
|
||||
super.key,
|
||||
required this.label,
|
||||
this.value,
|
||||
this.onChanged,
|
||||
this.firstDate,
|
||||
this.lastDate,
|
||||
this.dateFormat = 'yyyy-MM-dd',
|
||||
this.placeholder,
|
||||
this.enabled = true,
|
||||
this.required = false,
|
||||
this.errorText,
|
||||
this.helperText,
|
||||
this.allowClear = true,
|
||||
this.mode = DatePickerMode.day,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SuperportShadDatePicker> createState() => _SuperportShadDatePickerState();
|
||||
}
|
||||
|
||||
class _SuperportShadDatePickerState extends State<SuperportShadDatePicker> {
|
||||
late TextEditingController _controller;
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController(
|
||||
text: widget.value != null
|
||||
? DateFormat(widget.dateFormat).format(widget.value!)
|
||||
: '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(SuperportShadDatePicker oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.value != widget.value) {
|
||||
_controller.text = widget.value != null
|
||||
? DateFormat(widget.dateFormat).format(widget.value!)
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _selectDate() async {
|
||||
final theme = ShadTheme.of(context);
|
||||
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: widget.value ?? DateTime.now(),
|
||||
firstDate: widget.firstDate ?? DateTime(1900),
|
||||
lastDate: widget.lastDate ?? DateTime(2100),
|
||||
initialDatePickerMode: widget.mode,
|
||||
locale: const Locale('ko', 'KR'),
|
||||
builder: (context, child) {
|
||||
return Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
primaryColor: theme.colorScheme.primary,
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: theme.colorScheme.primary,
|
||||
onPrimary: theme.colorScheme.primaryForeground,
|
||||
surface: theme.colorScheme.card,
|
||||
onSurface: theme.colorScheme.cardForeground,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (picked != null) {
|
||||
widget.onChanged?.call(picked);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
widget.label,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
if (widget.required)
|
||||
Text(
|
||||
' *',
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.destructive,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: widget.enabled ? _selectDate : null,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: IgnorePointer(
|
||||
child: ShadInput(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
placeholder: Text(widget.placeholder ?? 'YYYY-MM-DD'),
|
||||
enabled: widget.enabled,
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.errorText != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
widget.errorText!,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.destructive,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (widget.helperText != null && widget.errorText == null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
widget.helperText!,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.mutedForeground,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SuperportShadDateRangePicker extends StatefulWidget {
|
||||
final String label;
|
||||
final DateTimeRange? value;
|
||||
final ValueChanged<DateTimeRange?>? onChanged;
|
||||
final DateTime? firstDate;
|
||||
final DateTime? lastDate;
|
||||
final String dateFormat;
|
||||
final String? placeholder;
|
||||
final bool enabled;
|
||||
final bool required;
|
||||
final String? errorText;
|
||||
final String? helperText;
|
||||
final bool allowClear;
|
||||
|
||||
const SuperportShadDateRangePicker({
|
||||
super.key,
|
||||
required this.label,
|
||||
this.value,
|
||||
this.onChanged,
|
||||
this.firstDate,
|
||||
this.lastDate,
|
||||
this.dateFormat = 'yyyy-MM-dd',
|
||||
this.placeholder,
|
||||
this.enabled = true,
|
||||
this.required = false,
|
||||
this.errorText,
|
||||
this.helperText,
|
||||
this.allowClear = true,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SuperportShadDateRangePicker> createState() => _SuperportShadDateRangePickerState();
|
||||
}
|
||||
|
||||
class _SuperportShadDateRangePickerState extends State<SuperportShadDateRangePicker> {
|
||||
late TextEditingController _controller;
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_updateControllerText();
|
||||
}
|
||||
|
||||
void _updateControllerText() {
|
||||
if (widget.value != null) {
|
||||
final startText = DateFormat(widget.dateFormat).format(widget.value!.start);
|
||||
final endText = DateFormat(widget.dateFormat).format(widget.value!.end);
|
||||
_controller = TextEditingController(text: '$startText ~ $endText');
|
||||
} else {
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(SuperportShadDateRangePicker oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.value != widget.value) {
|
||||
_updateControllerText();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _selectDateRange() async {
|
||||
final theme = ShadTheme.of(context);
|
||||
|
||||
final DateTimeRange? picked = await showDateRangePicker(
|
||||
context: context,
|
||||
initialDateRange: widget.value,
|
||||
firstDate: widget.firstDate ?? DateTime(1900),
|
||||
lastDate: widget.lastDate ?? DateTime(2100),
|
||||
locale: const Locale('ko', 'KR'),
|
||||
builder: (context, child) {
|
||||
return Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
primaryColor: theme.colorScheme.primary,
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: theme.colorScheme.primary,
|
||||
onPrimary: theme.colorScheme.primaryForeground,
|
||||
surface: theme.colorScheme.card,
|
||||
onSurface: theme.colorScheme.cardForeground,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (picked != null) {
|
||||
widget.onChanged?.call(picked);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
widget.label,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
if (widget.required)
|
||||
Text(
|
||||
' *',
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.destructive,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: widget.enabled ? _selectDateRange : null,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: IgnorePointer(
|
||||
child: ShadInput(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
placeholder: Text(widget.placeholder ?? 'YYYY-MM-DD ~ YYYY-MM-DD'),
|
||||
enabled: widget.enabled,
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.errorText != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
widget.errorText!,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.destructive,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (widget.helperText != null && widget.errorText == null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
widget.helperText!,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.mutedForeground,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KoreanDateTimeFormatter {
|
||||
static String format(DateTime dateTime, {String pattern = 'yyyy년 MM월 dd일 (E)'}) {
|
||||
final formatter = DateFormat(pattern, 'ko_KR');
|
||||
return formatter.format(dateTime);
|
||||
}
|
||||
|
||||
static String formatRange(DateTimeRange range, {String pattern = 'yyyy.MM.dd'}) {
|
||||
final formatter = DateFormat(pattern, 'ko_KR');
|
||||
return '${formatter.format(range.start)} ~ ${formatter.format(range.end)}';
|
||||
}
|
||||
|
||||
static String formatRelative(DateTime dateTime) {
|
||||
final now = DateTime.now();
|
||||
final difference = now.difference(dateTime);
|
||||
|
||||
if (difference.inDays == 0) {
|
||||
if (difference.inHours == 0) {
|
||||
if (difference.inMinutes == 0) {
|
||||
return '방금 전';
|
||||
}
|
||||
return '${difference.inMinutes}분 전';
|
||||
}
|
||||
return '${difference.inHours}시간 전';
|
||||
} else if (difference.inDays == 1) {
|
||||
return '어제';
|
||||
} else if (difference.inDays == 2) {
|
||||
return '그저께';
|
||||
} else if (difference.inDays < 7) {
|
||||
return '${difference.inDays}일 전';
|
||||
} else if (difference.inDays < 30) {
|
||||
return '${(difference.inDays / 7).round()}주 전';
|
||||
} else if (difference.inDays < 365) {
|
||||
return '${(difference.inDays / 30).round()}개월 전';
|
||||
} else {
|
||||
return '${(difference.inDays / 365).round()}년 전';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user