Merge pull request '[gitea] week 2024-20-v7.0 cherry pick (release/v1.22 -> v7.0/forgejo)' (#3772) from earl-warren/wcp/2024-20-v7.0 into v7.0/forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3772
Reviewed-by: Beowulf <beowulf@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2024-05-16 15:36:08 +00:00
commit 4ecbb2ef1b
53 changed files with 948 additions and 509 deletions

View file

@ -1,23 +1,21 @@
// Check similar implementation in modules/util/color.go and keep synchronization
// Return R, G, B values defined in reletive luminance
function getLuminanceRGB(channel) {
const sRGB = channel / 255;
return (sRGB <= 0.03928) ? sRGB / 12.92 : ((sRGB + 0.055) / 1.055) ** 2.4;
import tinycolor from 'tinycolor2';
// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
// Keep this in sync with modules/util/color.go
function getRelativeLuminance(color) {
const {r, g, b} = tinycolor(color).toRgb();
return (0.2126729 * r + 0.7151522 * g + 0.072175 * b) / 255;
}
// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance
function getLuminance(r, g, b) {
const R = getLuminanceRGB(r);
const G = getLuminanceRGB(g);
const B = getLuminanceRGB(b);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
function useLightText(backgroundColor) {
return getRelativeLuminance(backgroundColor) < 0.453;
}
// Reference from: https://firsching.ch/github_labels.html
// In the future WCAG 3 APCA may be a better solution.
// Check if text should use light color based on RGB of background
export function useLightTextOnBackground(r, g, b) {
return getLuminance(r, g, b) < 0.453;
// Given a background color, returns a black or white foreground color that the highest
// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better.
// https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42
export function contrastColor(backgroundColor) {
return useLightText(backgroundColor) ? '#fff' : '#000';
}
function resolveColors(obj) {

View file

@ -1,21 +1,22 @@
import {useLightTextOnBackground} from './color.js';
import {contrastColor} from './color.js';
test('useLightTextOnBackground', () => {
expect(useLightTextOnBackground(215, 58, 74)).toBe(true);
expect(useLightTextOnBackground(0, 117, 202)).toBe(true);
expect(useLightTextOnBackground(207, 211, 215)).toBe(false);
expect(useLightTextOnBackground(162, 238, 239)).toBe(false);
expect(useLightTextOnBackground(112, 87, 255)).toBe(true);
expect(useLightTextOnBackground(0, 134, 114)).toBe(true);
expect(useLightTextOnBackground(228, 230, 105)).toBe(false);
expect(useLightTextOnBackground(216, 118, 227)).toBe(true);
expect(useLightTextOnBackground(255, 255, 255)).toBe(false);
expect(useLightTextOnBackground(43, 134, 133)).toBe(true);
expect(useLightTextOnBackground(43, 135, 134)).toBe(true);
expect(useLightTextOnBackground(44, 135, 134)).toBe(true);
expect(useLightTextOnBackground(59, 182, 179)).toBe(true);
expect(useLightTextOnBackground(124, 114, 104)).toBe(true);
expect(useLightTextOnBackground(126, 113, 108)).toBe(true);
expect(useLightTextOnBackground(129, 112, 109)).toBe(true);
expect(useLightTextOnBackground(128, 112, 112)).toBe(true);
test('contrastColor', () => {
expect(contrastColor('#d73a4a')).toBe('#fff');
expect(contrastColor('#0075ca')).toBe('#fff');
expect(contrastColor('#cfd3d7')).toBe('#000');
expect(contrastColor('#a2eeef')).toBe('#000');
expect(contrastColor('#7057ff')).toBe('#fff');
expect(contrastColor('#008672')).toBe('#fff');
expect(contrastColor('#e4e669')).toBe('#000');
expect(contrastColor('#d876e3')).toBe('#000');
expect(contrastColor('#ffffff')).toBe('#000');
expect(contrastColor('#2b8684')).toBe('#fff');
expect(contrastColor('#2b8786')).toBe('#fff');
expect(contrastColor('#2c8786')).toBe('#000');
expect(contrastColor('#3bb6b3')).toBe('#000');
expect(contrastColor('#7c7268')).toBe('#fff');
expect(contrastColor('#7e716c')).toBe('#fff');
expect(contrastColor('#81706d')).toBe('#fff');
expect(contrastColor('#807070')).toBe('#fff');
expect(contrastColor('#84b6eb')).toBe('#000');
});

View file

@ -1,25 +1,30 @@
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
import {getCurrentLocale} from '../utils.js';
// Returns an array of millisecond-timestamps of start-of-week days (Sundays)
export function startDaysBetween(startDate, endDate) {
// Ensure the start date is a Sunday
while (startDate.getDay() !== 0) {
startDate.setDate(startDate.getDate() + 1);
}
dayjs.extend(utc);
const start = dayjs(startDate);
const end = dayjs(endDate);
const startDays = [];
/**
* Returns an array of millisecond-timestamps of start-of-week days (Sundays)
*
* @param startConfig The start date. Can take any type that `Date` accepts.
* @param endConfig The end date. Can take any type that `Date` accepts.
*/
export function startDaysBetween(startDate, endDate) {
const start = dayjs.utc(startDate);
const end = dayjs.utc(endDate);
let current = start;
// Ensure the start date is a Sunday
while (current.day() !== 0) {
current = current.add(1, 'day');
}
const startDays = [];
while (current.isBefore(end)) {
startDays.push(current.valueOf());
// we are adding 7 * 24 hours instead of 1 week because we don't want
// date library to use local time zone to calculate 1 week from now.
// local time zone is problematic because of daylight saving time (dst)
// used on some countries
current = current.add(7 * 24, 'hour');
current = current.add(1, 'week');
}
return startDays;
@ -29,10 +34,10 @@ export function firstStartDateAfterDate(inputDate) {
if (!(inputDate instanceof Date)) {
throw new Error('Invalid date');
}
const dayOfWeek = inputDate.getDay();
const dayOfWeek = inputDate.getUTCDay();
const daysUntilSunday = 7 - dayOfWeek;
const resultDate = new Date(inputDate.getTime());
resultDate.setDate(resultDate.getDate() + daysUntilSunday);
resultDate.setUTCDate(resultDate.getUTCDate() + daysUntilSunday);
return resultDate.valueOf();
}