147 lines
4.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:manager_app/Components/multi_string_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/Slider/new_update_image_slider.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/l10n/app_localizations.dart';
import 'package:manager_api_new/api.dart';
/// Les champs d'un `ContentDTO`, tels qu'ils étaient dans
/// `showNewOrUpdateContentSlider` — mais posés dans le panneau d'édition en
/// place du gabarit B au lieu d'une modale.
class ContentFields extends StatefulWidget {
const ContentFields({
Key? key,
required this.content,
required this.onChanged,
this.showTitle = true,
this.showDescription = true,
this.resourceTypes = kSliderContentResourceTypes,
}) : super(key: key);
final ContentDTO content;
final VoidCallback onChanged;
final bool showTitle;
final bool showDescription;
final List<ResourceType> resourceTypes;
@override
State<ContentFields> createState() => _ContentFieldsState();
}
class _ContentFieldsState extends State<ContentFields> {
@override
Widget build(BuildContext context) {
final l = AppLocalizations.of(context)!;
final content = widget.content;
return LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth > 520;
final resource = ResourceInputContainer(
label: l.resourceColonLabel,
initialValue: content.resourceId,
inResourceTypes: widget.resourceTypes,
onChanged: (ResourceDTO resource) {
setState(() {
if (resource.id == null) {
content.resourceId = null;
content.resource = null;
} else {
content.resourceId = resource.id;
content.resource = resource;
}
});
widget.onChanged();
},
);
final texts = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (widget.showTitle)
MultiStringInputContainer(
label: l.displayTitleLabel,
modalLabel: l.titleModalLabel,
isHTML: true,
initialValue: content.title ?? [],
onGetResult: (value) {
setState(() => content.title = value);
widget.onChanged();
},
maxLines: 1,
isTitle: true,
showPreview: true,
),
if (widget.showTitle && widget.showDescription)
const SizedBox(height: kSpace5),
if (widget.showDescription)
MultiStringInputContainer(
label: l.displayedDescriptionLabel,
modalLabel: l.descriptionModalLabel,
isHTML: true,
initialValue: content.description ?? [],
isMandatory: false,
onGetResult: (value) {
setState(() => content.description = value);
widget.onChanged();
},
maxLines: 1,
isTitle: false,
showPreview: true,
),
],
);
if (!widget.showTitle && !widget.showDescription) return resource;
if (isWide) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
resource,
const SizedBox(width: kSpace5),
Expanded(child: texts),
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
resource,
const SizedBox(height: kSpace5),
texts,
],
);
},
);
}
}
/// Le libellé d'un contenu dans la liste : son titre FR, ou son rang.
String contentLabel(AppLocalizations l, ContentDTO content, int index) {
final titles = content.title ?? [];
final title = titles.isEmpty
? ""
: (titles
.firstWhere((t) => t.language == 'FR', orElse: () => titles.first)
.value ??
"");
final plain = title.replaceAll(RegExp(r'<[^>]*>'), ' ').trim();
return plain.isEmpty ? l.untitledContent(index + 1) : plain;
}
/// Un contenu vide, avec une traduction par langue de la configuration —
/// c'est ce que faisait `showNewOrUpdateContentSlider` à l'ouverture.
ContentDTO emptyContent(List<String> languages) {
final content = ContentDTO();
content.title = <TranslationDTO>[];
content.description = <TranslationDTO>[];
for (final language in languages) {
content.title!.add(TranslationDTO(language: language, value: ""));
content.description!.add(TranslationDTO(language: language, value: ""));
}
return content;
}