156 lines
4.1 KiB
Dart
156 lines
4.1 KiB
Dart
import "package:flutter/widgets.dart";
|
|
import "package:shadcn_flutter/shadcn_flutter.dart" as shad;
|
|
|
|
|
|
// floating pane visual — use inside showDialog, backdrop handled by caller
|
|
class PaneDialog extends StatelessWidget {
|
|
const PaneDialog({
|
|
required this.title,
|
|
required this.child,
|
|
this.onClose,
|
|
this.fillHeight = false,
|
|
super.key,
|
|
});
|
|
|
|
final String title;
|
|
final Widget child;
|
|
final VoidCallback? onClose;
|
|
final bool fillHeight;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final scheme = shad.Theme.of(context).colorScheme;
|
|
final borderColor = Color.lerp(scheme.border, scheme.foreground, 0.1)!;
|
|
|
|
return DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
color: scheme.background,
|
|
borderRadius: BorderRadius.circular(8),
|
|
boxShadow: const [
|
|
BoxShadow(color: Color(0x26888888), blurRadius: 4, spreadRadius: 2),
|
|
],
|
|
),
|
|
|
|
child: Stack(
|
|
children: [
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Column(
|
|
mainAxisSize: fillHeight ? MainAxisSize.max : MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
_TitleBar(title: title, borderColor: borderColor, onClose: onClose),
|
|
child,
|
|
],
|
|
),
|
|
),
|
|
|
|
Positioned.fill(
|
|
child: IgnorePointer(
|
|
child: DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(color: borderColor, width: 1),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
class _TitleBar extends StatelessWidget {
|
|
const _TitleBar({
|
|
required this.title,
|
|
required this.borderColor,
|
|
this.onClose,
|
|
});
|
|
|
|
final String title;
|
|
final Color borderColor;
|
|
final VoidCallback? onClose;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final scheme = shad.Theme.of(context).colorScheme;
|
|
|
|
return Container(
|
|
height: 34,
|
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
|
decoration: BoxDecoration(
|
|
color: scheme.secondary,
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(8),
|
|
topRight: Radius.circular(8),
|
|
),
|
|
border: Border(bottom: BorderSide(color: borderColor, width: 1)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: shad.TextStyle(
|
|
color: scheme.secondaryForeground,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const Spacer(),
|
|
if (onClose != null) _CloseBtn(onTap: onClose!),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
class _CloseBtn extends StatefulWidget {
|
|
const _CloseBtn({required this.onTap});
|
|
final VoidCallback onTap;
|
|
|
|
@override
|
|
State<_CloseBtn> createState() => _CloseBtnState();
|
|
}
|
|
|
|
class _CloseBtnState extends State<_CloseBtn> {
|
|
bool _hovered = false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final scheme = shad.Theme.of(context).colorScheme;
|
|
|
|
return MouseRegion(
|
|
onEnter: (_) => setState(() => _hovered = true),
|
|
onExit: (_) => setState(() => _hovered = false),
|
|
child: GestureDetector(
|
|
onTap: widget.onTap,
|
|
child: TweenAnimationBuilder<double>(
|
|
tween: Tween(begin: 0, end: _hovered ? 1.0 : 0.0),
|
|
duration: const Duration(milliseconds: 20),
|
|
builder: (context, t, _) {
|
|
return Container(
|
|
width: 18,
|
|
height: 18,
|
|
decoration: BoxDecoration(
|
|
color: Color.lerp(
|
|
const Color(0x00000000),
|
|
scheme.destructive.withValues(alpha: 0.12),
|
|
t,
|
|
),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: shad.Icon(
|
|
shad.LucideIcons.x,
|
|
size: 13,
|
|
color: Color.lerp(scheme.mutedForeground, scheme.destructive, t),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|