manager-app/lib/Components/translation_input_and_resource_container.dart

251 lines
7.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_app/constants.dart';
import 'flag_decoration.dart';
class TranslationInputAndResourceContainer extends StatefulWidget {
TranslationInputAndResourceContainer({
Key? key,
required this.isTitle,
required this.values,
required this.newValues,
required this.onGetResult,
required this.maxLines,
required this.resourceTypes,
}) : super(key: key);
bool isTitle;
List<TranslationAndResourceDTO> values;
List<TranslationAndResourceDTO> newValues;
Function onGetResult;
int maxLines;
List<ResourceType>? resourceTypes;
@override
State<TranslationInputAndResourceContainer> createState() =>
_TranslationInputAndResourceContainerState();
}
class _TranslationInputAndResourceContainerState
extends State<TranslationInputAndResourceContainer> {
late Map<String, QuillController> _controllers;
bool _isEnforcingLimit = false;
@override
void initState() {
super.initState();
_controllers = _buildControllers();
}
static const _emptyDelta = [{'insert': '\n'}];
List<dynamic> _htmlToDeltaJson(String html) {
if (html.trim().isEmpty) return _emptyDelta;
final ops = HtmlToDelta().convert(html).toJson();
return ops.isEmpty ? _emptyDelta : ops;
}
Map<String, QuillController> _buildControllers() {
final map = <String, QuillController>{};
for (final translation in widget.newValues) {
final html = translation.value ?? '';
final controller = QuillController(
document: Document.fromJson(_htmlToDeltaJson(html)),
selection: const TextSelection.collapsed(offset: 0),
);
_setupListener(translation.language!, controller);
map[translation.language!] = controller;
}
return map;
}
void _setupListener(String lang, QuillController controller) {
controller.document.changes.listen((_) {
if (!mounted || _isEnforcingLimit) return;
final limit = widget.isTitle ? kTitleMaxLength : kDescriptionMaxLength;
final plain = controller.document.toPlainText().trimRight();
if (plain.length > limit) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted && !_isEnforcingLimit) {
_isEnforcingLimit = true;
controller.undo();
_isEnforcingLimit = false;
}
});
return;
}
widget.newValues
.firstWhere((e) => e.language == lang)
.value = _controllerToHtml(controller);
});
}
String _controllerToHtml(QuillController controller) {
final ops = controller.document.toDelta().toJson();
return QuillDeltaToHtmlConverter(
List<Map<String, dynamic>>.from(ops),
).convert();
}
@override
void dispose() {
for (final c in _controllers.values) {
c.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.newValues.map((t) => _buildLanguageSection(t)).toList(),
);
}
Widget _buildLanguageSection(TranslationAndResourceDTO translation) {
final lang = translation.language!;
// Resource-only mode (no text editor)
if (widget.resourceTypes != null && _controllers[lang] == null) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildLangHeader(lang),
const SizedBox(height: 4),
SizedBox(
width: 250,
height: 120,
child: ResourceInputContainer(
label: "",
initialValue: translation.resourceId,
inResourceTypes: widget.resourceTypes!,
onChanged: (ResourceDTO resource) {
setState(() {
translation.resourceId =
resource.id == null ? null : resource.id;
translation.resource =
resource.id == null ? null : resource;
});
},
),
),
],
),
);
}
final controller = _controllers[lang]!;
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildLangHeader(lang),
const SizedBox(height: 4),
QuillSimpleToolbar(
controller: controller,
config: _buildToolbarConfig(),
),
Container(
height: widget.isTitle ? 100 : 200,
decoration: BoxDecoration(
color: kBackgroundColor,
border: Border.all(color: Colors.grey.shade300),
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(4),
bottomRight: Radius.circular(4),
),
),
child: QuillEditor.basic(
controller: controller,
config: const QuillEditorConfig(
scrollable: true,
expands: false,
padding: EdgeInsets.all(8),
autoFocus: false,
),
),
),
if (widget.resourceTypes != null) ...[
const SizedBox(height: 8),
ResourceInputContainer(
label: "Ressource à afficher :",
initialValue: translation.resourceId,
color: kPrimaryColor,
inResourceTypes: widget.resourceTypes!,
isLanguageTab: true,
onChanged: (ResourceDTO resource) {
setState(() {
translation.resourceId =
resource.id == null ? null : resource.id;
translation.resource =
resource.id == null ? null : resource;
});
},
isSmall: true,
),
],
],
),
);
}
Widget _buildLangHeader(String lang) {
return Row(
children: [
FlagDecoration(language: lang),
const SizedBox(width: 8),
Text(
lang.toUpperCase(),
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14),
),
],
);
}
QuillSimpleToolbarConfig _buildToolbarConfig() {
return QuillSimpleToolbarConfig(
showBoldButton: true,
showItalicButton: true,
showColorButton: true,
showBackgroundColorButton: true,
showListBullets: !widget.isTitle,
showListNumbers: !widget.isTitle,
showListCheck: false,
showClearFormat: true,
showUnderLineButton: false,
showStrikeThrough: false,
showInlineCode: false,
showSubscript: false,
showSuperscript: false,
showSmallButton: false,
showLineHeightButton: false,
showHeaderStyle: false,
showLink: false,
showSearchButton: false,
showQuote: false,
showCodeBlock: false,
showIndent: false,
showAlignmentButtons: false,
showLeftAlignment: false,
showCenterAlignment: false,
showRightAlignment: false,
showJustifyAlignment: false,
showDirection: false,
showUndo: false,
showRedo: false,
showClipboardCut: false,
showClipboardCopy: false,
showClipboardPaste: false,
);
}
}