mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-05-25 15:48:42 -05:00
[client] Responsive destination picker
This commit is contained in:
@@ -56,7 +56,9 @@ class _DestinationPickerState extends State<DestinationPicker> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context) => MediaQuery.sizeOf(context).width <= 600 ? buildScaffold(context) : buildDialog(context);
|
||||
|
||||
Widget buildScaffold(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Builder(builder: (context) {
|
||||
@@ -89,96 +91,156 @@ class _DestinationPickerState extends State<DestinationPicker> {
|
||||
})
|
||||
],
|
||||
),
|
||||
body: FocusScope(
|
||||
child: Actions(
|
||||
actions: {
|
||||
NavUpIntent: CallbackAction<NavUpIntent>(onInvoke: (i) async {
|
||||
final parentId = context.read<ExplorerState>().folder?.parent;
|
||||
if (parentId != null) {
|
||||
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: parentId));
|
||||
}
|
||||
node.requestFocus();
|
||||
return null;
|
||||
}),
|
||||
FocusDownIntent: CallbackAction<FocusDownIntent>(
|
||||
onInvoke: (i) async {
|
||||
context.read<ExplorerController>().updateSelection((i) => i + 1, SelectionMode.single, false);
|
||||
node.requestFocus();
|
||||
return null;
|
||||
},
|
||||
),
|
||||
FocusUpIntent: CallbackAction<FocusUpIntent>(
|
||||
onInvoke: (i) async {
|
||||
context.read<ExplorerController>().updateSelection((i) => i - 1, SelectionMode.single, false);
|
||||
node.requestFocus();
|
||||
return null;
|
||||
},
|
||||
),
|
||||
ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: (i) async {
|
||||
final selected = context.read<ExplorerState>().selectedSingle;
|
||||
if (selected != null && selected.dir) {
|
||||
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: selected.id));
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
DismissIntent: CallbackAction<DismissIntent>(onInvoke: (i) => Navigator.of(context).pop()),
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Focus(
|
||||
autofocus: true,
|
||||
focusNode: node,
|
||||
child: ExcludeFocus(child: const DestinationFolderView()),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(onPressed: Navigator.of(context).pop, child: Text('Cancel')),
|
||||
ElevatedButton(
|
||||
onPressed: context.select<ExplorerState, bool>((state) => state.folderId == widget.initialFolderId)
|
||||
? null
|
||||
: () => Navigator.of(context).pop(context.read<ExplorerState>().folderId),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
body: _buildContents(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildDialog(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: _ActionHandler(
|
||||
focusNode: node,
|
||||
child: AlertDialog(
|
||||
titlePadding: EdgeInsets.zero,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Builder(
|
||||
builder: (context) {
|
||||
final name = context.select<ExplorerState, String?>((state) => state.folder?.name);
|
||||
return ListTile(
|
||||
leading: BackButton(onPressed: () {
|
||||
final parentId = context.read<ExplorerState>().folder?.parent;
|
||||
if (parentId != null) {
|
||||
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: parentId));
|
||||
node.requestFocus();
|
||||
} else {
|
||||
context.pop();
|
||||
}
|
||||
}),
|
||||
title: Text(name ?? ''),
|
||||
subtitle: const Text('Pick Destination'),
|
||||
trailing: Builder(builder: (context) {
|
||||
final folderId = context.select<ExplorerState, String?>((state) => state.folderId);
|
||||
return IconButton(
|
||||
onPressed: folderId == null ? null : () => createDirectory(context, folderId),
|
||||
icon: Icon(Icons.create_new_folder_outlined),
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
content: SizedBox(width: 360, height: 600, child: _buildContents(context)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContents(BuildContext context) {
|
||||
return FocusScope(
|
||||
child: _ActionHandler(
|
||||
focusNode: node,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(child: _FolderListView(focusNode: node)),
|
||||
Divider(thickness: 0, height: 0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: OverflowBar(
|
||||
alignment: MainAxisAlignment.end,
|
||||
spacing: 8,
|
||||
children: [
|
||||
TextButton(onPressed: Navigator.of(context).pop, child: Text('Cancel')),
|
||||
ElevatedButton(
|
||||
onPressed: context.select<ExplorerState, bool>((state) => state.folderId == widget.initialFolderId)
|
||||
? null
|
||||
: () => Navigator.of(context).pop(context.read<ExplorerState>().folderId),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DestinationFolderView extends StatelessWidget {
|
||||
const DestinationFolderView({super.key});
|
||||
class _FolderListView extends StatelessWidget {
|
||||
final FocusNode focusNode;
|
||||
const _FolderListView({required this.focusNode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final items = context.select<ExplorerState, List<Resource>>((state) => state.resources);
|
||||
return ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final r = items[index];
|
||||
return Builder(builder: (context) {
|
||||
final selected = context.select<ExplorerState, bool>((state) => state.selectedIds.contains(r.id));
|
||||
return ListTile(
|
||||
key: ValueKey(r.id),
|
||||
visualDensity: VisualDensity.compact,
|
||||
selected: selected,
|
||||
leading: r.getIcon(),
|
||||
title: Text(r.name),
|
||||
enabled: r.dir,
|
||||
onTap: () => context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: r.id)),
|
||||
);
|
||||
});
|
||||
},
|
||||
itemCount: items.length,
|
||||
return Focus(
|
||||
autofocus: true,
|
||||
focusNode: focusNode,
|
||||
child: ExcludeFocus(
|
||||
child: ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final r = items[index];
|
||||
return Builder(builder: (context) {
|
||||
final selected = context.select<ExplorerState, bool>((state) => state.selectedIds.contains(r.id));
|
||||
return ListTile(
|
||||
key: ValueKey(r.id),
|
||||
visualDensity: VisualDensity.compact,
|
||||
selected: selected,
|
||||
leading: r.getIcon(),
|
||||
title: Text(r.name),
|
||||
enabled: r.dir,
|
||||
onTap: () => context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: r.id)),
|
||||
);
|
||||
});
|
||||
},
|
||||
itemCount: items.length,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ActionHandler extends StatelessWidget {
|
||||
final Widget child;
|
||||
final FocusNode focusNode;
|
||||
const _ActionHandler({required this.child, required this.focusNode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Actions(
|
||||
actions: {
|
||||
NavUpIntent: CallbackAction<NavUpIntent>(onInvoke: (i) async {
|
||||
final parentId = context.read<ExplorerState>().folder?.parent;
|
||||
if (parentId != null) {
|
||||
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: parentId));
|
||||
}
|
||||
focusNode.requestFocus();
|
||||
return null;
|
||||
}),
|
||||
FocusDownIntent: CallbackAction<FocusDownIntent>(
|
||||
onInvoke: (i) async {
|
||||
context.read<ExplorerController>().updateSelection((i) => i + 1, SelectionMode.single, false);
|
||||
focusNode.requestFocus();
|
||||
return null;
|
||||
},
|
||||
),
|
||||
FocusUpIntent: CallbackAction<FocusUpIntent>(
|
||||
onInvoke: (i) async {
|
||||
context.read<ExplorerController>().updateSelection((i) => i - 1, SelectionMode.single, false);
|
||||
focusNode.requestFocus();
|
||||
return null;
|
||||
},
|
||||
),
|
||||
ActivateIntent: CallbackAction<ActivateIntent>(onInvoke: (i) async {
|
||||
final selected = context.read<ExplorerState>().selectedSingle;
|
||||
if (selected != null && selected.dir) {
|
||||
context.read<ExplorerNavigator>().go(ExplorerPageFolder(folderId: selected.id));
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
DismissIntent: CallbackAction<DismissIntent>(onInvoke: (i) => Navigator.of(context).pop()),
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user