WIP: feat(ui): Migrate to new UI implementation
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
import 'main.directories.g.dart';
|
||||
|
||||
void main() => runApp(const MoonWellWidgetbook());
|
||||
|
||||
@widgetbook.App()
|
||||
class MoonWellWidgetbook extends StatelessWidget {
|
||||
const MoonWellWidgetbook({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Widgetbook.material(
|
||||
initialRoute: '/?path=compositions/mwlaunchershell/ready',
|
||||
directories: directories,
|
||||
addons: [
|
||||
MaterialThemeAddon(
|
||||
themes: [
|
||||
WidgetbookTheme(
|
||||
name: 'Forest',
|
||||
data: MoonWellTheme.create(
|
||||
MoonWellThemeVariant.forest,
|
||||
fontPackage: 'moonwell_launcher',
|
||||
),
|
||||
),
|
||||
WidgetbookTheme(
|
||||
name: 'Temple',
|
||||
data: MoonWellTheme.create(
|
||||
MoonWellThemeVariant.temple,
|
||||
fontPackage: 'moonwell_launcher',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
ViewportAddon(const [
|
||||
ViewportData(
|
||||
name: 'Minimum desktop · 1024×640',
|
||||
width: 1024,
|
||||
height: 640,
|
||||
pixelRatio: 1,
|
||||
platform: TargetPlatform.windows,
|
||||
),
|
||||
ViewportData(
|
||||
name: 'Target desktop · 1280×720',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
pixelRatio: 1,
|
||||
platform: TargetPlatform.windows,
|
||||
),
|
||||
ViewportData(
|
||||
name: 'Large desktop · 1600×900',
|
||||
width: 1600,
|
||||
height: 900,
|
||||
pixelRatio: 1,
|
||||
platform: TargetPlatform.windows,
|
||||
),
|
||||
]),
|
||||
TextScaleAddon(min: 1, max: 2, divisions: 4, initialScale: 1),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
// dart format width=80
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_import, prefer_relative_imports, directives_ordering
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
// **************************************************************************
|
||||
// AppGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:widgetbook/widgetbook.dart' as _widgetbook;
|
||||
import 'package:widgetbook_workspace/stories/authentication_stories.dart'
|
||||
as _widgetbook_workspace_stories_authentication_stories;
|
||||
import 'package:widgetbook_workspace/stories/foundations_stories.dart'
|
||||
as _widgetbook_workspace_stories_foundations_stories;
|
||||
import 'package:widgetbook_workspace/stories/launcher_stories.dart'
|
||||
as _widgetbook_workspace_stories_launcher_stories;
|
||||
import 'package:widgetbook_workspace/stories/primitives_stories.dart'
|
||||
as _widgetbook_workspace_stories_primitives_stories;
|
||||
import 'package:widgetbook_workspace/stories/shell_stories.dart'
|
||||
as _widgetbook_workspace_stories_shell_stories;
|
||||
import 'package:widgetbook_workspace/stories/window_chrome_stories.dart'
|
||||
as _widgetbook_workspace_stories_window_chrome_stories;
|
||||
|
||||
final directories = <_widgetbook.WidgetbookNode>[
|
||||
_widgetbook.WidgetbookCategory(
|
||||
name: 'Authentication',
|
||||
children: [
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwAuthenticationForm',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'api_error',
|
||||
builder: _widgetbook_workspace_stories_authentication_stories
|
||||
.buildMwAuthenticationFormApiErrorUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_authentication_stories
|
||||
.buildMwAuthenticationFormUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'loading',
|
||||
builder: _widgetbook_workspace_stories_authentication_stories
|
||||
.buildMwAuthenticationFormLoadingUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'long_russian_copy',
|
||||
builder: _widgetbook_workspace_stories_authentication_stories
|
||||
.buildMwAuthenticationFormLongRussianCopyUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookCategory(
|
||||
name: 'Compositions',
|
||||
children: [
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwLauncherShell',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'downloading',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLauncherShellDownloadingUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'empty_news',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLauncherShellEmptyNewsUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'failure',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLauncherShellFailureUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'paused',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLauncherShellPausedUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'ready',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLauncherShellReadyUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'verifying',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLauncherShellVerifyingUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwLoginShell',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'api_error',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLoginShellApiErrorUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLoginShellUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'restored_session_loading',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwLoginShellRestoredSessionLoadingUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookCategory(
|
||||
name: 'Foundations',
|
||||
children: [
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'ArtworkSlot',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_foundations_stories
|
||||
.buildArtworkSlotUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'FoundationsGallery',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_foundations_stories
|
||||
.buildFoundationsGalleryUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwArtworkBackground',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwArtworkBackgroundUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwBrandMark',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_shell_stories
|
||||
.buildMwBrandMarkUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookCategory(
|
||||
name: 'Launcher',
|
||||
children: [
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwAccountAffordance',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwAccountAffordanceUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwGameRail',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'compact',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwGameRailCompactUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'full',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwGameRailFullUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwGameTile',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwGameTileInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwLauncherStatusBar',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'metadata',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwLauncherStatusBarMetadataUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwNewsItem',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'image_failure',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwNewsItemImageFailureUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'text_only',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwNewsItemTextOnlyUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwNewsList',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'empty',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwNewsListEmptyUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'failure',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwNewsListFailureUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'loading',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwNewsListLoadingUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'populated',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwNewsListPopulatedUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwSettingsPanel',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwSettingsPanelInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwSyncActionArea',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_launcher_stories
|
||||
.buildMwSyncActionAreaInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookCategory(
|
||||
name: 'Primitives',
|
||||
children: [
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwBadge',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwBadgeInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwButton',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'disabled',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwButtonDisabledUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwButtonInteractiveUseCase,
|
||||
),
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'loading',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwButtonLoadingUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwCheckbox',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwCheckboxInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwDisabledFeature',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'default',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwDisabledFeatureUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwIconButton',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwIconButtonInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwPanel',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwPanelInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwProgressBar',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwProgressBarInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwStateView',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwStateViewInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwStatusText',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwStatusTextInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwTabs',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwTabsInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwTextField',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_primitives_stories
|
||||
.buildMwTextFieldInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
_widgetbook.WidgetbookCategory(
|
||||
name: 'Window Chrome',
|
||||
children: [
|
||||
_widgetbook.WidgetbookComponent(
|
||||
name: 'MwWindowChrome',
|
||||
useCases: [
|
||||
_widgetbook.WidgetbookUseCase(
|
||||
name: 'interactive',
|
||||
builder: _widgetbook_workspace_stories_window_chrome_stories
|
||||
.buildMwWindowChromeInteractiveUseCase,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_authentication.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'default',
|
||||
type: MwAuthenticationForm,
|
||||
path: '[Authentication]',
|
||||
)
|
||||
Widget buildMwAuthenticationFormUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 420,
|
||||
child: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(
|
||||
text: context.knobs.string(label: 'username', initialValue: ''),
|
||||
),
|
||||
passwordController: TextEditingController(
|
||||
text: context.knobs.string(label: 'password', initialValue: ''),
|
||||
),
|
||||
onSubmit: () => debugPrint('Authentication form submitted'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'loading',
|
||||
type: MwAuthenticationForm,
|
||||
path: '[Authentication]',
|
||||
)
|
||||
Widget buildMwAuthenticationFormLoadingUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 420,
|
||||
child: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(text: 'moonwalker'),
|
||||
passwordController: TextEditingController(text: 'password'),
|
||||
loading: true,
|
||||
onSubmit: () => debugPrint('Loading authentication form submitted'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'api_error',
|
||||
type: MwAuthenticationForm,
|
||||
path: '[Authentication]',
|
||||
)
|
||||
Widget buildMwAuthenticationFormApiErrorUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 420,
|
||||
child: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(text: 'moonwalker'),
|
||||
passwordController: TextEditingController(),
|
||||
error: context.knobs.string(
|
||||
label: 'error',
|
||||
initialValue: 'Неверный логин или пароль.',
|
||||
),
|
||||
onSubmit: () => debugPrint('Errored authentication form submitted'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'long_russian_copy',
|
||||
type: MwAuthenticationForm,
|
||||
path: '[Authentication]',
|
||||
)
|
||||
Widget buildMwAuthenticationFormLongRussianCopyUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 420,
|
||||
child: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(),
|
||||
passwordController: TextEditingController(),
|
||||
error:
|
||||
'Не удалось связаться с сервером авторизации. Проверьте подключение '
|
||||
'к интернету и повторите попытку через несколько минут.',
|
||||
onSubmit: () => debugPrint('Long-copy authentication form submitted'),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_primitives.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_shells.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
class FoundationsGallery extends StatelessWidget {
|
||||
const FoundationsGallery({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = MoonWellDesignTokens.of(context);
|
||||
final colors = <String, Color>{
|
||||
'Background': tokens.background,
|
||||
'Surface low': tokens.surfaceLow,
|
||||
'Surface': tokens.surface,
|
||||
'Surface high': tokens.surfaceHigh,
|
||||
'Accent': tokens.accent,
|
||||
'Accent muted': tokens.accentMuted,
|
||||
'Success': tokens.success,
|
||||
'Warning': tokens.warning,
|
||||
'Error': Theme.of(context).colorScheme.error,
|
||||
};
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Semantic colors',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
for (final entry in colors.entries)
|
||||
SizedBox(
|
||||
width: 144,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
color: entry.value,
|
||||
border: Border.all(color: Colors.white24),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(entry.key),
|
||||
MwStatusText(
|
||||
text:
|
||||
'#${entry.value.toARGB32().toRadixString(16).substring(2).toUpperCase()}',
|
||||
monospace: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Text('Typography', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'MoonWell',
|
||||
style: TextStyle(
|
||||
fontFamily: tokens.brandFontFamily,
|
||||
fontSize: 32,
|
||||
letterSpacing: 2,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Лунный колодец зовёт героев Нордскола',
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
const Text('Интерфейс, формы, навигация и рабочие статусы'),
|
||||
MwStatusText(
|
||||
text: 'build a93fd1c2 · 12.4 MB/s · 00:01:42',
|
||||
monospace: true,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Text(
|
||||
'Surfaces & radii',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MwPanel(level: MwPanelLevel.low, child: Text('Low')),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(child: MwPanel(child: Text('Regular'))),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: MwPanel(level: MwPanelLevel.high, child: Text('High')),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'default',
|
||||
type: FoundationsGallery,
|
||||
path: '[Foundations]',
|
||||
)
|
||||
Widget buildFoundationsGalleryUseCase(BuildContext context) {
|
||||
return const FoundationsGallery();
|
||||
}
|
||||
|
||||
class ArtworkSlot extends StatelessWidget {
|
||||
const ArtworkSlot({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/background.png',
|
||||
package: 'moonwell_launcher',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
const DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0x22000000), Color(0xEE000000)],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Center(child: MwBrandMark(width: 240)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'default', type: ArtworkSlot, path: '[Foundations]')
|
||||
Widget buildArtworkSlotUseCase(BuildContext context) {
|
||||
return const ArtworkSlot();
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_launcher_components.dart';
|
||||
import 'package:moonwell_launcher/core/moonwell_theme_variant.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
@widgetbook.UseCase(name: 'interactive', type: MwGameTile, path: '[Launcher]')
|
||||
Widget buildMwGameTileInteractiveUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: context.knobs.double.slider(
|
||||
label: 'width',
|
||||
initialValue: 240,
|
||||
min: 72,
|
||||
max: 360,
|
||||
divisions: 36,
|
||||
),
|
||||
child: MwGameTile(
|
||||
title: context.knobs.string(label: 'title', initialValue: 'MoonWell'),
|
||||
subtitle: context.knobs.stringOrNull(
|
||||
label: 'subtitle',
|
||||
initialValue: 'Wrath of the Lich King',
|
||||
),
|
||||
icon: Icons.nights_stay_outlined,
|
||||
selected: context.knobs.boolean(label: 'selected', initialValue: true),
|
||||
enabled: context.knobs.boolean(label: 'enabled', initialValue: true),
|
||||
compact: context.knobs.boolean(label: 'compact', initialValue: false),
|
||||
onPressed: () => debugPrint('Game tile selected'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'full', type: MwGameRail, path: '[Launcher]')
|
||||
Widget buildMwGameRailFullUseCase(BuildContext context) {
|
||||
return const SizedBox(width: 240, height: 560, child: MwGameRail());
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'compact', type: MwGameRail, path: '[Launcher]')
|
||||
Widget buildMwGameRailCompactUseCase(BuildContext context) {
|
||||
return const SizedBox(
|
||||
width: 72,
|
||||
height: 560,
|
||||
child: MwGameRail(compact: true),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'text_only', type: MwNewsItem, path: '[Launcher]')
|
||||
Widget buildMwNewsItemTextOnlyUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 620,
|
||||
child: MwNewsItem(
|
||||
item: MwNewsItemData(
|
||||
title: context.knobs.string(
|
||||
label: 'title',
|
||||
initialValue: 'Добро пожаловать в MoonWell',
|
||||
),
|
||||
body: context.knobs.string(
|
||||
label: 'body',
|
||||
initialValue: 'Приключение в Нордсколе начинается здесь.',
|
||||
),
|
||||
createdAt: DateTime(2026, 6, 22),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'image_failure', type: MwNewsItem, path: '[Launcher]')
|
||||
Widget buildMwNewsItemImageFailureUseCase(BuildContext context) {
|
||||
return const SizedBox(
|
||||
width: 620,
|
||||
child: MwNewsItem(
|
||||
item: MwNewsItemData(
|
||||
title: 'Обновление игрового мира',
|
||||
body: 'Изображение недоступно, но публикация остаётся читаемой.',
|
||||
imageUrl: 'https://invalid.moonwell.local/news.jpg',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'loading', type: MwNewsList, path: '[Launcher]')
|
||||
Widget buildMwNewsListLoadingUseCase(BuildContext context) {
|
||||
return const MwNewsList(state: MwNewsListState.loading);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'empty', type: MwNewsList, path: '[Launcher]')
|
||||
Widget buildMwNewsListEmptyUseCase(BuildContext context) {
|
||||
return const MwNewsList(state: MwNewsListState.empty);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'failure', type: MwNewsList, path: '[Launcher]')
|
||||
Widget buildMwNewsListFailureUseCase(BuildContext context) {
|
||||
return MwNewsList(
|
||||
state: MwNewsListState.failure,
|
||||
error: context.knobs.string(
|
||||
label: 'error',
|
||||
initialValue: 'Сервер новостей временно недоступен.',
|
||||
),
|
||||
onRetry: () => debugPrint('News reload requested'),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'populated', type: MwNewsList, path: '[Launcher]')
|
||||
Widget buildMwNewsListPopulatedUseCase(BuildContext context) {
|
||||
final count = context.knobs.int.slider(
|
||||
label: 'itemCount',
|
||||
initialValue: 8,
|
||||
min: 1,
|
||||
max: 50,
|
||||
divisions: 49,
|
||||
);
|
||||
return MwNewsList(
|
||||
state: MwNewsListState.populated,
|
||||
items: List.generate(
|
||||
count,
|
||||
(index) => MwNewsItemData(
|
||||
title: 'Новость ${index + 1}',
|
||||
body:
|
||||
'Реалистичный текст публикации о событиях и обновлениях MoonWell.',
|
||||
createdAt: DateTime(2026, 6, 22).subtract(Duration(days: index)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwSyncActionArea,
|
||||
path: '[Launcher]',
|
||||
)
|
||||
Widget buildMwSyncActionAreaInteractiveUseCase(BuildContext context) {
|
||||
final state = context.knobs.object.dropdown<MwSyncPresentation>(
|
||||
label: 'state',
|
||||
initialOption: MwSyncPresentation.downloading,
|
||||
options: MwSyncPresentation.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
);
|
||||
return SizedBox(
|
||||
width: 420,
|
||||
child: MwSyncActionArea(
|
||||
state: state,
|
||||
status: context.knobs.string(
|
||||
label: 'status',
|
||||
initialValue: 'Загружаем обновление клиента…',
|
||||
),
|
||||
progress: context.knobs.double.slider(
|
||||
label: 'progress',
|
||||
initialValue: .46,
|
||||
min: 0,
|
||||
max: 1,
|
||||
divisions: 100,
|
||||
),
|
||||
currentPath: context.knobs.stringOrNull(
|
||||
label: 'currentPath',
|
||||
initialValue: r'Data\ruRU\patch-ruRU.MPQ',
|
||||
),
|
||||
error: state == MwSyncPresentation.failure
|
||||
? context.knobs.string(
|
||||
label: 'error',
|
||||
initialValue: 'Проверка SHA-256 не пройдена.',
|
||||
)
|
||||
: null,
|
||||
onPrimary: () => debugPrint('Primary sync action requested: $state'),
|
||||
onPause: state == MwSyncPresentation.downloading
|
||||
? () => debugPrint('Sync pause requested')
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'metadata',
|
||||
type: MwLauncherStatusBar,
|
||||
path: '[Launcher]',
|
||||
)
|
||||
Widget buildMwLauncherStatusBarMetadataUseCase(BuildContext context) {
|
||||
return MwLauncherStatusBar(
|
||||
status: context.knobs.string(
|
||||
label: 'status',
|
||||
initialValue: 'Загрузка игровых файлов',
|
||||
),
|
||||
fileCount: 'Файлы 124/480',
|
||||
speed: '12.4 MB/s',
|
||||
eta: 'ETA 00:01:42',
|
||||
buildHash: 'a93fd1c2',
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'default',
|
||||
type: MwAccountAffordance,
|
||||
path: '[Launcher]',
|
||||
)
|
||||
Widget buildMwAccountAffordanceUseCase(BuildContext context) {
|
||||
return MwAccountAffordance(
|
||||
onSettings: () => debugPrint('Settings requested'),
|
||||
onLogout: () => debugPrint('Logout requested'),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwSettingsPanel,
|
||||
path: '[Launcher]',
|
||||
)
|
||||
Widget buildMwSettingsPanelInteractiveUseCase(BuildContext context) {
|
||||
final variant = context.knobs.object.dropdown<MoonWellThemeVariant>(
|
||||
label: 'themeVariant',
|
||||
initialOption: MoonWellThemeVariant.forest,
|
||||
options: MoonWellThemeVariant.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
);
|
||||
return SizedBox(
|
||||
width: 420,
|
||||
child: MwSettingsPanel(
|
||||
installationPath: context.knobs.stringOrNull(
|
||||
label: 'installationPath',
|
||||
initialValue: r'C:\Games\MoonWell',
|
||||
),
|
||||
themeVariant: variant,
|
||||
syncActive: context.knobs.boolean(
|
||||
label: 'syncActive',
|
||||
initialValue: false,
|
||||
),
|
||||
onChooseDirectory: () => debugPrint('Directory selection requested'),
|
||||
onThemeChanged: (value) => debugPrint('Theme changed: ${value.name}'),
|
||||
onVerify: () => debugPrint('Manual verification requested'),
|
||||
onLogout: () => debugPrint('Logout requested'),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_primitives.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
@widgetbook.UseCase(name: 'interactive', type: MwButton, path: '[Primitives]')
|
||||
Widget buildMwButtonInteractiveUseCase(BuildContext context) {
|
||||
final variant = context.knobs.object.dropdown<MwButtonVariant>(
|
||||
label: 'variant',
|
||||
initialOption: MwButtonVariant.primary,
|
||||
options: MwButtonVariant.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
);
|
||||
return MwButton(
|
||||
label: context.knobs.string(label: 'label', initialValue: 'Играть'),
|
||||
icon: context.knobs.boolean(label: 'showIcon', initialValue: true)
|
||||
? Icons.play_arrow
|
||||
: null,
|
||||
variant: variant,
|
||||
loading: context.knobs.boolean(label: 'loading', initialValue: false),
|
||||
expand: context.knobs.boolean(label: 'expand', initialValue: false),
|
||||
onPressed: context.knobs.boolean(label: 'enabled', initialValue: true)
|
||||
? () => debugPrint('MwButton pressed')
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'loading', type: MwButton, path: '[Primitives]')
|
||||
Widget buildMwButtonLoadingUseCase(BuildContext context) {
|
||||
return MwButton(
|
||||
label: context.knobs.string(label: 'label', initialValue: 'Загрузка'),
|
||||
loading: true,
|
||||
onPressed: () => debugPrint('Loading MwButton pressed'),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'disabled', type: MwButton, path: '[Primitives]')
|
||||
Widget buildMwButtonDisabledUseCase(BuildContext context) {
|
||||
return MwButton(
|
||||
label: context.knobs.string(label: 'label', initialValue: 'Недоступно'),
|
||||
onPressed: null,
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwIconButton,
|
||||
path: '[Primitives]',
|
||||
)
|
||||
Widget buildMwIconButtonInteractiveUseCase(BuildContext context) {
|
||||
return MwIconButton(
|
||||
icon: Icons.settings,
|
||||
tooltip: context.knobs.string(label: 'tooltip', initialValue: 'Настройки'),
|
||||
selected: context.knobs.boolean(label: 'selected', initialValue: false),
|
||||
destructive: context.knobs.boolean(
|
||||
label: 'destructive',
|
||||
initialValue: false,
|
||||
),
|
||||
onPressed: context.knobs.boolean(label: 'enabled', initialValue: true)
|
||||
? () => debugPrint('MwIconButton pressed')
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwTextField,
|
||||
path: '[Primitives]',
|
||||
)
|
||||
Widget buildMwTextFieldInteractiveUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 380,
|
||||
child: MwTextField(
|
||||
label: context.knobs.string(label: 'label', initialValue: 'Логин'),
|
||||
hint: context.knobs.stringOrNull(
|
||||
label: 'hint',
|
||||
initialValue: 'Введите логин',
|
||||
),
|
||||
errorText: context.knobs.stringOrNull(
|
||||
label: 'errorText',
|
||||
initialValue: null,
|
||||
),
|
||||
enabled: context.knobs.boolean(label: 'enabled', initialValue: true),
|
||||
obscureText: context.knobs.boolean(
|
||||
label: 'obscureText',
|
||||
initialValue: false,
|
||||
),
|
||||
prefixIcon: context.knobs.boolean(label: 'showIcon', initialValue: true)
|
||||
? Icons.person_outline
|
||||
: null,
|
||||
onSubmitted: (value) => debugPrint('MwTextField submitted: $value'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'interactive', type: MwCheckbox, path: '[Primitives]')
|
||||
Widget buildMwCheckboxInteractiveUseCase(BuildContext context) {
|
||||
return MwCheckbox(
|
||||
label: context.knobs.string(label: 'label', initialValue: 'Запомнить меня'),
|
||||
value: context.knobs.boolean(label: 'value', initialValue: false),
|
||||
onChanged: context.knobs.boolean(label: 'enabled', initialValue: true)
|
||||
? (value) => debugPrint('MwCheckbox changed: $value')
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'interactive', type: MwTabs, path: '[Primitives]')
|
||||
Widget buildMwTabsInteractiveUseCase(BuildContext context) {
|
||||
return MwTabs(
|
||||
labels: const ['Новости', 'Обновления', 'Очень длинный раздел'],
|
||||
selectedIndex: context.knobs.int.slider(
|
||||
label: 'selectedIndex',
|
||||
initialValue: 0,
|
||||
min: 0,
|
||||
max: 2,
|
||||
divisions: 2,
|
||||
),
|
||||
disabledIndices:
|
||||
context.knobs.boolean(label: 'disableFutureTabs', initialValue: true)
|
||||
? const {1, 2}
|
||||
: const {},
|
||||
onSelected: (index) => debugPrint('MwTabs selected: $index'),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'interactive', type: MwBadge, path: '[Primitives]')
|
||||
Widget buildMwBadgeInteractiveUseCase(BuildContext context) {
|
||||
return MwBadge(
|
||||
label: context.knobs.string(label: 'label', initialValue: 'Готово'),
|
||||
tone: context.knobs.object.dropdown<MwBadgeTone>(
|
||||
label: 'tone',
|
||||
initialOption: MwBadgeTone.success,
|
||||
options: MwBadgeTone.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'interactive', type: MwPanel, path: '[Primitives]')
|
||||
Widget buildMwPanelInteractiveUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 420,
|
||||
child: MwPanel(
|
||||
level: context.knobs.object.dropdown<MwPanelLevel>(
|
||||
label: 'level',
|
||||
initialOption: MwPanelLevel.regular,
|
||||
options: MwPanelLevel.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
),
|
||||
child: Text(
|
||||
context.knobs.string(
|
||||
label: 'content',
|
||||
initialValue: 'Содержимое панели MoonWell',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwProgressBar,
|
||||
path: '[Primitives]',
|
||||
)
|
||||
Widget buildMwProgressBarInteractiveUseCase(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 440,
|
||||
child: MwProgressBar(
|
||||
value: context.knobs.boolean(label: 'indeterminate', initialValue: false)
|
||||
? null
|
||||
: context.knobs.double.slider(
|
||||
label: 'value',
|
||||
initialValue: .46,
|
||||
min: 0,
|
||||
max: 1,
|
||||
divisions: 100,
|
||||
),
|
||||
label: context.knobs.stringOrNull(
|
||||
label: 'label',
|
||||
initialValue: 'Загрузка файлов',
|
||||
),
|
||||
state: context.knobs.object.dropdown<MwProgressState>(
|
||||
label: 'state',
|
||||
initialOption: MwProgressState.active,
|
||||
options: MwProgressState.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwStatusText,
|
||||
path: '[Primitives]',
|
||||
)
|
||||
Widget buildMwStatusTextInteractiveUseCase(BuildContext context) {
|
||||
return MwStatusText(
|
||||
text: context.knobs.string(
|
||||
label: 'text',
|
||||
initialValue: 'Проверяем локальные файлы…',
|
||||
),
|
||||
tone: context.knobs.object.dropdown<MwStatusTone>(
|
||||
label: 'tone',
|
||||
initialOption: MwStatusTone.operational,
|
||||
options: MwStatusTone.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
),
|
||||
monospace: context.knobs.boolean(label: 'monospace', initialValue: false),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwStateView,
|
||||
path: '[Primitives]',
|
||||
)
|
||||
Widget buildMwStateViewInteractiveUseCase(BuildContext context) {
|
||||
final kind = context.knobs.object.dropdown<MwStateKind>(
|
||||
label: 'kind',
|
||||
initialOption: MwStateKind.empty,
|
||||
options: MwStateKind.values,
|
||||
labelBuilder: (value) => value.name,
|
||||
);
|
||||
return MwStateView(
|
||||
kind: kind,
|
||||
title: context.knobs.string(
|
||||
label: 'title',
|
||||
initialValue: 'Новостей пока нет',
|
||||
),
|
||||
description: context.knobs.stringOrNull(
|
||||
label: 'description',
|
||||
initialValue: 'Новые публикации появятся здесь.',
|
||||
),
|
||||
onRetry: kind == MwStateKind.error
|
||||
? () => debugPrint('MwStateView retry pressed')
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'default',
|
||||
type: MwDisabledFeature,
|
||||
path: '[Primitives]',
|
||||
)
|
||||
Widget buildMwDisabledFeatureUseCase(BuildContext context) {
|
||||
return MwDisabledFeature(
|
||||
message: context.knobs.string(
|
||||
label: 'message',
|
||||
initialValue: 'Регистрация появится позже',
|
||||
),
|
||||
child: const MwButton(
|
||||
label: 'Регистрация · скоро',
|
||||
onPressed: null,
|
||||
variant: MwButtonVariant.secondary,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_authentication.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_launcher_components.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_shells.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
VoidCallback _windowCallback(String action) {
|
||||
return () => debugPrint('Window action requested: $action');
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'default', type: MwBrandMark, path: '[Foundations]')
|
||||
Widget buildMwBrandMarkUseCase(BuildContext context) {
|
||||
return MwBrandMark(
|
||||
width: context.knobs.double.slider(
|
||||
label: 'width',
|
||||
initialValue: 220,
|
||||
min: 96,
|
||||
max: 360,
|
||||
divisions: 33,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'default',
|
||||
type: MwArtworkBackground,
|
||||
path: '[Foundations]',
|
||||
)
|
||||
Widget buildMwArtworkBackgroundUseCase(BuildContext context) {
|
||||
return const MwArtworkBackground(
|
||||
assetPackage: 'moonwell_launcher',
|
||||
child: Center(child: Text('Safe content area')),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(name: 'default', type: MwLoginShell, path: '[Compositions]')
|
||||
Widget buildMwLoginShellUseCase(BuildContext context) {
|
||||
return MwLoginShell(
|
||||
assetPackage: 'moonwell_launcher',
|
||||
form: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(),
|
||||
passwordController: TextEditingController(),
|
||||
onSubmit: () => debugPrint('Login submitted'),
|
||||
),
|
||||
onDrag: _windowCallback('drag'),
|
||||
onDoubleTap: _windowCallback('double_tap'),
|
||||
onMinimize: _windowCallback('minimize'),
|
||||
onMaximizeRestore: _windowCallback('maximize_restore'),
|
||||
onClose: _windowCallback('close'),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'api_error',
|
||||
type: MwLoginShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLoginShellApiErrorUseCase(BuildContext context) {
|
||||
return MwLoginShell(
|
||||
assetPackage: 'moonwell_launcher',
|
||||
form: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(text: 'moonwalker'),
|
||||
passwordController: TextEditingController(),
|
||||
error: context.knobs.string(
|
||||
label: 'error',
|
||||
initialValue: 'Неверный логин или пароль.',
|
||||
),
|
||||
onSubmit: () => debugPrint('Login submitted after API error'),
|
||||
),
|
||||
onDrag: _windowCallback('drag'),
|
||||
onDoubleTap: _windowCallback('double_tap'),
|
||||
onMinimize: _windowCallback('minimize'),
|
||||
onMaximizeRestore: _windowCallback('maximize_restore'),
|
||||
onClose: _windowCallback('close'),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'restored_session_loading',
|
||||
type: MwLoginShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLoginShellRestoredSessionLoadingUseCase(BuildContext context) {
|
||||
return MwLoginShell(
|
||||
assetPackage: 'moonwell_launcher',
|
||||
form: const Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 16),
|
||||
Text('Восстанавливаем защищённую сессию…'),
|
||||
],
|
||||
),
|
||||
),
|
||||
onDrag: _windowCallback('drag'),
|
||||
onDoubleTap: _windowCallback('double_tap'),
|
||||
onMinimize: _windowCallback('minimize'),
|
||||
onMaximizeRestore: _windowCallback('maximize_restore'),
|
||||
onClose: _windowCallback('close'),
|
||||
);
|
||||
}
|
||||
|
||||
MwLauncherShell _launcherShell({
|
||||
required MwNewsListState newsState,
|
||||
required MwSyncPresentation syncState,
|
||||
required String status,
|
||||
double? progress,
|
||||
String? error,
|
||||
}) {
|
||||
return MwLauncherShell(
|
||||
assetPackage: 'moonwell_launcher',
|
||||
news: MwNewsList(
|
||||
state: newsState,
|
||||
error: newsState == MwNewsListState.failure
|
||||
? 'Сервер новостей временно недоступен.'
|
||||
: null,
|
||||
items: newsState == MwNewsListState.populated
|
||||
? List.generate(
|
||||
8,
|
||||
(index) => MwNewsItemData(
|
||||
title: 'Новость ${index + 1}',
|
||||
body: 'События игрового мира и важные обновления MoonWell.',
|
||||
createdAt: DateTime(2026, 6, 22 - index),
|
||||
),
|
||||
)
|
||||
: const [],
|
||||
onRetry: () => debugPrint('News reload requested'),
|
||||
),
|
||||
syncActions: MwSyncActionArea(
|
||||
state: syncState,
|
||||
status: status,
|
||||
progress: progress,
|
||||
currentPath: syncState == MwSyncPresentation.downloading
|
||||
? r'Data\ruRU\patch-ruRU.MPQ'
|
||||
: null,
|
||||
error: error,
|
||||
onPrimary: () => debugPrint('Primary sync action requested'),
|
||||
onPause: syncState == MwSyncPresentation.downloading
|
||||
? () => debugPrint('Sync pause requested')
|
||||
: null,
|
||||
),
|
||||
statusBar: const MwLauncherStatusBar(
|
||||
status: 'MoonWell Launcher · локальная установка',
|
||||
fileCount: '124/480',
|
||||
buildHash: 'a93fd1c2',
|
||||
),
|
||||
account: MwAccountAffordance(
|
||||
onSettings: () => debugPrint('Settings requested'),
|
||||
onLogout: () => debugPrint('Logout requested'),
|
||||
),
|
||||
onDrag: _windowCallback('drag'),
|
||||
onDoubleTap: _windowCallback('double_tap'),
|
||||
onMinimize: _windowCallback('minimize'),
|
||||
onMaximizeRestore: _windowCallback('maximize_restore'),
|
||||
onClose: _windowCallback('close'),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'ready',
|
||||
type: MwLauncherShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLauncherShellReadyUseCase(BuildContext context) {
|
||||
return _launcherShell(
|
||||
newsState: MwNewsListState.populated,
|
||||
syncState: MwSyncPresentation.ready,
|
||||
status: 'Клиент готов к запуску.',
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'downloading',
|
||||
type: MwLauncherShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLauncherShellDownloadingUseCase(BuildContext context) {
|
||||
return _launcherShell(
|
||||
newsState: MwNewsListState.populated,
|
||||
syncState: MwSyncPresentation.downloading,
|
||||
status: 'Загружаем обновление клиента…',
|
||||
progress: context.knobs.double.slider(
|
||||
label: 'progress',
|
||||
initialValue: .46,
|
||||
min: 0,
|
||||
max: 1,
|
||||
divisions: 100,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'paused',
|
||||
type: MwLauncherShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLauncherShellPausedUseCase(BuildContext context) {
|
||||
return _launcherShell(
|
||||
newsState: MwNewsListState.populated,
|
||||
syncState: MwSyncPresentation.paused,
|
||||
status: 'Обновление приостановлено.',
|
||||
progress: .46,
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'verifying',
|
||||
type: MwLauncherShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLauncherShellVerifyingUseCase(BuildContext context) {
|
||||
return _launcherShell(
|
||||
newsState: MwNewsListState.populated,
|
||||
syncState: MwSyncPresentation.verifying,
|
||||
status: 'Проверяем целостность установки…',
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'failure',
|
||||
type: MwLauncherShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLauncherShellFailureUseCase(BuildContext context) {
|
||||
return _launcherShell(
|
||||
newsState: MwNewsListState.failure,
|
||||
syncState: MwSyncPresentation.failure,
|
||||
status: 'Не удалось завершить обновление.',
|
||||
error: 'Проверка SHA-256 не пройдена.',
|
||||
);
|
||||
}
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'empty_news',
|
||||
type: MwLauncherShell,
|
||||
path: '[Compositions]',
|
||||
)
|
||||
Widget buildMwLauncherShellEmptyNewsUseCase(BuildContext context) {
|
||||
return _launcherShell(
|
||||
newsState: MwNewsListState.empty,
|
||||
syncState: MwSyncPresentation.ready,
|
||||
status: 'Клиент готов к запуску.',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_window_chrome.dart';
|
||||
import 'package:widgetbook/widgetbook.dart';
|
||||
import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
|
||||
|
||||
@widgetbook.UseCase(
|
||||
name: 'interactive',
|
||||
type: MwWindowChrome,
|
||||
path: '[Window Chrome]',
|
||||
)
|
||||
Widget buildMwWindowChromeInteractiveUseCase(BuildContext context) {
|
||||
return MwWindowChrome(
|
||||
onDrag: () => debugPrint('Window drag requested'),
|
||||
onDoubleTap: () => debugPrint('Window maximize/restore requested'),
|
||||
onMinimize: () => debugPrint('Window minimize requested'),
|
||||
onMaximizeRestore: () => debugPrint('Window maximize/restore requested'),
|
||||
onClose: () => debugPrint('Window close requested'),
|
||||
showMaximize: context.knobs.boolean(label: 'Show maximize button'),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user