refactor progress
This commit is contained in:
12
packages/list_utils/package.json
Normal file
12
packages/list_utils/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@fluxer/list_utils",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./src/*": "./src/*",
|
||||
"./*": "./*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluxer/constants": "workspace:*"
|
||||
}
|
||||
}
|
||||
111
packages/list_utils/src/ListFormatting.tsx
Normal file
111
packages/list_utils/src/ListFormatting.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Locales} from '@fluxer/constants/src/Locales';
|
||||
import {getCachedListFormatter} from '@fluxer/list_utils/src/ListFormattingCache';
|
||||
import {formatListWithFallback} from '@fluxer/list_utils/src/ListFormattingFallback';
|
||||
import {isIntlListFormatLocaleSupported, isIntlListFormatSupported} from '@fluxer/list_utils/src/ListFormattingSupport';
|
||||
import type {
|
||||
IListFormatter,
|
||||
ListFormatOptions,
|
||||
ListFormatStyle,
|
||||
ListFormatType,
|
||||
ListFormatterConfig,
|
||||
ResolvedListFormatterConfig,
|
||||
} from '@fluxer/list_utils/src/ListFormattingTypes';
|
||||
|
||||
const DEFAULT_LOCALE = Locales.EN_US;
|
||||
const DEFAULT_LIST_FORMAT_STYLE: ListFormatStyle = 'long';
|
||||
const DEFAULT_LIST_FORMAT_TYPE: ListFormatType = 'conjunction';
|
||||
|
||||
export function isListFormatSupported(): boolean {
|
||||
return isIntlListFormatSupported();
|
||||
}
|
||||
|
||||
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 (!isListFormatSupported()) {
|
||||
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);
|
||||
}
|
||||
|
||||
export function createListFormatter(config: ListFormatterConfig = {}): IListFormatter {
|
||||
const resolvedConfig = resolveListFormatterConfig(config);
|
||||
|
||||
return {
|
||||
format(items: ReadonlyArray<string>): string {
|
||||
return formatItems(items, resolvedConfig);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function formatList(
|
||||
items: Array<string>,
|
||||
locale: string = DEFAULT_LOCALE,
|
||||
options: ListFormatOptions = {},
|
||||
): string {
|
||||
return formatListWithConfig(items, {
|
||||
locale,
|
||||
style: options.style,
|
||||
type: options.type,
|
||||
});
|
||||
}
|
||||
38
packages/list_utils/src/ListFormattingCache.tsx
Normal file
38
packages/list_utils/src/ListFormattingCache.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {ResolvedListFormatterConfig} from '@fluxer/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;
|
||||
}
|
||||
51
packages/list_utils/src/ListFormattingFallback.tsx
Normal file
51
packages/list_utils/src/ListFormattingFallback.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {ListFormatType} from '@fluxer/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}`;
|
||||
}
|
||||
34
packages/list_utils/src/ListFormattingSupport.tsx
Normal file
34
packages/list_utils/src/ListFormattingSupport.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
41
packages/list_utils/src/ListFormattingTypes.tsx
Normal file
41
packages/list_utils/src/ListFormattingTypes.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export interface IListFormatter {
|
||||
format(items: ReadonlyArray<string>): string;
|
||||
}
|
||||
3
packages/list_utils/tsconfig.json
Normal file
3
packages/list_utils/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfigs/package.json"
|
||||
}
|
||||
Reference in New Issue
Block a user