feat(pagination): 공통 컨트롤 도입과 사용자 관리 가이드 추가
- 테이블 푸터에서 SuperportPaginationControls를 사용하도록 각 관리 페이지 페이지네이션 로직을 정리 - SuperportPaginationControls 위젯을 추가하고 SuperportTable 푸터를 개선해 페이지 사이즈 선택과 이동 버튼을 분리 - 사용자 등록·계정 관리 요구사항을 문서화한 doc/user_setting.md를 작성하고 AGENTS.md 코멘트 규칙을 업데이트 - flutter analyze를 수행해 빌드 경고가 없음을 확인
This commit is contained in:
98
lib/widgets/components/superport_pagination_controls.dart
Normal file
98
lib/widgets/components/superport_pagination_controls.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
/// 페이지 이동용 <<, <, >, >> 버튼을 공통 스타일로 제공하는 위젯.
|
||||
class SuperportPaginationControls extends StatelessWidget {
|
||||
const SuperportPaginationControls({
|
||||
super.key,
|
||||
required this.currentPage,
|
||||
required this.totalPages,
|
||||
this.onPageSelected,
|
||||
this.isBusy = false,
|
||||
this.size = ShadButtonSize.sm,
|
||||
this.spacing = 8,
|
||||
this.mainAxisSize = MainAxisSize.min,
|
||||
});
|
||||
|
||||
/// 현재 선택된 페이지 번호(1-base).
|
||||
final int currentPage;
|
||||
|
||||
/// 전체 페이지 수.
|
||||
final int totalPages;
|
||||
|
||||
/// 페이지 변경 콜백. `null`이면 모든 버튼을 비활성화한다.
|
||||
final void Function(int page)? onPageSelected;
|
||||
|
||||
/// 비동기 작업 중 여부. `true`면 버튼이 비활성화된다.
|
||||
final bool isBusy;
|
||||
|
||||
/// 버튼 크기 옵션.
|
||||
final ShadButtonSize size;
|
||||
|
||||
/// 버튼 간 간격.
|
||||
final double spacing;
|
||||
|
||||
/// 내부 Row의 MainAxisSize.
|
||||
final MainAxisSize mainAxisSize;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final int safeTotal = totalPages <= 0 ? 1 : totalPages;
|
||||
final int safeCurrent = currentPage < 1
|
||||
? 1
|
||||
: (currentPage > safeTotal ? safeTotal : currentPage);
|
||||
final bool canInteract = onPageSelected != null && !isBusy;
|
||||
final bool canGoPrev = canInteract && safeCurrent > 1;
|
||||
final bool canGoNext = canInteract && safeCurrent < safeTotal;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: mainAxisSize,
|
||||
children: _withSpacing([
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronsLeft,
|
||||
enabled: canGoPrev,
|
||||
onTap: () => onPageSelected?.call(1),
|
||||
),
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronLeft,
|
||||
enabled: canGoPrev,
|
||||
onTap: () => onPageSelected?.call(safeCurrent - 1),
|
||||
),
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronRight,
|
||||
enabled: canGoNext,
|
||||
onTap: () => onPageSelected?.call(safeCurrent + 1),
|
||||
),
|
||||
_buildButton(
|
||||
icon: lucide.LucideIcons.chevronsRight,
|
||||
enabled: canGoNext,
|
||||
onTap: () => onPageSelected?.call(safeTotal),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _withSpacing(List<Widget> children) {
|
||||
final result = <Widget>[];
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
if (i > 0) {
|
||||
result.add(SizedBox(width: spacing));
|
||||
}
|
||||
result.add(children[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Widget _buildButton({
|
||||
required IconData icon,
|
||||
required bool enabled,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return ShadButton.ghost(
|
||||
size: size,
|
||||
onPressed: enabled ? onTap : null,
|
||||
child: Icon(icon, size: 16),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:superport_v2/widgets/components/superport_pagination_controls.dart';
|
||||
|
||||
/// 테이블 정렬 상태 정보를 보관하는 모델.
|
||||
class SuperportTableSortState {
|
||||
@@ -276,15 +277,14 @@ class _PaginationFooter extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
final int totalPages =
|
||||
pagination.totalPages <= 0 ? 1 : pagination.totalPages;
|
||||
final int totalPages = pagination.totalPages <= 0
|
||||
? 1
|
||||
: pagination.totalPages;
|
||||
final int currentPage = pagination.currentPage < 1
|
||||
? 1
|
||||
: (pagination.currentPage > totalPages
|
||||
? totalPages
|
||||
: pagination.currentPage);
|
||||
final canGoPrev = currentPage > 1 && !isLoading;
|
||||
final canGoNext = currentPage < totalPages && !isLoading;
|
||||
? totalPages
|
||||
: pagination.currentPage);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -316,33 +316,11 @@ class _PaginationFooter extends StatelessWidget {
|
||||
style: theme.textTheme.small,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: canGoPrev ? () => onPageChange?.call(1) : null,
|
||||
child: const Icon(lucide.LucideIcons.chevronsLeft, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: canGoPrev
|
||||
? () => onPageChange?.call(currentPage - 1)
|
||||
: null,
|
||||
child: const Icon(lucide.LucideIcons.chevronLeft, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: canGoNext
|
||||
? () => onPageChange?.call(currentPage + 1)
|
||||
: null,
|
||||
child: const Icon(lucide.LucideIcons.chevronRight, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed:
|
||||
canGoNext ? () => onPageChange?.call(totalPages) : null,
|
||||
child: const Icon(lucide.LucideIcons.chevronsRight, size: 16),
|
||||
SuperportPaginationControls(
|
||||
currentPage: currentPage,
|
||||
totalPages: totalPages,
|
||||
onPageSelected: onPageChange,
|
||||
isBusy: isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user