mirror of
https://github.com/immich-app/immich.git
synced 2026-06-22 22:56:39 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e51c4cb355 |
@@ -1,7 +1,9 @@
|
||||
export 'src/components/close_button.dart';
|
||||
export 'src/components/column_button.dart';
|
||||
export 'src/components/form.dart';
|
||||
export 'src/components/formatted_text.dart';
|
||||
export 'src/components/icon_button.dart';
|
||||
export 'src/components/menu_item.dart';
|
||||
export 'src/components/password_input.dart';
|
||||
export 'src/components/text_button.dart';
|
||||
export 'src/components/text_input.dart';
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_ui/src/constants.dart';
|
||||
import 'package:immich_ui/src/internal.dart';
|
||||
|
||||
class ImmichColumnButton extends StatefulWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final FutureOr<void> Function() onPressed;
|
||||
final bool disabled;
|
||||
final bool? loading;
|
||||
|
||||
const ImmichColumnButton({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.disabled = false,
|
||||
this.loading,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ImmichColumnButton> createState() => _ImmichColumnButtonState();
|
||||
}
|
||||
|
||||
class _ImmichColumnButtonState extends State<ImmichColumnButton> {
|
||||
bool _loading = false;
|
||||
bool get _isLoading => widget.loading ?? _loading;
|
||||
|
||||
Future<void> _onPressed() async {
|
||||
setState(() => _loading = true);
|
||||
try {
|
||||
await widget.onPressed();
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _loading = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final foreground = context.colorOverride ?? Theme.of(context).colorScheme.onSurface;
|
||||
|
||||
return TextButton(
|
||||
onPressed: widget.disabled || _isLoading ? null : _onPressed,
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: foreground,
|
||||
padding: const .symmetric(horizontal: ImmichSpacing.sm, vertical: ImmichSpacing.md),
|
||||
tapTargetSize: .shrinkWrap,
|
||||
shape: const RoundedRectangleBorder(borderRadius: .all(.circular(ImmichRadius.xl))),
|
||||
),
|
||||
child: ConstrainedBox(
|
||||
constraints: const .new(maxWidth: 90),
|
||||
child: Column(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
_isLoading
|
||||
? const SizedBox.square(
|
||||
dimension: ImmichIconSize.md,
|
||||
child: CircularProgressIndicator(strokeWidth: ImmichBorderWidth.lg),
|
||||
)
|
||||
: Icon(widget.icon, size: ImmichIconSize.md),
|
||||
const SizedBox(height: ImmichSpacing.sm),
|
||||
Text(
|
||||
widget.label,
|
||||
maxLines: 2,
|
||||
textAlign: .center,
|
||||
overflow: .ellipsis,
|
||||
style: const .new(fontSize: ImmichTextSize.label, fontWeight: .w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_ui/src/constants.dart';
|
||||
import 'package:immich_ui/src/internal.dart';
|
||||
|
||||
class ImmichMenu extends StatefulWidget {
|
||||
final List<Widget> children;
|
||||
final MenuAnchorChildBuilder builder;
|
||||
final MenuStyle? style;
|
||||
final bool consumeOutsideTap;
|
||||
final Widget? child;
|
||||
|
||||
const ImmichMenu({
|
||||
super.key,
|
||||
required this.children,
|
||||
required this.builder,
|
||||
this.style,
|
||||
this.consumeOutsideTap = false,
|
||||
this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ImmichMenu> createState() => _ImmichMenuState();
|
||||
}
|
||||
|
||||
class _ImmichMenuState extends State<ImmichMenu> {
|
||||
final _controller = MenuController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _ImmichMenuScope(
|
||||
controller: _controller,
|
||||
child: MenuAnchor(
|
||||
controller: _controller,
|
||||
style: widget.style,
|
||||
consumeOutsideTap: widget.consumeOutsideTap,
|
||||
menuChildren: widget.children,
|
||||
builder: widget.builder,
|
||||
child: widget.child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ImmichMenuScope extends InheritedWidget {
|
||||
final MenuController controller;
|
||||
|
||||
const _ImmichMenuScope({required this.controller, required super.child});
|
||||
|
||||
static MenuController? maybeOf(BuildContext context) =>
|
||||
context.dependOnInheritedWidgetOfExactType<_ImmichMenuScope>()?.controller;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(_ImmichMenuScope oldWidget) => controller != oldWidget.controller;
|
||||
}
|
||||
|
||||
class ImmichMenuItem extends StatefulWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final FutureOr<void> Function() onPressed;
|
||||
final bool disabled;
|
||||
|
||||
const ImmichMenuItem({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.disabled = false,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ImmichMenuItem> createState() => _ImmichMenuItemState();
|
||||
}
|
||||
|
||||
class _ImmichMenuItemState extends State<ImmichMenuItem> {
|
||||
Future<void> _onPressed(MenuController? controller) async {
|
||||
try {
|
||||
await widget.onPressed();
|
||||
} finally {
|
||||
controller?.close();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final controller = _ImmichMenuScope.maybeOf(context);
|
||||
return MenuItemButton(
|
||||
onPressed: widget.disabled ? null : () => _onPressed(controller),
|
||||
closeOnActivate: controller == null,
|
||||
style: MenuItemButton.styleFrom(
|
||||
foregroundColor: context.colorOverride,
|
||||
alignment: .centerLeft,
|
||||
padding: const .symmetric(horizontal: ImmichSpacing.lg, vertical: ImmichSpacing.md),
|
||||
),
|
||||
leadingIcon: Icon(widget.icon, size: ImmichIconSize.sm),
|
||||
child: Text(widget.label, style: const .new(fontSize: ImmichTextSize.body)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_ui/src/components/column_button.dart';
|
||||
import 'package:immich_ui/src/previews.dart';
|
||||
|
||||
void _previewNoop() {}
|
||||
|
||||
@ImmichPreview(group: 'ColumnButton', name: 'Default')
|
||||
Widget previewColumnButtonDefault() => const Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
ImmichColumnButton(onPressed: _previewNoop, icon: Icons.favorite_border_rounded, label: 'Favorite'),
|
||||
ImmichColumnButton(onPressed: _previewNoop, icon: Icons.archive_outlined, label: 'Archive'),
|
||||
ImmichColumnButton(onPressed: _previewNoop, icon: Icons.delete_outline_rounded, label: 'Delete'),
|
||||
],
|
||||
);
|
||||
|
||||
@ImmichPreview(group: 'ColumnButton', name: 'Loading')
|
||||
Widget previewColumnButtonLoading() => ImmichColumnButton(
|
||||
onPressed: () => Future<void>.delayed(const .new(seconds: 2)),
|
||||
icon: Icons.download,
|
||||
label: 'Download',
|
||||
);
|
||||
|
||||
@ImmichPreview(group: 'ColumnButton', name: 'Disabled')
|
||||
Widget previewColumnButtonDisabled() =>
|
||||
const ImmichColumnButton(onPressed: _previewNoop, icon: Icons.ios_share_rounded, label: 'Share', disabled: true);
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_ui/src/components/menu_item.dart';
|
||||
import 'package:immich_ui/src/previews.dart';
|
||||
|
||||
void _previewNoop() {}
|
||||
|
||||
@ImmichPreview(group: 'MenuItem', name: 'Default')
|
||||
Widget previewMenuItemDefault() => const Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ImmichMenuItem(onPressed: _previewNoop, icon: Icons.info_outline, label: 'Info'),
|
||||
ImmichMenuItem(onPressed: _previewNoop, icon: Icons.help_outline_rounded, label: 'Troubleshoot'),
|
||||
ImmichMenuItem(onPressed: _previewNoop, icon: Icons.cast_rounded, label: 'Cast'),
|
||||
],
|
||||
);
|
||||
|
||||
@ImmichPreview(group: 'MenuItem', name: 'Disabled')
|
||||
Widget previewMenuItemDisabled() =>
|
||||
const ImmichMenuItem(onPressed: _previewNoop, icon: Icons.delete_outline_rounded, label: 'Delete', disabled: true);
|
||||
Reference in New Issue
Block a user