Ocideck/lib/state/editor_provider.dart

123 lines
3.5 KiB
Dart
Raw Normal View History

import 'package:flutter_riverpod/legacy.dart';
enum EditorMode { visual, markdown }
class EditorState {
/// The active slide (shown in the editor/preview). Always part of [selection].
final int selectedIndex;
/// All currently selected slide indices (for bulk actions). Never empty.
final Set<int> selection;
final EditorMode mode;
final String markdownBuffer;
final bool parseError;
const EditorState({
this.selectedIndex = 0,
this.selection = const {0},
this.mode = EditorMode.visual,
this.markdownBuffer = '',
this.parseError = false,
});
bool get hasMultiSelection => selection.length > 1;
EditorState copyWith({
int? selectedIndex,
Set<int>? selection,
EditorMode? mode,
String? markdownBuffer,
bool? parseError,
}) {
return EditorState(
selectedIndex: selectedIndex ?? this.selectedIndex,
selection: selection ?? this.selection,
mode: mode ?? this.mode,
markdownBuffer: markdownBuffer ?? this.markdownBuffer,
parseError: parseError ?? this.parseError,
);
}
}
class EditorNotifier extends StateNotifier<EditorState> {
EditorNotifier() : super(const EditorState());
EditorState get currentState => state;
/// Single-select [index] (clears any multi-selection).
void select(int index) {
if (index == state.selectedIndex && state.selection.length == 1) return;
state = state.copyWith(
selectedIndex: index,
selection: {index},
parseError: false,
);
}
/// Ctrl/Cmd-click: voeg [index] toe of haal 'm uit de selectie.
void toggleSelect(int index) {
final next = Set<int>.from(state.selection);
if (next.contains(index) && next.length > 1) {
next.remove(index);
final active = index == state.selectedIndex
? next.reduce((a, b) => a < b ? a : b)
: state.selectedIndex;
state = state.copyWith(selection: next, selectedIndex: active);
} else {
next.add(index);
state = state.copyWith(selection: next, selectedIndex: index);
}
}
/// Selecteer alle [count] slides (Ctrl/Cmd+A).
void selectAll(int count) {
if (count <= 0) return;
state = state.copyWith(
selection: {for (var i = 0; i < count; i++) i},
selectedIndex: state.selectedIndex.clamp(0, count - 1),
);
}
/// Shift-click: selecteer het bereik van de actieve slide tot [index].
void selectRange(int index) {
final anchor = state.selectedIndex;
final lo = anchor < index ? anchor : index;
final hi = anchor < index ? index : anchor;
state = state.copyWith(
selection: {for (var i = lo; i <= hi; i++) i},
selectedIndex: index,
);
}
void setMode(EditorMode mode, {String? initialMarkdown}) {
state = state.copyWith(
mode: mode,
markdownBuffer: initialMarkdown ?? state.markdownBuffer,
parseError: false,
);
}
void updateMarkdown(String content) {
state = state.copyWith(markdownBuffer: content);
}
void setParseError(bool value) {
state = state.copyWith(parseError: value);
}
/// Clamp/normaliseer de selectie nadat slides zijn verwijderd.
void clampIndex(int maxIndex) {
final max = maxIndex < 0 ? 0 : maxIndex;
final pruned = state.selection.where((i) => i <= max).toSet();
final index = state.selectedIndex > max ? max : state.selectedIndex;
state = state.copyWith(
selectedIndex: index,
selection: pruned.isEmpty ? {index} : pruned,
);
}
}
final editorProvider = StateNotifierProvider<EditorNotifier, EditorState>(
(_) => EditorNotifier(),
);