spotube/lib/components/track_presentation/presentation_modifiers.dart
2025-04-25 21:54:57 +06:00

132 lines
5.3 KiB
Dart

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/track_presentation/sort_tracks_dropdown.dart';
import 'package:spotube/components/track_presentation/presentation_actions.dart';
import 'package:spotube/components/track_presentation/presentation_props.dart';
import 'package:spotube/components/track_presentation/presentation_state.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/controllers/use_shadcn_text_editing_controller.dart';
class TrackPresentationModifiersSection extends HookConsumerWidget {
final FocusNode? focusNode;
const TrackPresentationModifiersSection({
super.key,
this.focusNode,
});
@override
Widget build(BuildContext context, ref) {
final options = TrackPresentationOptions.of(context);
final state = ref.watch(presentationStateProvider(options.collection));
final notifier = ref.watch(
presentationStateProvider(options.collection).notifier,
);
final controller = useShadcnTextEditingController();
final scale = context.theme.scaling;
return LayoutBuilder(builder: (context, constrains) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: (constrains.mdAndUp ? 16 : 8) * scale,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
state: state.selectedTracks.length == options.tracks.length
? CheckboxState.checked
: CheckboxState.unchecked,
onChanged: (value) {
if (value == CheckboxState.checked) {
notifier.selectAllTracks();
} else {
notifier.deselectAllTracks();
}
},
),
],
),
Flexible(
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 8,
children: [
Flexible(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 320 * scale,
maxHeight: 38 * scale,
),
child: TextField(
controller: controller,
focusNode: focusNode,
placeholder: Text(context.l10n.search_tracks),
onChanged: (value) {
if (value.isEmpty) {
notifier.clearFilter();
} else {
notifier.filterTracks(value);
}
},
features: [
InputFeature.leading(
Icon(
SpotubeIcons.search,
color: context.theme.colorScheme.mutedForeground,
),
),
InputFeature.trailing(
ListenableBuilder(
listenable: controller,
builder: (context, _) {
return AnimatedCrossFade(
duration: const Duration(milliseconds: 300),
crossFadeState: controller.text.isEmpty
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild:
const SizedBox.square(dimension: 20),
secondChild: AnimatedScale(
duration:
const Duration(milliseconds: 300),
scale: controller.text.isEmpty ? 0 : 1,
child: IconButton.ghost(
size: const ButtonSize(.6),
icon: const Icon(SpotubeIcons.close),
onPressed: () {
controller.clear();
notifier.clearFilter();
},
),
),
);
}),
)
],
),
),
),
SortTracksDropdown(
value: state.sortBy,
onChanged: (value) {
notifier.sortTracks(value);
},
),
const TrackPresentationActionsSection(),
],
),
),
],
),
);
});
}
}