general: snapshot

This commit is contained in:
k
2024-06-30 17:01:15 +02:00
parent 9905eff383
commit b7e8cbea20
68 changed files with 1725 additions and 1535 deletions

View File

@@ -1,31 +1,52 @@
export const priceToUSLocale = (price: number, compact = true) => {
const absolutePrice = Math.abs(price);
const lessThan100 = absolutePrice < 100;
const lessThan1000 = absolutePrice < 1_000;
const biggerThanMillion = absolutePrice >= 1_000_000;
const suffices = ["M", "B", "T", "Q"];
return numberToUSLocale(
price,
lessThan1000 ? (lessThan100 ? 2 : 1) : biggerThanMillion ? 3 : 0,
biggerThanMillion && compact
? {
notation: "compact",
compactDisplay: "short",
}
: undefined,
);
};
export function valueToString(value: number) {
const absoluteValue = Math.abs(value);
export const percentageToUSLocale = (percentage: number) =>
numberToUSLocale(percentage, 1);
// value = absoluteValue;
const numberToUSLocale = (
if (isNaN(value)) {
return "";
// } else if (value === 0) {
// return "0";
} else if (absoluteValue < 10) {
return numberToUSLocale(value, 3);
} else if (absoluteValue < 100) {
return numberToUSLocale(value, 2);
} else if (absoluteValue < 1_000) {
return numberToUSLocale(value, 1);
} else if (absoluteValue < 100_000) {
return numberToUSLocale(value, 0);
} else if (absoluteValue < 1_000_000) {
return `${numberToUSLocale(value / 1_000, 1)}K`;
} else if (absoluteValue >= 1_000_000_000_000_000_000) {
return "Inf.";
}
const log = Math.floor(Math.log10(absoluteValue) - 6);
const letterIndex = Math.floor(log / 3);
const letter = suffices[letterIndex];
const modulused = log % 3;
if (modulused === 0) {
return `${numberToUSLocale(value / (1_000_000 * 1_000 ** letterIndex), 3)}${letter}`;
} else if (modulused === 1) {
return `${numberToUSLocale(value / (1_000_000 * 1_000 ** letterIndex), 2)}${letter}`;
} else {
return `${numberToUSLocale(value / (1_000_000 * 1_000 ** letterIndex), 1)}${letter}`;
}
}
function numberToUSLocale(
value: number,
digits: number,
digits?: number,
options?: Intl.NumberFormatOptions | undefined,
) =>
value.toLocaleString("en-us", {
) {
return value.toLocaleString("en-us", {
...options,
minimumFractionDigits: digits,
maximumFractionDigits: digits,
});
}

View File

@@ -1,7 +1,7 @@
import { createRWS } from "/src/solid/rws";
export const createSelectableList = <T, L extends T[] = T[]>(
list: L,
export const createDynamicList = <T, L extends T[] = T[]>(
l: L,
parameters?: {
selected?: L[number];
selectedIndex?: number | null;
@@ -10,10 +10,10 @@ export const createSelectableList = <T, L extends T[] = T[]>(
const selected = createRWS<L[number] | null>(null);
const selectedIndex = createRWS<number | null>(null);
const selectableList: SelectableList<L[number], L> = {
const list: DynamicList<L[number], L> = {
selected,
selectedIndex,
list: createRWS(list, {
list: createRWS(l, {
equals: false,
}),
select(s) {
@@ -83,10 +83,10 @@ export const createSelectableList = <T, L extends T[] = T[]>(
toJSON<TJSON, LJSON extends TJSON[] = TJSON[]>(
transform: (value: T) => TJSON,
filter?: (value: T) => boolean,
): JSONSelectableList<TJSON, LJSON> {
): JSONDynamicList<TJSON, LJSON> {
return {
version: 1,
selectedIndex: getIndexOfSelectedInSelectableList(this),
selectedIndex: getIndexOfSelectedInDynamicList(this),
list: (filter ? this.list().filter(filter) : this.list()).map((value) =>
transform(value),
) as LJSON,
@@ -95,18 +95,18 @@ export const createSelectableList = <T, L extends T[] = T[]>(
};
if (parameters?.selected !== undefined) {
selectableList.select(parameters.selected);
list.select(parameters.selected);
} else if (parameters?.selectedIndex !== undefined) {
selectableList.selectIndex(parameters.selectedIndex);
list.selectIndex(parameters.selectedIndex);
}
return selectableList;
return list;
};
export const createSL = createSelectableList;
export const createDSL = createDynamicList;
export const getIndexOfSelectedInSelectableList = <T, L extends T[] = T[]>(
sl: SelectableList<L[number], L>,
export const getIndexOfSelectedInDynamicList = <T, L extends T[] = T[]>(
sl: DynamicList<L[number], L>,
) => {
const selected = sl.selected();

View File

@@ -2,7 +2,7 @@
// JSON
// ---
interface JSONSelectableList<T, L extends T[] = T[]> {
interface JSONDynamicList<T, L extends T[] = T[]> {
readonly version: 1;
selectedIndex: number | null;
readonly list: L;
@@ -12,7 +12,7 @@ interface JSONSelectableList<T, L extends T[] = T[]> {
// Object
// ---
interface SelectableList<T, L extends T[] = T[]> {
interface DynamicList<T, L extends T[] = T[]> {
readonly selected: Accessor<T | null>;
readonly selectedIndex: Accessor<number | null>;
readonly list: RWS<L>;
@@ -29,5 +29,5 @@ interface SelectableList<T, L extends T[] = T[]> {
readonly toJSON: <TJSON, LJSON extends TJSON[] = TJSON[]>(
transform: (value: T) => LJSON[number],
filter?: (value: T) => boolean,
) => JSONSelectableList<TJSON, LJSON>;
) => JSONDynamicList<TJSON, LJSON>;
}

View File

@@ -0,0 +1,140 @@
import { createRWS } from "/src/solid/rws";
import { run } from "../../run";
export const createStaticList = <T, L extends T[] = T[]>(
l: L,
parameters: {
selected?: L[number];
selectedIndex?: number;
saveable?: {
mode: "localStorage" | "URLParams" | "both";
key: string;
};
defaultValue?: L[number];
defaultIndex?: number;
},
) => {
if (
!l.length ||
(parameters.saveable === undefined &&
parameters.selected === undefined &&
parameters.selectedIndex === undefined)
) {
throw Error("not possible");
}
const selected = createRWS<L[number]>(
run(() => {
let savedIndex: number | undefined;
if (parameters.saveable) {
if (parameters.saveable.mode !== "localStorage") {
throw Error("unsupported");
}
const savedRaw = localStorage.getItem(parameters.saveable.key);
if (savedRaw) {
savedIndex = Number(savedRaw);
}
}
if (parameters.selected) {
const found = l.find((v) => v === parameters.selected);
if (!found) {
throw Error("unreachable");
}
return found;
} else {
return (
l.at(savedIndex ?? parameters.selectedIndex!) ??
parameters.defaultValue ??
l[parameters.defaultIndex || 0]
);
}
}),
);
const selectedIndex = createRWS<number>(
run(() => {
if (
parameters.selectedIndex !== null &&
parameters.selectedIndex !== undefined
) {
const found = l.at(parameters.selectedIndex);
if (!found) {
throw Error("unreachable");
}
return parameters.selectedIndex;
} else {
return l.indexOf(selected());
}
}),
);
createEffect(() => {
if (parameters.saveable) {
localStorage.setItem(parameters.saveable.key, String(selectedIndex()));
}
});
const list: StaticList<L[number], L> = {
selected,
selectedIndex,
list: createRWS(l, {
equals: false,
}),
select(s) {
if (this.selected() !== s) {
batch(() => {
selected.set(() => s);
this.selectIndex(this.list().indexOf(s));
});
}
},
selectIndex(i) {
if (i && (i < 0 || i >= this.list().length)) {
throw new Error(
`SelectableList: selectIndex: ${i} is incorrect ! (has ${
this.list().length
} elements)`,
);
}
if (i !== this.selectedIndex()) {
selectedIndex.set(i);
const value = this.list().at(i);
if (value === undefined) {
throw Error("unreachable");
}
this.select(value);
}
},
};
if (parameters?.selected !== undefined) {
list.select(parameters.selected);
} else if (parameters?.selectedIndex !== undefined) {
list.selectIndex(parameters.selectedIndex);
}
return list;
};
export const createSL = createStaticList;
export const getIndexOfSelectedInStaticList = <T, L extends T[] = T[]>(
sl: StaticList<L[number], L>,
) => {
const selected = sl.selected();
return selected ? sl.list().indexOf(selected) : null;
};

View File

@@ -0,0 +1,9 @@
interface StaticList<T, L extends T[] = T[]> {
readonly selected: Accessor<T>;
readonly selectedIndex: Accessor<number>;
readonly list: RWS<L>;
readonly select: <S extends L[number] = L[number]>(s: S) => void;
readonly selectIndex: (index: number) => void;
}
type SL<T, L extends T[] = T[]> = StaticList<T, L>;