refactor: StockList
This commit is contained in:
parent
ea611f0cdc
commit
454850c6b3
@ -1,9 +1,18 @@
|
|||||||
import { Button } from "../components/Button.tsx";
|
import { Button } from "../components/Button.tsx";
|
||||||
import { useEffect, useRef, useLayoutEffect } from "preact/hooks";
|
import { useEffect, useLayoutEffect, useRef } from "preact/hooks";
|
||||||
import { ComponentChildren } from "preact";
|
import { ComponentChildren } from "preact";
|
||||||
import { Signal, useSignal } from "@preact/signals";
|
import { Signal, useSignal } from "@preact/signals";
|
||||||
import { IS_BROWSER } from "$fresh/runtime.ts";
|
import { IS_BROWSER } from "$fresh/runtime.ts";
|
||||||
import { mapValues } from "$std/collections/map_values.ts";
|
import { mapValues } from "$std/collections/map_values.ts";
|
||||||
|
import { useAsync } from "../util/util.ts";
|
||||||
|
import {
|
||||||
|
Coperation,
|
||||||
|
CorpSimple,
|
||||||
|
fetchKosdaqList,
|
||||||
|
fetchKospiList,
|
||||||
|
fetchPageInfo,
|
||||||
|
PageCorpsInfo,
|
||||||
|
} from "../util/api.ts";
|
||||||
|
|
||||||
interface StockProps {
|
interface StockProps {
|
||||||
pageName: string;
|
pageName: string;
|
||||||
@ -31,100 +40,51 @@ function ToggleButton(props: ToggleButtonProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryStatus<T> = {
|
function StockListByDate(
|
||||||
type: "loading";
|
{ prevSet, rows, name }: {
|
||||||
} | {
|
prevSet: Set<string>;
|
||||||
type: "complete";
|
rows: Coperation[];
|
||||||
data: T;
|
|
||||||
} | {
|
|
||||||
type: "error";
|
|
||||||
err: Error;
|
|
||||||
};
|
|
||||||
|
|
||||||
function useAsync<T>(fn: () => Promise<T>): Signal<QueryStatus<T>> {
|
|
||||||
const state = useSignal({
|
|
||||||
type: "loading",
|
|
||||||
} as QueryStatus<T>);
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
const data = await fn();
|
|
||||||
state.value = {
|
|
||||||
type: "complete",
|
|
||||||
data: data,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
state.value = {
|
|
||||||
type: "error",
|
|
||||||
err: err,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Coperation {
|
|
||||||
Name: string;
|
|
||||||
Code: string;
|
|
||||||
Sector: string;
|
|
||||||
Product: string;
|
|
||||||
ListingDay: string;
|
|
||||||
ClosingMonth: string;
|
|
||||||
Representative: string;
|
|
||||||
Homepage: string;
|
|
||||||
AddressArea: string;
|
|
||||||
LastUpdate: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PageCorpsInfo {
|
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
},
|
||||||
corpListByDate: Record<string, Coperation[]>;
|
) {
|
||||||
}
|
|
||||||
|
|
||||||
interface CorpSimple {
|
|
||||||
code: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function StockListByDate({prevSet, rows, name}:{prevSet:Set<string>,
|
|
||||||
rows:Coperation[],
|
|
||||||
name: string}){
|
|
||||||
const lastCount = useRef(rows.length);
|
const lastCount = useRef(rows.length);
|
||||||
const curCount = rows.length;
|
const curCount = rows.length;
|
||||||
const parent = useRef<HTMLDivElement>(null);
|
const parent = useRef<HTMLDivElement>(null);
|
||||||
const controller = useRef<{
|
const controller = useRef<
|
||||||
isEnabled: ()=> boolean,
|
{
|
||||||
disable: ()=>void,
|
isEnabled: () => boolean;
|
||||||
enable: ()=> void
|
disable: () => void;
|
||||||
}| undefined>();
|
enable: () => void;
|
||||||
useEffect(()=>{
|
} | undefined
|
||||||
(async ()=>{
|
>();
|
||||||
console.log("animation mount on ",name);
|
useEffect(() => {
|
||||||
const {default:autoAnimate} = await import("https://esm.sh/@formkit/auto-animate@0.7.0");
|
(async () => {
|
||||||
if (parent.current){
|
console.log("animation mount on ", name);
|
||||||
|
const { default: autoAnimate } = await import(
|
||||||
|
"https://esm.sh/@formkit/auto-animate@0.7.0"
|
||||||
|
);
|
||||||
|
if (parent.current) {
|
||||||
const cntr = autoAnimate(parent.current);
|
const cntr = autoAnimate(parent.current);
|
||||||
controller.current = cntr;
|
controller.current = cntr;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},[parent]);
|
}, [parent]);
|
||||||
|
|
||||||
useLayoutEffect(()=>{
|
useLayoutEffect(() => {
|
||||||
if (controller.current){
|
if (controller.current) {
|
||||||
if (Math.abs(curCount - lastCount.current) > 200){
|
if (Math.abs(curCount - lastCount.current) > 200) {
|
||||||
console.log('disable animation', curCount, "from", lastCount.current);
|
console.log("disable animation", curCount, "from", lastCount.current);
|
||||||
controller.current.disable();
|
controller.current.disable();
|
||||||
}
|
} else {
|
||||||
else {
|
console.log("enable animation", curCount, "from", lastCount.current);
|
||||||
console.log('enable animation', curCount, "from", lastCount.current);
|
|
||||||
controller.current.enable();
|
controller.current.enable();
|
||||||
}
|
}
|
||||||
lastCount.current = curCount;
|
lastCount.current = curCount;
|
||||||
}
|
}
|
||||||
}, [parent, rows]);
|
}, [parent, rows]);
|
||||||
|
|
||||||
return <div ref={parent}>
|
return (
|
||||||
|
<div ref={parent}>
|
||||||
<h2 class="text-lg">{name}</h2>
|
<h2 class="text-lg">{name}</h2>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
const firstOccur = !prevSet.has(row.Code);
|
const firstOccur = !prevSet.has(row.Code);
|
||||||
@ -142,7 +102,8 @@ name: string}){
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function StockList({ data }: { data: PageCorpsInfo }) {
|
function StockList({ data }: { data: PageCorpsInfo }) {
|
||||||
@ -159,14 +120,13 @@ function StockList({ data }: { data: PageCorpsInfo }) {
|
|||||||
const prevSet = i == 0 ? new Set<string>() : sets[i - 1];
|
const prevSet = i == 0 ? new Set<string>() : sets[i - 1];
|
||||||
const rows = corpListByDate[x];
|
const rows = corpListByDate[x];
|
||||||
return (
|
return (
|
||||||
<StockListByDate key={x} name={x} prevSet={prevSet} rows={rows}></StockListByDate>
|
<StockListByDate key={x} name={x} prevSet={prevSet} rows={rows} />
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type FilterInfoOption = {
|
type FilterInfoOption = {
|
||||||
list: {
|
list: {
|
||||||
items: CorpSimple[];
|
items: CorpSimple[];
|
||||||
@ -193,17 +153,13 @@ function filterInfo(info: Coperation[], filterList: FilterInfoOption) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function StockListUI(props: StockProps) {
|
export default function StockListUI(props: StockProps) {
|
||||||
const sig = useAsync<[PageCorpsInfo, CorpSimple[], CorpSimple[]]>(async () => {
|
const sig = useAsync<[PageCorpsInfo, CorpSimple[], CorpSimple[]]>(() =>
|
||||||
const res = await Promise.all([
|
Promise.all([
|
||||||
fetch("/api/pages/" + encodeURIComponent(props.pageName)),
|
fetchPageInfo(props.pageName),
|
||||||
fetch("/api/kospi"),
|
fetchKospiList(),
|
||||||
fetch("/api/kosdaq"),
|
fetchKosdaqList(),
|
||||||
]);
|
])
|
||||||
const corpsInfo = await res[0].json() as PageCorpsInfo;
|
);
|
||||||
const kospi = await res[1].json();
|
|
||||||
const kosdaq = await res[2].json();
|
|
||||||
return [corpsInfo, kospi, kosdaq];
|
|
||||||
});
|
|
||||||
const viewKospi = useSignal(true);
|
const viewKospi = useSignal(true);
|
||||||
const viewKosdaq = useSignal(false);
|
const viewKosdaq = useSignal(false);
|
||||||
const viewOtherwise = useSignal(false);
|
const viewOtherwise = useSignal(false);
|
||||||
@ -227,34 +183,47 @@ export default function StockListUI(props: StockProps) {
|
|||||||
<p>File Loading Failed</p>
|
<p>File Loading Failed</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: <StockList data={applyFilter(sig.value.data[0], sig.value.data[1], sig.value.data[2])}></StockList>}
|
: (
|
||||||
|
<StockList
|
||||||
|
data={applyFilter(
|
||||||
|
sig.value.data[0],
|
||||||
|
sig.value.data[1],
|
||||||
|
sig.value.data[2],
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
function applyFilter(data: PageCorpsInfo, kospi: CorpSimple[], kosdaq: CorpSimple[]): PageCorpsInfo{
|
function applyFilter(
|
||||||
const filter = getFilters(kospi,kosdaq);
|
data: PageCorpsInfo,
|
||||||
|
kospi: CorpSimple[],
|
||||||
|
kosdaq: CorpSimple[],
|
||||||
|
): PageCorpsInfo {
|
||||||
|
const filter = getFilters(kospi, kosdaq);
|
||||||
return {
|
return {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
description: data.description,
|
description: data.description,
|
||||||
corpListByDate: mapValues(data.corpListByDate, (it: Coperation[])=>{
|
corpListByDate: mapValues(data.corpListByDate, (it: Coperation[]) => {
|
||||||
return filterInfo(it, filter);
|
return filterInfo(it, filter);
|
||||||
})
|
}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
function getFilters(
|
||||||
function getFilters(kospi: CorpSimple[], kosdaq: CorpSimple[]): FilterInfoOption{
|
kospi: CorpSimple[],
|
||||||
|
kosdaq: CorpSimple[],
|
||||||
|
): FilterInfoOption {
|
||||||
return {
|
return {
|
||||||
otherwise: viewOtherwise.value,
|
otherwise: viewOtherwise.value,
|
||||||
list: [{
|
list: [{
|
||||||
include: viewKospi.value,
|
include: viewKospi.value,
|
||||||
items: kospi
|
items: kospi,
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
include: viewKosdaq.value,
|
include: viewKosdaq.value,
|
||||||
items: kosdaq
|
items: kosdaq,
|
||||||
}
|
}],
|
||||||
]
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
util/api.ts
Normal file
38
util/api.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
export interface Coperation {
|
||||||
|
Name: string;
|
||||||
|
Code: string;
|
||||||
|
Sector: string;
|
||||||
|
Product: string;
|
||||||
|
ListingDay: string;
|
||||||
|
ClosingMonth: string;
|
||||||
|
Representative: string;
|
||||||
|
Homepage: string;
|
||||||
|
AddressArea: string;
|
||||||
|
LastUpdate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageCorpsInfo {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
corpListByDate: Record<string, Coperation[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CorpSimple {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchPageInfo(pageName: string): Promise<PageCorpsInfo>{
|
||||||
|
const res = await fetch("/api/pages/" + encodeURIComponent(pageName));
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchKospiList(): Promise<CorpSimple[]>{
|
||||||
|
const res = await fetch("/api/kospi");
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchKosdaqList(): Promise<CorpSimple[]> {
|
||||||
|
const res = await fetch("/api/kosdaq");
|
||||||
|
return await res.json();
|
||||||
|
}
|
35
util/util.ts
Normal file
35
util/util.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Signal, useSignal } from "@preact/signals";
|
||||||
|
import { useEffect } from "preact/hooks";
|
||||||
|
|
||||||
|
export type QueryStatus<T> = {
|
||||||
|
type: "loading";
|
||||||
|
} | {
|
||||||
|
type: "complete";
|
||||||
|
data: T;
|
||||||
|
} | {
|
||||||
|
type: "error";
|
||||||
|
err: Error;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useAsync<T>(fn: () => Promise<T>): Signal<QueryStatus<T>> {
|
||||||
|
const state = useSignal({
|
||||||
|
type: "loading",
|
||||||
|
} as QueryStatus<T>);
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const data = await fn();
|
||||||
|
state.value = {
|
||||||
|
type: "complete",
|
||||||
|
data: data,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
state.value = {
|
||||||
|
type: "error",
|
||||||
|
err: err,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
return state;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user