154 lines
4.7 KiB
PHP
154 lines
4.7 KiB
PHP
<?php
|
||
|
||
if (! function_exists('format_event_url')) {
|
||
/**
|
||
* format an event url with a given calendar ID and event ID
|
||
*/
|
||
function format_event_url(string $eid, string $cid): string
|
||
{
|
||
return 'calendar/'.$cid.'/event/'.$eid;
|
||
}
|
||
}
|
||
|
||
if (! function_exists('contrast_text_color')) {
|
||
/**
|
||
* Choose an accessible foreground (#fff or #000) against a HEX background.
|
||
*
|
||
* Rule set:
|
||
* 1. Calculate WCAG contrast ratios for both black and white.
|
||
* 2. Prefer the colour with the *higher* ratio.
|
||
* 3. Override: if black wins but ratio < 5.5 AND white > 4, use white.
|
||
*
|
||
* @param string $hex Background colour (3- or 6-digit hex, with or without #)
|
||
* @param string $light Return value for “white” (default '#ffffff')
|
||
* @param string $dark Return value for “black” (default '#000000')
|
||
* @return string
|
||
*/
|
||
function contrast_text_color(string $hex, string $light = '#ffffff', string $dark = '#000000'): string
|
||
{
|
||
// --- normalise ----------------------------------------------------
|
||
$hex = ltrim($hex, '#');
|
||
if (strlen($hex) === 3) { // #abc → #aabbcc
|
||
$hex = preg_replace('/./', '$0$0', $hex);
|
||
}
|
||
|
||
[$r, $g, $b] = [
|
||
hexdec(substr($hex, 0, 2)) / 255,
|
||
hexdec(substr($hex, 2, 2)) / 255,
|
||
hexdec(substr($hex, 4, 2)) / 255,
|
||
];
|
||
|
||
// --- convert sRGB → linear RGB -----------------------------------
|
||
$linear = function (float $c): float {
|
||
return $c <= 0.04045 ? $c / 12.92 : pow(($c + 0.055) / 1.055, 2.4);
|
||
};
|
||
|
||
$R = $linear($r);
|
||
$G = $linear($g);
|
||
$B = $linear($b);
|
||
|
||
// --- relative luminance (ITU-R BT.709) ----------------------------
|
||
$L_bg = 0.2126 * $R + 0.7152 * $G + 0.0722 * $B; // 0–1
|
||
|
||
// --- contrast ratios vs black (L=0) and white (L=1) ---------------
|
||
$contrast_black = ($L_bg + 0.05) / 0.05; // bg lighter than black
|
||
$contrast_white = 1.05 / ($L_bg + 0.05); // white vs bg
|
||
|
||
// --- pick the winner ---------------------------------------------
|
||
$useDark = $contrast_black >= $contrast_white;
|
||
|
||
// override rule if dark is true but white "looks better"
|
||
if ($useDark && $contrast_black < 5.5 && $contrast_white > 4) {
|
||
$useDark = false; // switch to white
|
||
}
|
||
|
||
return $useDark ? $dark : $light;
|
||
}
|
||
}
|
||
|
||
if (! function_exists('is_hex_color')) {
|
||
function is_hex_color(mixed $value): bool
|
||
{
|
||
return is_string($value) && preg_match('/^#[0-9A-Fa-f]{6}$/', $value) === 1;
|
||
}
|
||
}
|
||
|
||
if (! function_exists('calendar_color_palette')) {
|
||
/**
|
||
* returns a cleaned palette from config (valid hex only).
|
||
*
|
||
* @return array<int,string>
|
||
*/
|
||
function calendar_color_palette(): array
|
||
{
|
||
$palette = config('kithkin.calendar.color_palette', []);
|
||
|
||
if (! is_array($palette)) {
|
||
$palette = [];
|
||
}
|
||
|
||
return array_values(array_filter($palette, fn ($c) => is_hex_color($c)));
|
||
}
|
||
}
|
||
|
||
if (! function_exists('calendar_color_failsafe')) {
|
||
function calendar_color_failsafe(): string
|
||
{
|
||
$fallback = config('kithkin.calendar.color_failsafe', '#1a1a1a');
|
||
|
||
return is_hex_color($fallback) ? $fallback : '#1a1a1a';
|
||
}
|
||
}
|
||
|
||
if (! function_exists('default_calendar_color')) {
|
||
/**
|
||
* returns the default calendar color from the configured palette.
|
||
* if the palette is empty/invalid, falls back to the failsafe.
|
||
*/
|
||
function default_calendar_color(): string
|
||
{
|
||
$palette = calendar_color_palette();
|
||
|
||
if (count($palette) === 0) {
|
||
return calendar_color_failsafe();
|
||
}
|
||
|
||
$strategy = config('kithkin.calendar.default_color_strategy', 'first');
|
||
|
||
if ($strategy === 'random') {
|
||
return $palette[array_rand($palette)];
|
||
}
|
||
|
||
return $palette[0];
|
||
}
|
||
}
|
||
|
||
if (! function_exists('calendar_color')) {
|
||
/**
|
||
* pick a calendar color with a single source of truth for defaults.
|
||
*
|
||
* order:
|
||
* 1) $data['color'] if valid
|
||
* 2) $fallback if valid
|
||
* 3) default_calendar_color() (palette-based)
|
||
* 4) hard failsafe from config, then '#1a1a1a'
|
||
*
|
||
* @param array<string,mixed> $data
|
||
*/
|
||
function calendar_color(array $data = [], ?string $fallback = null): string
|
||
{
|
||
$raw = $data['color'] ?? null;
|
||
|
||
if (is_hex_color($raw)) {
|
||
return $raw;
|
||
}
|
||
|
||
if (is_hex_color($fallback)) {
|
||
return $fallback;
|
||
}
|
||
|
||
$default = default_calendar_color();
|
||
return is_hex_color($default) ? $default : calendar_color_failsafe();
|
||
}
|
||
}
|