주석화 진행상황 정리하고 핵심 모듈에 한글 주석 추가
This commit is contained in:
@@ -6,6 +6,7 @@ import '../core/constants/app_sections.dart';
|
||||
import '../core/theme/theme_controller.dart';
|
||||
import '../core/permissions/permission_manager.dart';
|
||||
|
||||
/// 앱 기본 레이아웃을 제공하는 셸 위젯. 사이드 네비게이션과 AppBar를 구성한다.
|
||||
class AppShell extends StatelessWidget {
|
||||
const AppShell({
|
||||
super.key,
|
||||
@@ -248,10 +249,17 @@ class _ThemeMenuButton extends StatelessWidget {
|
||||
(value) => PopupMenuItem<ThemeMode>(
|
||||
value: value,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(_icon(value), size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Text(_label(value)),
|
||||
Flexible(
|
||||
child: Text(
|
||||
_label(value),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -265,10 +273,18 @@ class _ThemeMenuButton extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Text('테마 · $label', style: theme.textTheme.labelSmall),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'테마 · $label',
|
||||
style: theme.textTheme.labelSmall,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -5,22 +5,27 @@ import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
class SuperportToast {
|
||||
SuperportToast._();
|
||||
|
||||
/// 성공 처리 완료를 사용자에게 안내한다.
|
||||
static void success(BuildContext context, String message) {
|
||||
_show(context, message, _ToastVariant.success);
|
||||
}
|
||||
|
||||
/// 정보성 피드백을 노출한다.
|
||||
static void info(BuildContext context, String message) {
|
||||
_show(context, message, _ToastVariant.info);
|
||||
}
|
||||
|
||||
/// 주의가 필요한 상황을 경고한다.
|
||||
static void warning(BuildContext context, String message) {
|
||||
_show(context, message, _ToastVariant.warning);
|
||||
}
|
||||
|
||||
/// 오류 발생 시 스낵바를 표시한다.
|
||||
static void error(BuildContext context, String message) {
|
||||
_show(context, message, _ToastVariant.error);
|
||||
}
|
||||
|
||||
/// 공통 스낵바 렌더링 로직.
|
||||
static void _show(
|
||||
BuildContext context,
|
||||
String message,
|
||||
@@ -106,9 +111,13 @@ class SuperportSkeletonList extends StatelessWidget {
|
||||
this.padding = const EdgeInsets.all(16),
|
||||
});
|
||||
|
||||
/// 렌더링할 스켈레톤 행 개수.
|
||||
final int itemCount;
|
||||
/// 각 항목 높이.
|
||||
final double height;
|
||||
/// 행 사이 간격.
|
||||
final double gap;
|
||||
/// 전체 패딩.
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
@override
|
||||
|
||||
@@ -157,8 +157,13 @@ class FilterBarActionConfig {
|
||||
final Key? applyKey;
|
||||
final Key? resetKey;
|
||||
|
||||
/// 즉시 적용 가능한지 여부.
|
||||
bool get canApply => applyEnabled ?? hasPendingChanges;
|
||||
|
||||
/// Reset 버튼을 노출할지 여부.
|
||||
bool get shouldShowReset =>
|
||||
showReset ?? (hasActiveFilters || hasPendingChanges);
|
||||
|
||||
/// Reset 버튼이 활성화 가능한지 여부.
|
||||
bool get canReset => resetEnabled ?? shouldShowReset;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,19 @@ class SuperportFormField extends StatelessWidget {
|
||||
this.spacing = _kFieldSpacing,
|
||||
});
|
||||
|
||||
/// 폼 필드 라벨 텍스트.
|
||||
final String label;
|
||||
/// 입력 영역으로 렌더링할 위젯.
|
||||
final Widget child;
|
||||
/// 필수 여부. true면 라벨 옆에 `*` 표시를 추가한다.
|
||||
final bool required;
|
||||
/// 보조 설명 문구. 에러가 없을 때만 출력된다.
|
||||
final String? caption;
|
||||
/// 에러 메시지. 존재하면 캡션 대신 우선적으로 노출된다.
|
||||
final String? errorText;
|
||||
/// 라벨 우측에 배치할 추가 위젯(예: 도움말 버튼).
|
||||
final Widget? trailing;
|
||||
/// 라벨과 본문 사이 간격.
|
||||
final double spacing;
|
||||
|
||||
@override
|
||||
@@ -81,14 +88,23 @@ class SuperportTextInput extends StatelessWidget {
|
||||
});
|
||||
|
||||
final TextEditingController? controller;
|
||||
/// 입력 없을 때 보여줄 플레이스홀더 위젯.
|
||||
final Widget? placeholder;
|
||||
/// 입력 변경 콜백.
|
||||
final ValueChanged<String>? onChanged;
|
||||
/// 제출(Enter) 시 호출되는 콜백.
|
||||
final ValueChanged<String>? onSubmitted;
|
||||
/// 키보드 타입. 숫자/이메일 등으로 지정 가능.
|
||||
final TextInputType? keyboardType;
|
||||
/// 입력 활성 여부.
|
||||
final bool enabled;
|
||||
/// 읽기 전용 여부. true면 수정 불가.
|
||||
final bool readOnly;
|
||||
/// 최대 줄 수. 1보다 크면 멀티라인 입력을 지원한다.
|
||||
final int maxLines;
|
||||
/// 앞에 붙일 위젯 (아이콘 등).
|
||||
final Widget? leading;
|
||||
/// 뒤에 붙일 위젯 (버튼 등).
|
||||
final Widget? trailing;
|
||||
|
||||
@override
|
||||
@@ -118,9 +134,13 @@ class SuperportSwitchField extends StatelessWidget {
|
||||
this.caption,
|
||||
});
|
||||
|
||||
/// 스위치 현재 상태.
|
||||
final bool value;
|
||||
/// 상태 변경 시 호출되는 콜백.
|
||||
final ValueChanged<bool> onChanged;
|
||||
/// 스위치 상단에 표시할 제목.
|
||||
final String? label;
|
||||
/// 보조 설명 문구.
|
||||
final String? caption;
|
||||
|
||||
@override
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
/// 다이얼로그에서 ESC/Enter 키를 처리하고 포커스를 트랩하는 래퍼 위젯.
|
||||
class DialogKeyboardShortcuts extends StatefulWidget {
|
||||
const DialogKeyboardShortcuts({
|
||||
super.key,
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
/// 데스크톱 레이아웃으로 간주할 최소 너비(px).
|
||||
const double desktopBreakpoint = 1200;
|
||||
/// 태블릿 레이아웃을 구분하는 최소 너비(px).
|
||||
const double tabletBreakpoint = 960;
|
||||
|
||||
/// 뷰포트 크기별 분기값.
|
||||
enum DeviceBreakpoint { mobile, tablet, desktop }
|
||||
|
||||
/// 현재 화면 너비에 맞는 [DeviceBreakpoint]를 계산한다.
|
||||
DeviceBreakpoint breakpointForWidth(double width) {
|
||||
if (width >= desktopBreakpoint) {
|
||||
return DeviceBreakpoint.desktop;
|
||||
@@ -15,34 +19,52 @@ DeviceBreakpoint breakpointForWidth(double width) {
|
||||
return DeviceBreakpoint.mobile;
|
||||
}
|
||||
|
||||
/// 주어진 너비가 데스크톱 분기에 해당하는지 여부.
|
||||
bool isDesktop(double width) => width >= desktopBreakpoint;
|
||||
|
||||
/// 주어진 너비가 태블릿 분기에 해당하는지 여부.
|
||||
bool isTablet(double width) =>
|
||||
width >= tabletBreakpoint && width < desktopBreakpoint;
|
||||
|
||||
/// 주어진 너비가 모바일 분기에 해당하는지 여부.
|
||||
bool isMobile(double width) => width < tabletBreakpoint;
|
||||
|
||||
/// 컨텍스트 기반으로 데스크톱 범위인지 확인한다.
|
||||
bool isDesktopContext(BuildContext context) =>
|
||||
isDesktop(MediaQuery.of(context).size.width);
|
||||
|
||||
/// 컨텍스트 기반으로 태블릿 범위인지 확인한다.
|
||||
bool isTabletContext(BuildContext context) =>
|
||||
isTablet(MediaQuery.of(context).size.width);
|
||||
|
||||
/// 컨텍스트 기반으로 모바일 범위인지 확인한다.
|
||||
bool isMobileContext(BuildContext context) =>
|
||||
isMobile(MediaQuery.of(context).size.width);
|
||||
|
||||
/// 반응형 분기 정보를 담는 값 객체.
|
||||
class ResponsiveBreakpoints {
|
||||
ResponsiveBreakpoints._(this.width) : breakpoint = breakpointForWidth(width);
|
||||
|
||||
/// 현재 뷰 가로 너비.
|
||||
final double width;
|
||||
/// 너비에서 계산된 분기값.
|
||||
final DeviceBreakpoint breakpoint;
|
||||
|
||||
/// 모바일 범위인지 여부.
|
||||
bool get isMobile => breakpoint == DeviceBreakpoint.mobile;
|
||||
/// 태블릿 범위인지 여부.
|
||||
bool get isTablet => breakpoint == DeviceBreakpoint.tablet;
|
||||
/// 데스크톱 범위인지 여부.
|
||||
bool get isDesktop => breakpoint == DeviceBreakpoint.desktop;
|
||||
|
||||
/// 현재 컨텍스트에서 [ResponsiveBreakpoints]를 생성한다.
|
||||
static ResponsiveBreakpoints of(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
return ResponsiveBreakpoints._(size.width);
|
||||
}
|
||||
}
|
||||
|
||||
/// 기기 타입에 따라 위젯 빌더를 분기 실행하는 레이아웃 헬퍼.
|
||||
class ResponsiveLayoutBuilder extends StatelessWidget {
|
||||
const ResponsiveLayoutBuilder({
|
||||
super.key,
|
||||
@@ -51,8 +73,11 @@ class ResponsiveLayoutBuilder extends StatelessWidget {
|
||||
required this.desktop,
|
||||
});
|
||||
|
||||
/// 모바일 뷰에서 사용할 빌더.
|
||||
final WidgetBuilder mobile;
|
||||
/// 태블릿 뷰에서 사용할 빌더. 제공되지 않으면 데스크톱 빌더를 재사용한다.
|
||||
final WidgetBuilder? tablet;
|
||||
/// 데스크톱 뷰에서 사용할 빌더.
|
||||
final WidgetBuilder desktop;
|
||||
|
||||
@override
|
||||
@@ -74,6 +99,7 @@ class ResponsiveLayoutBuilder extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 특정 분기에서만 child를 표시하는 헬퍼 위젯.
|
||||
class ResponsiveVisibility extends StatelessWidget {
|
||||
const ResponsiveVisibility({
|
||||
super.key,
|
||||
@@ -86,8 +112,11 @@ class ResponsiveVisibility extends StatelessWidget {
|
||||
},
|
||||
});
|
||||
|
||||
/// 조건을 만족할 때 보여줄 실제 위젯.
|
||||
final Widget child;
|
||||
/// 조건을 만족하지 않을 때 대체로 렌더링할 위젯.
|
||||
final Widget replacement;
|
||||
/// 어떤 분기에서 child를 노출할지 정의한 집합.
|
||||
final Set<DeviceBreakpoint> visibleOn;
|
||||
|
||||
@override
|
||||
|
||||
@@ -62,6 +62,7 @@ class SuperportDialog extends StatelessWidget {
|
||||
final FutureOr<void> Function()? onSubmit;
|
||||
final bool enableFocusTrap;
|
||||
|
||||
/// 공통 다이얼로그를 노출하는 헬퍼. `showDialog`와 동일하게 동작한다.
|
||||
static Future<T?> show<T>({
|
||||
required BuildContext context,
|
||||
required SuperportDialog dialog,
|
||||
@@ -285,7 +286,7 @@ class _SuperportDialogHeader extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience wrapper around [SuperportDialog.show] to reduce boilerplate in pages.
|
||||
/// 페이지에서 반복되는 호출 패턴을 줄이기 위한 편의 함수.
|
||||
Future<T?> showSuperportDialog<T>({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
|
||||
@@ -11,7 +11,10 @@ class SuperportTableSortState {
|
||||
required this.ascending,
|
||||
});
|
||||
|
||||
/// 정렬 대상이 되는 컬럼 인덱스.
|
||||
final int columnIndex;
|
||||
|
||||
/// 오름차순 여부. `false`면 내림차순이다.
|
||||
final bool ascending;
|
||||
}
|
||||
|
||||
@@ -25,10 +28,19 @@ class SuperportTablePagination {
|
||||
this.pageSizeOptions = const <int>[10, 20, 50],
|
||||
});
|
||||
|
||||
/// 현재 페이지 번호(1-base).
|
||||
final int currentPage;
|
||||
|
||||
/// 전체 페이지 수.
|
||||
final int totalPages;
|
||||
|
||||
/// 전체 데이터 건수.
|
||||
final int totalItems;
|
||||
|
||||
/// 현재 페이지네이션에서 선택된 페이지 크기.
|
||||
final int pageSize;
|
||||
|
||||
/// 사용자에게 노출할 페이지 크기 옵션 목록.
|
||||
final List<int> pageSizeOptions;
|
||||
}
|
||||
|
||||
@@ -55,6 +67,7 @@ class SuperportTable extends StatelessWidget {
|
||||
_headerCells = null,
|
||||
_rowCells = null;
|
||||
|
||||
/// 헤더와 행을 [ShadTableCell] 단위로 직접 전달할 때 사용하는 생성자.
|
||||
const SuperportTable.fromCells({
|
||||
super.key,
|
||||
required List<ShadTableCell> header,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
/// 기능 플래그 비활성화 시 사양 정보를 보여주기 위한 테이블 정의.
|
||||
class SpecTable {
|
||||
const SpecTable({
|
||||
required this.columns,
|
||||
@@ -13,6 +14,7 @@ class SpecTable {
|
||||
final double? columnWidth;
|
||||
}
|
||||
|
||||
/// 사양 페이지의 섹션 정보를 표현한다.
|
||||
class SpecSection {
|
||||
const SpecSection({
|
||||
required this.title,
|
||||
@@ -27,6 +29,7 @@ class SpecSection {
|
||||
final SpecTable? table;
|
||||
}
|
||||
|
||||
/// 기능 비활성 시 화면 대신 노출하는 사양 설명 페이지.
|
||||
class SpecPage extends StatelessWidget {
|
||||
const SpecPage({
|
||||
super.key,
|
||||
@@ -139,6 +142,7 @@ class SpecPage extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 사양 테이블을 렌더링하는 내부 위젯.
|
||||
class _SpecTableView extends StatelessWidget {
|
||||
const _SpecTableView({required this.table});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user