refactor(app): merge duplicated components and stores (#3383)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-10 23:17:29 +02:00
1 parent c16d414d93
commit 5695510980
202 files changed
+2924 -6532

No files matched your search

-12
View File
@@ -1,12 +0,0 @@
{
"name": "@pkgs/list_utils",
"version": "0.0.0",
"type": "module",
"exports": {
"./src/*": "./src/*",
"./*": "./*"
},
"dependencies": {
"@fluxer/constants": "workspace:*"
}
}
@@ -1,61 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {Locales} from '@fluxer/constants/src/Locales';
import {getCachedListFormatter} from '@pkgs/list_utils/src/ListFormattingCache';
import {formatListWithFallback} from '@pkgs/list_utils/src/ListFormattingFallback';
import {isIntlListFormatLocaleSupported, isIntlListFormatSupported} from '@pkgs/list_utils/src/ListFormattingSupport';
import type {
ListFormatStyle,
ListFormatType,
ListFormatterConfig,
ResolvedListFormatterConfig,
} from '@pkgs/list_utils/src/ListFormattingTypes';
const DEFAULT_LOCALE = Locales.EN_US;
const DEFAULT_LIST_FORMAT_STYLE: ListFormatStyle = 'long';
const DEFAULT_LIST_FORMAT_TYPE: ListFormatType = 'conjunction';
function normalizeLocale(locale: string): string {
if (!isIntlListFormatLocaleSupported(locale)) {
return DEFAULT_LOCALE;
}
return locale;
}
function resolveListFormatterConfig(config: ListFormatterConfig): ResolvedListFormatterConfig {
const requestedLocale = config.locale?.trim();
const locale = requestedLocale == null || requestedLocale === '' ? DEFAULT_LOCALE : normalizeLocale(requestedLocale);
const style = config.style ?? DEFAULT_LIST_FORMAT_STYLE;
const type = config.type ?? DEFAULT_LIST_FORMAT_TYPE;
return {
locale,
style,
type,
};
}
function formatWithIntl(items: ReadonlyArray<string>, config: ResolvedListFormatterConfig): string {
try {
return getCachedListFormatter(config).format(items);
} catch {
return formatListWithFallback(items, config.type);
}
}
function formatItems(items: ReadonlyArray<string>, config: ResolvedListFormatterConfig): string {
if (items.length === 0) {
return '';
}
if (items.length === 1) {
return items[0] ?? '';
}
if (!isIntlListFormatSupported()) {
return formatListWithFallback(items, config.type);
}
return formatWithIntl(items, config);
}
export function formatListWithConfig(items: ReadonlyArray<string>, config: ListFormatterConfig = {}): string {
const resolvedConfig = resolveListFormatterConfig(config);
return formatItems(items, resolvedConfig);
}
@@ -1,19 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {ResolvedListFormatterConfig} from '@pkgs/list_utils/src/ListFormattingTypes';
const formatterCache = new Map<string, Intl.ListFormat>();
export function getCachedListFormatter(config: ResolvedListFormatterConfig): Intl.ListFormat {
const cacheKey = `${config.locale}:${config.style}:${config.type}`;
const cachedFormatter = formatterCache.get(cacheKey);
if (cachedFormatter != null) {
return cachedFormatter;
}
const formatter = new Intl.ListFormat(config.locale, {
style: config.style,
type: config.type,
});
formatterCache.set(cacheKey, formatter);
return formatter;
}
@@ -1,29 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {ListFormatType} from '@pkgs/list_utils/src/ListFormattingTypes';
function getFallbackConjunction(type: ListFormatType): string {
if (type === 'disjunction') {
return 'or';
}
return 'and';
}
export function formatListWithFallback(items: ReadonlyArray<string>, type: ListFormatType): string {
if (items.length === 0) {
return '';
}
if (items.length === 1) {
return items[0] ?? '';
}
if (type === 'unit') {
return items.join(', ');
}
const conjunction = getFallbackConjunction(type);
if (items.length === 2) {
return `${items[0]} ${conjunction} ${items[1]}`;
}
const lastItem = items[items.length - 1];
const leadingItems = items.slice(0, -1).join(', ');
return `${leadingItems}, ${conjunction} ${lastItem}`;
}
@@ -1,16 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
export function isIntlListFormatSupported(): boolean {
return typeof Intl !== 'undefined' && typeof Intl.ListFormat !== 'undefined';
}
export function isIntlListFormatLocaleSupported(locale: string): boolean {
if (!isIntlListFormatSupported()) {
return false;
}
try {
return Intl.ListFormat.supportedLocalesOf([locale]).length > 0;
} catch {
return false;
}
}
@@ -1,19 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
export type ListFormatStyle = 'long' | 'short' | 'narrow';
export type ListFormatType = 'conjunction' | 'disjunction' | 'unit';
export interface ListFormatOptions {
style?: ListFormatStyle;
type?: ListFormatType;
}
export interface ListFormatterConfig extends ListFormatOptions {
locale?: string;
}
export interface ResolvedListFormatterConfig {
locale: string;
style: ListFormatStyle;
type: ListFormatType;
}
-9
View File
@@ -1,9 +0,0 @@
{
"extends": "../../../tsconfigs/package.json",
"compilerOptions": {
"paths": {
"@fluxer/*": ["../../../packages/*", "../../../packages/*/src/index.ts"],
"@pkgs/*": ["../*"]
}
}
}
-12
View File
@@ -1,12 +0,0 @@
{
"name": "@pkgs/number_utils",
"version": "0.0.0",
"type": "module",
"dependencies": {
"@fluxer/constants": "workspace:*"
},
"exports": {
"./src/*": "./src/*",
"./*": "./*"
}
}
@@ -1,7 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {Locales} from '@fluxer/constants/src/Locales';
export const DEFAULT_COMPACT_MAX_FRACTION_DIGITS = 1;
export const DEFAULT_NUMBER_FALLBACK = 0;
export const DEFAULT_NUMBER_LOCALE = Locales.EN_US;
@@ -1,25 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
const numberFormatterCache = new Map<string, Intl.NumberFormat>();
export function getNumberFormatter(
locale: string,
numberFormatOptions: Intl.NumberFormatOptions = {},
): Intl.NumberFormat {
const cacheKey = buildFormatterCacheKey(locale, numberFormatOptions);
const cachedFormatter = numberFormatterCache.get(cacheKey);
if (cachedFormatter !== undefined) {
return cachedFormatter;
}
const formatter = new Intl.NumberFormat(locale, numberFormatOptions);
numberFormatterCache.set(cacheKey, formatter);
return formatter;
}
function buildFormatterCacheKey(locale: string, numberFormatOptions: Intl.NumberFormatOptions): string {
const optionEntries = Object.entries(numberFormatOptions)
.filter(([, value]) => value !== undefined)
.sort(([left], [right]) => left.localeCompare(right));
const optionKey = optionEntries.map(([key, value]) => `${key}:${String(value)}`).join('|');
return `${locale}|${optionKey}`;
}
@@ -1,104 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
DEFAULT_COMPACT_MAX_FRACTION_DIGITS,
DEFAULT_NUMBER_FALLBACK,
DEFAULT_NUMBER_LOCALE,
} from '@pkgs/number_utils/src/NumberConstants';
import {getNumberFormatter} from '@pkgs/number_utils/src/NumberFormatterCache';
import {parseNumberInput} from '@pkgs/number_utils/src/NumberParsing';
import type {
CompactNumberFormatOptions,
NumberFormatBaseOptions,
NumberFormatOptions,
NumberInput,
} from '@pkgs/number_utils/src/NumberTypes';
interface ResolvedNumberFormatBaseOptions {
locale: string;
fallbackValue: number;
}
interface CompactFormatOptionsInput {
maximumFractionDigits?: number;
minimumFractionDigits?: number;
}
function resolveBaseOptions(options?: NumberFormatBaseOptions): ResolvedNumberFormatBaseOptions {
return {
locale: options?.locale ?? DEFAULT_NUMBER_LOCALE,
fallbackValue: options?.fallbackValue ?? DEFAULT_NUMBER_FALLBACK,
};
}
function resolveNumberFormatOptions(optionsOrLocale: NumberFormatOptions | string | undefined): NumberFormatOptions {
if (typeof optionsOrLocale === 'string') {
return {locale: optionsOrLocale};
}
return optionsOrLocale ?? {};
}
function resolveCompactFormatOptions(
optionsOrLocale: CompactNumberFormatOptions | string | undefined,
maximumFractionDigits: number | undefined,
): CompactNumberFormatOptions {
if (typeof optionsOrLocale === 'string') {
return {
locale: optionsOrLocale,
maximumFractionDigits,
};
}
if (optionsOrLocale === undefined) {
if (maximumFractionDigits === undefined) {
return {};
}
return {maximumFractionDigits};
}
if (maximumFractionDigits === undefined) {
return optionsOrLocale;
}
return {
...optionsOrLocale,
maximumFractionDigits: optionsOrLocale.maximumFractionDigits ?? maximumFractionDigits,
};
}
function formatNumberValue(
value: NumberInput,
resolvedOptions: ResolvedNumberFormatBaseOptions,
numberFormatOptions: Intl.NumberFormatOptions = {},
): string {
const parsedValue = parseNumberInput(value, resolvedOptions.fallbackValue);
return getNumberFormatter(resolvedOptions.locale, numberFormatOptions).format(parsedValue);
}
function buildCompactFormatOptions(options: CompactFormatOptionsInput): Intl.NumberFormatOptions {
const numberFormatOptions: Intl.NumberFormatOptions = {
notation: 'compact',
maximumFractionDigits: options.maximumFractionDigits ?? DEFAULT_COMPACT_MAX_FRACTION_DIGITS,
};
if (options.minimumFractionDigits !== undefined) {
numberFormatOptions.minimumFractionDigits = options.minimumFractionDigits;
}
return numberFormatOptions;
}
export function formatNumber(value: NumberInput, locale?: string): string;
export function formatNumber(value: NumberInput, options?: NumberFormatOptions): string;
export function formatNumber(value: NumberInput, optionsOrLocale: NumberFormatOptions | string = {}): string {
const options = resolveNumberFormatOptions(optionsOrLocale);
const resolvedOptions = resolveBaseOptions(options);
return formatNumberValue(value, resolvedOptions, options.numberFormatOptions);
}
export function formatCompactNumber(value: NumberInput, locale?: string, maximumFractionDigits?: number): string;
export function formatCompactNumber(value: NumberInput, options?: CompactNumberFormatOptions): string;
export function formatCompactNumber(
value: NumberInput,
optionsOrLocale: CompactNumberFormatOptions | string = {},
maximumFractionDigits?: number,
): string {
const options = resolveCompactFormatOptions(optionsOrLocale, maximumFractionDigits);
const resolvedOptions = resolveBaseOptions(options);
return formatNumberValue(value, resolvedOptions, buildCompactFormatOptions(options));
}
@@ -1,24 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {DEFAULT_NUMBER_FALLBACK} from '@pkgs/number_utils/src/NumberConstants';
import type {NumberInput} from '@pkgs/number_utils/src/NumberTypes';
export function parseNumberInput(value: NumberInput, fallbackValue: number = DEFAULT_NUMBER_FALLBACK): number {
if (typeof value === 'number') {
if (Number.isFinite(value)) {
return value;
}
return fallbackValue;
}
if (typeof value === 'string') {
const trimmedValue = value.trim();
if (trimmedValue === '') {
return fallbackValue;
}
const parsedValue = Number(trimmedValue);
if (Number.isFinite(parsedValue)) {
return parsedValue;
}
}
return fallbackValue;
}
@@ -1,17 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
export type NumberInput = number | string | null | undefined;
export interface NumberFormatBaseOptions {
locale?: string;
fallbackValue?: number;
}
export interface NumberFormatOptions extends NumberFormatBaseOptions {
numberFormatOptions?: Intl.NumberFormatOptions;
}
export interface CompactNumberFormatOptions extends NumberFormatBaseOptions {
maximumFractionDigits?: number;
minimumFractionDigits?: number;
}
@@ -1,9 +0,0 @@
{
"extends": "../../../tsconfigs/package.json",
"compilerOptions": {
"paths": {
"@fluxer/*": ["../../../packages/*", "../../../packages/*/src/index.ts"],
"@pkgs/*": ["../*"]
}
}
}