Compare commits
2
Commits
48999f5699
...
a8eb8f9ab1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8eb8f9ab1 | ||
|
|
6471f4365a |
@@ -115,13 +115,13 @@ function AcntgCodeTabPanel() {
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>코드</TableHead>
|
||||
<TableHead>코드명</TableHead>
|
||||
<TableHead>설명</TableHead>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableHead>정렬순서</TableHead>
|
||||
<TableHead>구분</TableHead>
|
||||
<TableHead>관리</TableHead>
|
||||
<TableHead scope="col">코드</TableHead>
|
||||
<TableHead scope="col">코드명</TableHead>
|
||||
<TableHead scope="col">설명</TableHead>
|
||||
<TableHead scope="col">사용 여부</TableHead>
|
||||
<TableHead scope="col">정렬순서</TableHead>
|
||||
<TableHead scope="col">구분</TableHead>
|
||||
<TableHead scope="col">관리</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
||||
@@ -40,9 +40,10 @@ function AcntgSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Acn
|
||||
value={row.acntgSeCd}
|
||||
onChange={(e) => onChange({ acntgSeCd: e.target.value })}
|
||||
placeholder="코드"
|
||||
title="코드"
|
||||
maxLength={10}
|
||||
disabled={!row.isNew}
|
||||
className="flex-1"
|
||||
className="flex-1 disabled:opacity-100"
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -51,6 +52,7 @@ function AcntgSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Acn
|
||||
value={row.acntgSeCdNm ?? ""}
|
||||
onChange={(e) => onChange({ acntgSeCdNm: e.target.value })}
|
||||
placeholder="코드명"
|
||||
title="코드명"
|
||||
maxLength={100}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -59,6 +61,7 @@ function AcntgSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Acn
|
||||
value={row.expln ?? ""}
|
||||
onChange={(e) => onChange({ expln: e.target.value })}
|
||||
placeholder="설명"
|
||||
title="설명"
|
||||
maxLength={1000}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -66,6 +69,7 @@ function AcntgSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Acn
|
||||
<RadioGroup
|
||||
value={row.useYn === "N" ? "N" : "Y"}
|
||||
onValueChange={(v) => onChange({ useYn: v })}
|
||||
aria-label="사용 여부"
|
||||
className="flex justify-center gap-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -82,6 +86,7 @@ function AcntgSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Acn
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
title="정렬순서"
|
||||
value={row.sortSeq ?? ""}
|
||||
onChange={(e) =>
|
||||
onChange({ sortSeq: e.target.value ? Number(e.target.value) : undefined })
|
||||
|
||||
@@ -115,15 +115,15 @@ function CnstpCodeTabPanel() {
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>코드</TableHead>
|
||||
<TableHead>코드명</TableHead>
|
||||
<TableHead>설명</TableHead>
|
||||
<TableHead>숫자 여부</TableHead>
|
||||
<TableHead>소숫점 단위</TableHead>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableHead>정렬순서</TableHead>
|
||||
<TableHead>구분</TableHead>
|
||||
<TableHead>관리</TableHead>
|
||||
<TableHead scope="col">코드</TableHead>
|
||||
<TableHead scope="col">코드명</TableHead>
|
||||
<TableHead scope="col">설명</TableHead>
|
||||
<TableHead scope="col">숫자 여부</TableHead>
|
||||
<TableHead scope="col">소숫점 단위</TableHead>
|
||||
<TableHead scope="col">사용 여부</TableHead>
|
||||
<TableHead scope="col">정렬순서</TableHead>
|
||||
<TableHead scope="col">구분</TableHead>
|
||||
<TableHead scope="col">관리</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
||||
@@ -40,9 +40,10 @@ function CnstpSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Cns
|
||||
value={row.cnstpSeCd}
|
||||
onChange={(e) => onChange({ cnstpSeCd: e.target.value })}
|
||||
placeholder="코드"
|
||||
title="코드"
|
||||
maxLength={10}
|
||||
disabled={!row.isNew}
|
||||
className="flex-1"
|
||||
className="flex-1 disabled:opacity-100"
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -51,6 +52,7 @@ function CnstpSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Cns
|
||||
value={row.cnstpSeCdNm ?? ""}
|
||||
onChange={(e) => onChange({ cnstpSeCdNm: e.target.value })}
|
||||
placeholder="코드명"
|
||||
title="코드명"
|
||||
maxLength={100}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -59,6 +61,7 @@ function CnstpSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Cns
|
||||
value={row.expln ?? ""}
|
||||
onChange={(e) => onChange({ expln: e.target.value })}
|
||||
placeholder="설명"
|
||||
title="설명"
|
||||
maxLength={1000}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -66,15 +69,16 @@ function CnstpSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Cns
|
||||
<RadioGroup
|
||||
value={row.numYn === "N" ? "N" : "Y"}
|
||||
onValueChange={(v) => onChange({ numYn: v })}
|
||||
aria-label="숫자 여부"
|
||||
className="flex justify-center gap-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="Y" id={`${row.rowKey}-num-y`} />
|
||||
<Label htmlFor={`${row.rowKey}-use-y`}>예</Label>
|
||||
<Label htmlFor={`${row.rowKey}-num-y`}>예</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="N" id={`${row.rowKey}-num-n`} />
|
||||
<Label htmlFor={`${row.rowKey}-use-n`}>아니오</Label>
|
||||
<Label htmlFor={`${row.rowKey}-num-n`}>아니오</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</TableCell>
|
||||
@@ -82,6 +86,7 @@ function CnstpSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Cns
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
title="소숫점 단위"
|
||||
value={row.dcptUnitCnt ?? ""}
|
||||
onChange={(e) =>
|
||||
onChange({ dcptUnitCnt: e.target.value ? Number(e.target.value) : undefined })
|
||||
@@ -93,6 +98,7 @@ function CnstpSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Cns
|
||||
<RadioGroup
|
||||
value={row.useYn === "N" ? "N" : "Y"}
|
||||
onValueChange={(v) => onChange({ useYn: v })}
|
||||
aria-label="사용 여부"
|
||||
className="flex justify-center gap-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -109,6 +115,7 @@ function CnstpSeRowComponent({ row, depth, onChange, onAddChild, onDelete }: Cns
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
title="정렬순서"
|
||||
value={row.sortSeq ?? ""}
|
||||
onChange={(e) =>
|
||||
onChange({ sortSeq: e.target.value ? Number(e.target.value) : undefined })
|
||||
|
||||
@@ -115,13 +115,13 @@ function FnrsSharCodeTabPanel() {
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>코드</TableHead>
|
||||
<TableHead>코드명</TableHead>
|
||||
<TableHead>설명</TableHead>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableHead>정렬순서</TableHead>
|
||||
<TableHead>구분</TableHead>
|
||||
<TableHead>관리</TableHead>
|
||||
<TableHead scope="col">코드</TableHead>
|
||||
<TableHead scope="col">코드명</TableHead>
|
||||
<TableHead scope="col">설명</TableHead>
|
||||
<TableHead scope="col">사용 여부</TableHead>
|
||||
<TableHead scope="col">정렬순서</TableHead>
|
||||
<TableHead scope="col">구분</TableHead>
|
||||
<TableHead scope="col">관리</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
||||
@@ -40,9 +40,10 @@ function FnrsSharSeRowComponent({ row, depth, onChange, onAddChild, onDelete }:
|
||||
value={row.fnrsSharSeCd}
|
||||
onChange={(e) => onChange({ fnrsSharSeCd: e.target.value })}
|
||||
placeholder="코드"
|
||||
title="코드"
|
||||
maxLength={10}
|
||||
disabled={!row.isNew}
|
||||
className="flex-1"
|
||||
className="flex-1 disabled:opacity-100"
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -51,6 +52,7 @@ function FnrsSharSeRowComponent({ row, depth, onChange, onAddChild, onDelete }:
|
||||
value={row.fnrsSharSeCdNm ?? ""}
|
||||
onChange={(e) => onChange({ fnrsSharSeCdNm: e.target.value })}
|
||||
placeholder="코드명"
|
||||
title="코드명"
|
||||
maxLength={100}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -59,6 +61,7 @@ function FnrsSharSeRowComponent({ row, depth, onChange, onAddChild, onDelete }:
|
||||
value={row.expln ?? ""}
|
||||
onChange={(e) => onChange({ expln: e.target.value })}
|
||||
placeholder="설명"
|
||||
title="설명"
|
||||
maxLength={1000}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -66,6 +69,7 @@ function FnrsSharSeRowComponent({ row, depth, onChange, onAddChild, onDelete }:
|
||||
<RadioGroup
|
||||
value={row.useYn === "N" ? "N" : "Y"}
|
||||
onValueChange={(v) => onChange({ useYn: v })}
|
||||
aria-label="사용 여부"
|
||||
className="flex justify-center gap-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -82,6 +86,7 @@ function FnrsSharSeRowComponent({ row, depth, onChange, onAddChild, onDelete }:
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
title="정렬순서"
|
||||
value={row.sortSeq ?? ""}
|
||||
onChange={(e) =>
|
||||
onChange({ sortSeq: e.target.value ? Number(e.target.value) : undefined })
|
||||
|
||||
@@ -81,22 +81,27 @@ export function Footer() {
|
||||
const mainBanners =
|
||||
bannerList?.filter((banner) => banner.bnrTypeCd === "BNRFTER") ?? [];
|
||||
|
||||
const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true }));
|
||||
// 기본은 정지 상태, 사용자가 시작 버튼을 눌러야 자동 넘김
|
||||
const plugin = useRef(
|
||||
Autoplay({ delay: 2000, stopOnInteraction: true, playOnInit: false }),
|
||||
);
|
||||
const [carouselApi, setCarouselApi] = useState<UseEmblaCarouselType[1]>();
|
||||
const [isAutoplaying, setIsAutoplaying] = useState(true);
|
||||
const [isAutoplaying, setIsAutoplaying] = useState(false);
|
||||
|
||||
// 사용자 조작·초점 진입 등으로 자동 넘김이 멈추거나 재개될 때 버튼 상태를 동기화한다
|
||||
// (autoplay:play/stop 이벤트는 플러그인 내부 상태 변경 "전"에 발생하므로 isPlaying() 대신 이벤트 종류로 판단)
|
||||
useEffect(() => {
|
||||
if (!carouselApi) return;
|
||||
|
||||
const syncAutoplayState = () => setIsAutoplaying(plugin.current.isPlaying());
|
||||
syncAutoplayState();
|
||||
carouselApi.on("autoplay:play", syncAutoplayState);
|
||||
carouselApi.on("autoplay:stop", syncAutoplayState);
|
||||
const handleAutoplayPlay = () => setIsAutoplaying(true);
|
||||
const handleAutoplayStop = () => setIsAutoplaying(false);
|
||||
setIsAutoplaying(plugin.current.isPlaying());
|
||||
carouselApi.on("autoplay:play", handleAutoplayPlay);
|
||||
carouselApi.on("autoplay:stop", handleAutoplayStop);
|
||||
|
||||
return () => {
|
||||
carouselApi.off("autoplay:play", syncAutoplayState);
|
||||
carouselApi.off("autoplay:stop", syncAutoplayState);
|
||||
carouselApi.off("autoplay:play", handleAutoplayPlay);
|
||||
carouselApi.off("autoplay:stop", handleAutoplayStop);
|
||||
};
|
||||
}, [carouselApi]);
|
||||
|
||||
@@ -160,26 +165,11 @@ export function Footer() {
|
||||
>지원센타</a>
|
||||
</div>
|
||||
<div className="hidden sm:block">
|
||||
<div className="mt-4 flex justify-end sm:mt-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAutoplayToggle}
|
||||
aria-label={isAutoplaying ? "배너 자동 넘김 멈춤" : "배너 자동 넘김 시작"}
|
||||
className="flex items-center gap-1 rounded-sm border border-border bg-background px-2 py-1 text-sm text-accent-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
>
|
||||
{isAutoplaying ? (
|
||||
<Pause className="size-4" aria-hidden="true" />
|
||||
) : (
|
||||
<Play className="size-4" aria-hidden="true" />
|
||||
)}
|
||||
<span>{isAutoplaying ? "멈춤" : "시작"}</span>
|
||||
</button>
|
||||
</div>
|
||||
<Carousel
|
||||
setApi={setCarouselApi}
|
||||
plugins={[plugin.current]}
|
||||
opts={{ loop: true }}
|
||||
className="relative mb-4 mt-2 h-20 sm:mb-8 sm:h-24"
|
||||
className="relative my-4 h-20 sm:my-8 sm:h-24"
|
||||
>
|
||||
<CarouselContent>
|
||||
{mainBanners && mainBanners.length > 0 ? (
|
||||
@@ -206,6 +196,22 @@ export function Footer() {
|
||||
</CarouselItem>
|
||||
)}
|
||||
</CarouselContent>
|
||||
{/* 자동 넘김 정지/시작: 좌측 화살표 앞에 배치 (탭 순서도 화살표보다 먼저) */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAutoplayToggle}
|
||||
title={isAutoplaying ? "배너 자동 넘김 정지" : "배너 자동 넘김 시작"}
|
||||
className="absolute -left-10 top-1/2 z-10 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full border border-input !bg-background text-accent-foreground hover:!bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
>
|
||||
{isAutoplaying ? (
|
||||
<Pause className="h-4 w-4" aria-hidden="true" />
|
||||
) : (
|
||||
<Play className="h-4 w-4" aria-hidden="true" />
|
||||
)}
|
||||
<span className="sr-only">
|
||||
{isAutoplaying ? "배너 자동 넘김 정지" : "배너 자동 넘김 시작"}
|
||||
</span>
|
||||
</button>
|
||||
<CarouselPrevious className="absolute left-2 top-1/2 z-10 !bg-background text-accent-foreground" />
|
||||
<CarouselNext className="absolute right-2 top-1/2 z-10 !bg-background text-accent-foreground" />
|
||||
</Carousel>
|
||||
|
||||
@@ -284,7 +284,7 @@ export function Header({
|
||||
ref={logoRef}
|
||||
src={getLogoImage()}
|
||||
className="h-[3rem]"
|
||||
alt={`${siteInfo?.siteNm}`}
|
||||
alt={`유역하수도 통합 정보 플랫폼 HASUON - ${siteInfo?.siteNm}`}
|
||||
onLoad={() => {
|
||||
if (logoRef.current) {
|
||||
setLogoWidth(logoRef.current.offsetWidth);
|
||||
|
||||
Reference in New Issue
Block a user