`(stroke 描边、currentColor
* 取文字颜色),列表 / 卡片里直接内联;日历那种纯文本的地方用同一图标的
* emoji 兜底(`emo()`)—— 日历的事件标题是纯文本,塞不进 SVG。
*
* ⚠️ **图标 key 是**契约**:它存在 settings(anniv_kinds)里,用户自己选。
* 所以:① 已有 key 永远不要改名(老数据会变成"认不出的图标");
* ② 新增图标只能往后加;③ 认不出的 key 一律回落 `dot`,不抛异常
* (用户可能手改过库、或跨版本导入过配置)。
*
* @see \App\Anniversary::kindsFull() 分类清单(名称 + 图标)的读写
*/
declare(strict_types=1);
namespace App;
final class AnnivIcon
{
/** 认不出/没配时用哪个 */
public const FALLBACK = 'dot';
/**
* key => [中文名, emoji, SVG 内容(24×24 画布,stroke 描边)]
*
* ⚠️ SVG 内容里**不要**写 `fill` / `stroke` —— 由 svg() 统一给
* `fill="none" stroke="currentColor"`,这样图标颜色永远跟着所在位置的文字色,
* 浅底深底都不会变成"看不见的黑块"。
*/
public const LIST = [
'cake' => ['蛋糕(生日)', '🎂',
''
. ''
. ''
. ''
. ''],
'ring' => ['戒指(结婚)', '💍',
''
. ''
. ''],
'heart' => ['爱心(相识/恋爱)', '❤️',
''],
'candle' => ['蜡烛(忌日/追思)', '🕯️',
''
. ''
. ''
. ''],
'star' => ['星星(节日)', '⭐',
''],
'gift' => ['礼物', '🎁',
''
. ''
. ''
. ''],
'baby' => ['孩子(满月/出生)', '👶',
''
. ''
. ''],
'home' => ['家(搬家/乔迁)', '🏠',
''
. ''],
'book' => ['书(毕业/升学)', '📖',
''
. ''],
'plane' => ['飞机(远行/旅行)', '✈️',
''],
'camera' => ['相机(合影/留念)', '📷',
''
. ''
. ''],
'music' => ['音符(音乐/演出)', '🎵',
''
. ''
. ''],
'trophy' => ['奖杯(成就/晋升)', '🏆',
''
. ''
. ''
. ''],
'clock' => ['时钟(周年/入职)', '⏰',
''
. ''],
'flower' => ['花(表白/祝福)', '🌸',
''
. ''
. ''
. ''
. ''
. ''],
'leaf' => ['叶(自然而然)', '🌿',
''
. ''],
'bell' => ['铃铛(提醒/纪念)', '🔔',
''
. ''
. ''],
'dot' => ['圆点(其它)', '⚪',
''],
];
public static function has(string $key): bool
{
return $key !== '' && isset(self::LIST[$key]);
}
/** 认不出的 key 一律回落,绝不抛异常(配置可能来自旧版本/手改) */
public static function normalize(string $key): string
{
$key = strtolower(trim($key));
return self::has($key) ? $key : self::FALLBACK;
}
/** 中文名(给设置页的下拉用) */
public static function label(string $key): string
{
return self::LIST[self::normalize($key)][0];
}
public static function emo(string $key): string
{
return self::LIST[self::normalize($key)][1];
}
/**
* 行内 SVG。
*
* ⚠️ 返回值是**可信 HTML**(不含任何用户输入:key 先 normalize,size 转 int),
* 模板里直接 `= ... ?>` 输出,不要 e() 一遍(那会把标签转义成文字)。
* aria-hidden:图标永远是"文字旁边的装饰",读屏器念分类名就够了。
*/
public static function svg(string $key, int $size = 15, string $cls = ''): string
{
$k = self::normalize($key);
$n = max(8, min(96, $size));
return '';
}
/** 设置页下拉里能选的全部图标:key => "emoji 中文名" */
public static function choices(): array
{
$out = [];
foreach (self::LIST as $k => $v) {
$out[$k] = $v[1] . ' ' . $v[0];
}
return $out;
}
}