Files
2025-09-10 00:07:52 +08:00

210 lines
5.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
// import 'package:ainoval/utils/web_theme.dart';
/// 上下文数据
class ContextData {
/// 构造函数
const ContextData({
required this.title,
this.subtitle,
this.icon,
this.id,
});
/// 标题
final String title;
/// 副标题(可选)
final String? subtitle;
/// 图标(可选,如果不提供会根据内容自动判断)
final IconData? icon;
/// 唯一标识(可选)
final String? id;
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ContextData &&
other.title == title &&
other.subtitle == subtitle &&
other.icon == icon &&
other.id == id;
}
@override
int get hashCode {
return title.hashCode ^
subtitle.hashCode ^
icon.hashCode ^
id.hashCode;
}
}
/// 上下文标签组件
/// 显示上下文信息,支持删除操作,风格简洁现代
class ContextBadge extends StatelessWidget {
/// 构造函数
const ContextBadge({
super.key,
required this.data,
this.onDelete,
this.maxWidth = 200,
this.showDeleteButton = true,
this.globalKey,
});
/// 上下文数据
final ContextData data;
/// 删除回调
final VoidCallback? onDelete;
/// 最大宽度
final double maxWidth;
/// 是否显示删除按钮
final bool showDeleteButton;
/// 全局Key用于定位
final GlobalKey? globalKey;
@override
Widget build(BuildContext context) {
// final isDark = WebTheme.isDarkMode(context);
return Container(
key: globalKey,
constraints: BoxConstraints(maxWidth: maxWidth),
height: 36, // h-9 equivalent
decoration: BoxDecoration(
color: _getBackgroundColor(context),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// 图标
Padding(
padding: const EdgeInsets.only(left: 8, right: 8),
child: Icon(
_getIcon(),
size: 16, // size-4 equivalent
color: _getIconColor(context),
),
),
// 内容
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 4),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
Text(
data.title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600, // font-semibold
color: _getTextColor(context),
height: 1.2, // leading-tight
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
// 副标题
if (data.subtitle != null && data.subtitle!.isNotEmpty) ...[
const SizedBox(height: 1),
Text(
data.subtitle!,
style: TextStyle(
fontSize: 10, // text-xs
color: _getSubtitleColor(context),
height: 1.2, // leading-tight
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
),
// 删除按钮
if (showDeleteButton)
Material(
type: MaterialType.transparency,
child: InkWell(
onTap: onDelete,
borderRadius: BorderRadius.circular(4),
child: Container(
width: 20, // h-5 w-5
height: 20,
margin: const EdgeInsets.only(right: 6),
child: Icon(
Icons.close,
size: 14, // h-3.5 w-3.5
color: _getDeleteButtonColor(context),
),
),
),
),
],
),
);
}
/// 获取图标
IconData _getIcon() {
// 如果提供了自定义图标,直接使用
if (data.icon != null) {
return data.icon!;
}
// 根据标题内容自动判断图标
final title = data.title.toLowerCase();
if (title.contains('act') || title.contains('chapter') || title.contains('scene')) {
return Icons.menu_book_outlined; // block-quote equivalent
} else if (title.contains('novel') || title.contains('book') || title.contains('text')) {
return Icons.menu_book; // book-open equivalent
} else if (title.contains('folder') || title.contains('directory')) {
return Icons.folder_outlined; // folder-closed equivalent
} else {
return Icons.description_outlined; // 默认文档图标
}
}
/// 获取背景颜色
Color _getBackgroundColor(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final title = data.title.toLowerCase();
final bool isContent = title.contains('novel') || title.contains('book') || title.contains('text');
return isContent ? scheme.surfaceContainerHigh : scheme.surfaceContainer;
}
/// 获取图标颜色
Color _getIconColor(BuildContext context) {
return Theme.of(context).colorScheme.onSurfaceVariant;
}
/// 获取文字颜色
Color _getTextColor(BuildContext context) {
return Theme.of(context).colorScheme.onSurface;
}
/// 获取副标题颜色
Color _getSubtitleColor(BuildContext context) {
return Theme.of(context).colorScheme.onSurfaceVariant;
}
/// 获取删除按钮颜色
Color _getDeleteButtonColor(BuildContext context) {
return Theme.of(context).colorScheme.onSurfaceVariant;
}
}