feat: add template legend on the calendar screen

This commit is contained in:
Freek van de Ven 2024-07-06 15:44:48 +02:00 committed by Bart Ribbers
parent a22c8ed69e
commit 63caa27f0a
3 changed files with 226 additions and 3 deletions

View file

@ -14,6 +14,7 @@ class AvailabilityTranslations {
required this.appbarTitle,
required this.editAvailabilityButton,
required this.templateLegendTitle,
required this.templateSelectionLabel,
required this.overviewScreenTitle,
required this.createTemplateButton,
required this.monthYearFormatter,
@ -26,6 +27,7 @@ class AvailabilityTranslations {
this.appbarTitle = "Availability",
this.editAvailabilityButton = "Edit availability",
this.templateLegendTitle = "Templates",
this.templateSelectionLabel = "Selected day(s)",
this.createTemplateButton = "Create a new template",
this.overviewScreenTitle = "Availability",
this.monthYearFormatter = _defaultMonthYearFormatter,
@ -41,6 +43,9 @@ class AvailabilityTranslations {
/// The title for the legend template section on the overview screen
final String templateLegendTitle;
/// The text for the selected days in the template legend
final String templateSelectionLabel;
/// The title on the overview screen
final String overviewScreenTitle;

View file

@ -1,6 +1,8 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/widgets/calendar.dart";
import "package:flutter_availability/src/ui/widgets/template_legend.dart";
import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
///
class AvailabilityOverview extends StatefulWidget {
@ -58,9 +60,35 @@ class _AvailabilityOverviewState extends State<AvailabilityOverview> {
},
);
const templateLegend = SizedBox(
height: 40,
child: Placeholder(),
var templateLegend = TemplateLegend(
onViewTemplates: widget.onViewTemplates,
templates: [
for (var template in <(Color, String)>[
(Colors.red, "Template 1"),
(Colors.blue, "Template 2"),
// do 10 more
(Colors.green, "Template 3"),
(Colors.yellow, "Template 4"),
(Colors.purple, "Template 5"),
(Colors.orange, "Template 6"),
(Colors.teal, "Template 7"),
(Colors.pink, "Template 8"),
(Colors.indigo, "Template 9"),
]) ...[
AvailabilityTemplateModel(
userId: "1",
id: "1",
name: template.$2,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime.now(),
endTime: DateTime.now(),
breaks: [],
),
color: template.$1.value,
),
],
],
);
// if there is no range selected we want to disable the button

View file

@ -0,0 +1,190 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// This shows all the templates of a given month and an option to navigate to
/// the template overview
class TemplateLegend extends StatefulWidget {
///
const TemplateLegend({
required this.templates,
required this.onViewTemplates,
super.key,
});
///
final List<AvailabilityTemplateModel> templates;
/// Callback for when the user wants to navigate to the overview of templates
final VoidCallback onViewTemplates;
@override
State<TemplateLegend> createState() => _TemplateLegendState();
}
class _TemplateLegendState extends State<TemplateLegend> {
bool _templateDrawerOpen = false;
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var textTheme = theme.textTheme;
var colorScheme = theme.colorScheme;
var availabilityScope = AvailabilityScope.of(context);
var options = availabilityScope.options;
var colors = options.colors;
var translations = options.translations;
var templatesAvailable = widget.templates.isNotEmpty;
void onDrawerHeaderClick() {
if (!templatesAvailable) {
return;
}
setState(() {
_templateDrawerOpen = !_templateDrawerOpen;
});
}
var createNewTemplateButton = GestureDetector(
onTap: () => widget.onViewTemplates(),
child: ColoredBox(
color: Colors.transparent,
child: Row(
children: [
const SizedBox(width: 12),
const Icon(Icons.add, size: 20),
const SizedBox(width: 6),
Text(
translations.createTemplateButton,
style: textTheme.bodyLarge,
),
],
),
),
);
return Column(
children: [
GestureDetector(
onTap: onDrawerHeaderClick,
child: ColoredBox(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
translations.templateLegendTitle,
style: textTheme.titleMedium,
),
// a button to open a drawer with all the templates
if (templatesAvailable) ...[
Icon(
_templateDrawerOpen
? Icons.arrow_drop_up
: Icons.arrow_drop_down,
),
],
],
),
),
),
const SizedBox(height: 4),
if (_templateDrawerOpen) ...[
// show a container with all the templates with a max height and after
// that scrollable but as small as possible
Container(
decoration: BoxDecoration(
border: Border.all(
color: theme.colorScheme.onSurface,
width: 1,
),
),
padding: const EdgeInsets.only(right: 2),
child: Column(
children: [
Container(
constraints: const BoxConstraints(
maxHeight: 100,
),
child: Scrollbar(
thumbVisibility: true,
trackVisibility: true,
thickness: 2,
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.templates.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return _TemplateLegendItem(
name: translations.templateSelectionLabel,
backgroundColor: colors.selectedDayColor ??
colorScheme.primaryFixedDim,
borderColor: colorScheme.primary,
);
}
var template = widget.templates[index - 1];
return _TemplateLegendItem(
name: template.name,
backgroundColor: Color(template.color),
);
},
),
),
),
const SizedBox(height: 4),
createNewTemplateButton,
const SizedBox(height: 8),
],
),
),
] else ...[
const Divider(height: 1),
],
if (!templatesAvailable) ...[
const SizedBox(height: 12),
createNewTemplateButton,
],
],
);
}
}
class _TemplateLegendItem extends StatelessWidget {
const _TemplateLegendItem({
required this.name,
required this.backgroundColor,
this.borderColor,
});
final String name;
final Color backgroundColor;
final Color? borderColor;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.only(
top: 10,
left: 12,
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(5),
border: Border.all(
color: borderColor ?? Colors.transparent,
),
),
width: 20,
height: 20,
),
const SizedBox(width: 6),
Text(name, style: Theme.of(context).textTheme.bodyLarge),
],
),
);
}