fix(core,engine,ecommerce,admin_basic): 렌더 경로 판정·파서 단일화로 표현식 비대칭 일괄 해소
표현식 해석 로직이 엔진 전역에 복제되면서 복사본마다 기능 집합과 판정 기준이 갈라졌고, 같은 작성이 어느 렌더 경로를 타느냐에 따라 다른 결과를 냈다. 증상 대부분이 예외가 아니라 조용한 오답 또는 값 소실이라 재현 위치를 특정하기 어려웠다. 판정만 BindingShape 단일 출처로 모으고 실행은 지점별로 유지했다. 전면 통합은 회귀 위험이 크고, 갈라진 것은 실행이 아니라 판정이었기 때문이다. - 파이프 평가를 문자열 보간에 위임하면서 생긴 비대칭 3건 해소 (중괄호 노출 / _computed stale 전파 / 원본 타입 소실) - 파이프 분기 누락 8지점 보강 — 평가 지점 모집단을 스캔해 가드를 강제하는 회귀선 신설 - 단일 바인딩·복잡식 판정 14곳을 정본으로 치환, 빈 바인딩 봉인 - BINDING_PATTERN 정규식을 수동 스캐너로 교체, resolveObject 를 key 단위 예외 격리로 전환 - iteration + 항목 참조 if 가 목록 전체를 지우던 결함 해소, 반복 경로 기능 격차 정렬 - 템플릿 컴포넌트의 new Function 자체 평가기 제거 → G7Core.evaluateCondition 위임 - 컨텍스트에 식별자가 아닌 키가 하나라도 있으면 그 컨텍스트의 모든 식이 평가되지 못하던 결함 수정 - SEO 봇 렌더러도 같은 규칙으로 정렬 — React 만 고치면 봇 화면에서만 목록이 빈다 - audit walker 가 레이아웃의 74%(partial 루트 444/598)로 내려가지 못하던 사각 해소, 신규 룰 5종 등재
This commit is contained in:
1 parent
ca4fd02e99
commit
953616552f
58 files changed
+5665
-651
No files matched your search
@@ -107,6 +107,13 @@
|
||||
#### 화면 표시·목록 상태
|
||||
|
||||
- 목록 표의 각 칸에 넣은 날짜·숫자 서식이 적용되지 않아 값이 비어 보이던 문제를 수정했습니다. 같은 서식을 일반 화면에서 쓰면 정상이었지만 목록 표의 칸 안에서는 값이 사라지거나(날짜 서식) 서식이 빠진 원래 값이 그대로 나왔습니다(숫자·대문자 서식). 목록 표와 카드 목록, 펼침 영역 등 반복해서 그려지는 모든 자리에서 서식이 정상 적용됩니다. (#87 @glitter-gim 님께서 제보해주셨습니다.)
|
||||
- 위 문제와 같은 뿌리에서 비롯된 화면 표시 오류를 함께 정리했습니다. 같은 방식으로 작성한 값이 어디에 놓이느냐(목록 칸·본문·표시 조건·버튼 동작)에 따라 다르게 해석되던 것을 한 가지 기준으로 통일했으며, 아래 항목이 그 결과입니다. 대부분 오류 메시지 없이 값이 비거나 잘못 보이던 증상이라 눈치채기 어려웠습니다.
|
||||
- 행마다 달라지는 조건(예: "활성 상태인 항목만 표시")을 목록에 걸면 해당하는 행만 걸러지지 않고 **목록 전체가 사라지던** 문제를 수정했습니다. 이제 행마다 조건을 따져 표시하며, 검색엔진 봇이 보는 화면에도 같은 규칙이 적용됩니다.
|
||||
- 관리자 목록의 여러 개 고를 수 있는 상태·분류 필터처럼 항목 이름에 대괄호가 들어가는 값이 화면에 따라 비어 보이던 문제를 수정했습니다. 선택한 조건이 화면에 표시되지 않거나 조건 표시가 사라지던 증상이 해소됩니다.
|
||||
- 값 하나를 계산하다 실패하면 그 값이 속한 영역 전체가 그려지지 않던 문제를 수정했습니다. 이제 문제가 된 자리만 비고 나머지는 정상 표시됩니다.
|
||||
- 계산식에 날짜·숫자 서식을 함께 걸면 원본 값이 바뀌어도 처음 계산된 값이 계속 표시되던 문제를 수정했습니다.
|
||||
- 목록 칸이나 합계 행 안에서 번역 문구가 문장 중간에 들어간 경우 번역되지 않고 원본 키가 그대로 보이던 문제, 그리고 일부 표시 값에 `{{ }}` 기호가 섞여 나오던 문제를 수정했습니다.
|
||||
- 목록 칸이나 합계 행에서 '번역하지 않고 원문 그대로 표시'로 지정한 값에 보이지 않는 특수문자가 함께 출력되던 문제를 수정했습니다. 화면에서는 빈칸이나 깨진 글자로 보였고, 값을 복사해 붙여넣을 때 따라붙었습니다.
|
||||
- 목록 화면에서 글이나 항목을 열어 보고 돌아올 때 보고 있던 페이지·검색어·필터가 사라지던 문제를 화면 엔진 차원에서 함께 수정했습니다. '현재 주소의 조건을 유지한다'고 지정해도 덧붙일 값이 없으면 유지가 아예 동작하지 않아 조건이 통째로 사라지던 동작을 바로잡았습니다. (#75 @jiwonpapa 님께서 제보해주셨습니다.)
|
||||
|
||||
#### 확장·데이터베이스 안정성
|
||||
|
||||
@@ -235,7 +235,13 @@ class ComponentHtmlMapper
|
||||
$component = $this->applyResponsiveOverrides($component);
|
||||
|
||||
// 조건부 렌더링 (if / condition / conditions)
|
||||
if (! $this->shouldRender($component, $context, $evaluator)) {
|
||||
//
|
||||
// iteration 이 있으면 여기서 끊지 않는다. 조건이 항목 변수(`{{user.is_active}}` 등)를
|
||||
// 참조하면 이 시점 컨텍스트에는 그 변수가 없어 항상 거짓이 되고, 목록 전체가
|
||||
// 사라진다. 항목별 컨텍스트에서 renderIteration 이 같은 조건을 다시 평가한다
|
||||
// (iteration 만 제거한 정의를 넘기므로 `if` 는 그대로 남는다).
|
||||
// React DynamicRenderer 와 동일한 순서 — 한쪽만 고치면 봇 화면에서만 목록이 빈다.
|
||||
if (! isset($component['iteration']) && ! $this->shouldRender($component, $context, $evaluator)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1654,6 +1660,8 @@ class ComponentHtmlMapper
|
||||
$iterContext = array_merge($context, [
|
||||
$itemVar => $item,
|
||||
$indexVar => $index,
|
||||
// `{item_var}_index` 자동 변수 — React 반복 렌더 경로와 동일하게 제공한다.
|
||||
$itemVar.'_index' => $index,
|
||||
]);
|
||||
$html .= $this->renderComponent($templateComponent, $iterContext, $evaluator);
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ php artisan core:build
|
||||
용도: DynamicRenderer가 해당 props의 바인딩을 지연 처리
|
||||
|
||||
예시:
|
||||
- DataGrid: cellChildren, expandChildren, expandContext, render
|
||||
- CardGrid: cardChildren
|
||||
- DataGrid: cellChildren, expandChildren, expandContext, render, subRowChildren, subRowCondition, footerCells, footerCardChildren
|
||||
- CardGrid: cardColumns
|
||||
- Select: optionRenderer
|
||||
\`\`\`
|
||||
|
||||
|
||||
@@ -298,8 +298,8 @@ DynamicRenderer가 props를 처리할 때 `{{...}}` 바인딩 표현식을 즉
|
||||
|
||||
| 컴포넌트 | skipBindingKeys | 이유 |
|
||||
|----------|-----------------|------|
|
||||
| DataGrid | `cellChildren`, `expandChildren`, `expandContext`, `render` | row 컨텍스트로 평가 필요 |
|
||||
| CardGrid | `cardChildren` | item 컨텍스트로 평가 필요 |
|
||||
| DataGrid | `cellChildren`, `expandChildren`, `expandContext`, `render`, `subRowChildren`, `subRowCondition`, `footerCells`, `footerCardChildren` | row 컨텍스트로 평가 필요 |
|
||||
| CardGrid | `cardColumns` | item 컨텍스트로 평가 필요 |
|
||||
|
||||
### 관련 파일
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ HTML 태그를 래핑하는 최소 단위 컴포넌트입니다. 모든 Basic
|
||||
| 컴포넌트 | 설명 | 주요 Props |
|
||||
|----------|------|-----------|
|
||||
| `DataGrid` | 정렬/필터링/페이지네이션 데이터 그리드 | columns, data, sortable, pagination, pageSize, ... |
|
||||
| `CardGrid` | 카드 그리드 레이아웃 (스켈레톤 로딩, 페이지네이션) | data, cardChildren, columns, gap, responsiveColumns, ... |
|
||||
| `CardGrid` | 카드 그리드 레이아웃 (스켈레톤 로딩, 페이지네이션) | data, cardColumns, columns, gap, responsiveColumns, ... |
|
||||
| `Pagination` | 페이지네이션 | currentPage, totalPages, onPageChange, maxVisiblePages, showFirstLast, ... |
|
||||
| `Badge` | 색상 기반 라벨 뱃지 | color, text, size, style |
|
||||
| `StatusBadge` | 상태 뱃지 (아이콘 포함) | status, label, showIcon, iconName, style |
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- 모바일·태블릿 화면에서 쿠폰 목록의 카드 보기가 내용 없이 나오던 문제를 수정했습니다. 목록의 각 쿠폰 정보가 카드에 전달되지 않아, 쿠폰이 있어도 빈 카드만 표시됐습니다.
|
||||
- 주문 검색 프리셋 관리 창에서 "프리셋 추가" 버튼을 눌러도 저장 창이 열리지 않던 문제를 수정했습니다.
|
||||
- 상품 리뷰 목록의 작성자 이름과 쿠폰 목록의 쿠폰 이름을 키보드로 선택해 이동할 수 있게 했습니다. 이전에는 마우스 클릭으로만 이동할 수 있었습니다.
|
||||
- 주문 검색 프리셋 관리 창의 수정·삭제 아이콘이 의도한 크기보다 작게 보이던 문제를 수정했습니다.
|
||||
- 할인코드 목록에서 선택 개수·전체 건수·페이지당 표시 개수 문구에 숫자가 채워지지 않고 자리표시자가 그대로 보이던 문제를 수정했습니다.
|
||||
- 미입금 자동취소 기한을 비운 채 저장하면 "저장되었습니다"라고 안내되면서도 값은 사라지던 문제를 수정했습니다. 숫자 항목에 숫자가 아닌 값을 입력하면 입력칸이 비워지는데, 그대로 저장하면 설정이 없는 상태로 남아 화면에 보이는 값과 실제 동작이 어긋났습니다. 이제 비워 둔 채로는 저장되지 않고 값을 입력하라고 안내합니다.
|
||||
- 미입금 자동취소 기한 입력 안내가 실제 입력 하한과 다른 "0일 이상"으로 표시되던 문구를 "1일 이상"으로 바로잡았습니다.
|
||||
- 쿠폰 수정 화면에서 유효기간을 "발급일로부터"로 두고 일수를 비운 채 저장할 수 있던 문제를 수정했습니다. 그렇게 저장된 쿠폰은 발급되는 즉시 만료 상태가 되어, 받은 회원이 사용할 수 없으면서도 아무 오류가 나지 않았습니다. 이제 저장 단계에서 일수를 입력하도록 안내하고, 이미 그렇게 저장돼 있던 쿠폰은 발급 시 유효기간이 설정되지 않았다고 알려 줍니다.
|
||||
|
||||
+2
-1
@@ -1434,7 +1434,7 @@
|
||||
"name": "Button",
|
||||
"props": {
|
||||
"type": "button",
|
||||
"className": "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
||||
"className": "px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
||||
"disabled": "{{!_local.batchAction || (_local.selectedReviews ?? []).length === 0}}"
|
||||
},
|
||||
"text": "$t:sirsoft-ecommerce.admin.product_review.batch.apply",
|
||||
@@ -1718,6 +1718,7 @@
|
||||
"name": "A",
|
||||
"if": "{{!!row.user_id}}",
|
||||
"props": {
|
||||
"href": "#",
|
||||
"className": "text-blue-600 dark:text-blue-400 hover:underline cursor-pointer font-medium"
|
||||
},
|
||||
"text": "{{row.user?.name ?? ''}}",
|
||||
|
||||
+24
-24
@@ -67,7 +67,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "percent",
|
||||
"className": "w-6 h-6 text-gray-700 dark:text-gray-300"
|
||||
"className": "text-2xl text-gray-700 dark:text-gray-300"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -107,7 +107,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "plus",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -214,7 +214,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "search",
|
||||
"className": "w-4 h-4 absolute right-3 top-1/2 -translate-y-1/2 icon-muted pointer-events-none"
|
||||
"className": "text-base absolute right-3 top-1/2 -translate-y-1/2 icon-muted pointer-events-none"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -239,7 +239,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "settings-2",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -270,7 +270,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "filter",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -776,7 +776,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "calendar",
|
||||
"className": "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 icon-muted pointer-events-none"
|
||||
"className": "text-base absolute left-3 top-1/2 -translate-y-1/2 icon-muted pointer-events-none"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -821,7 +821,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "calendar",
|
||||
"className": "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 icon-muted pointer-events-none"
|
||||
"className": "text-base absolute left-3 top-1/2 -translate-y-1/2 icon-muted pointer-events-none"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -938,7 +938,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "search",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -961,7 +961,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "refresh-cw",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1010,7 +1010,7 @@
|
||||
"props": {
|
||||
"className": "text-sm text-secondary"
|
||||
},
|
||||
"text": "0$t:sirsoft-ecommerce.admin.promotion_discount_code.batch.selected_count"
|
||||
"text": "$t:sirsoft-ecommerce.admin.promotion_discount_code.batch.selected_count|count=0"
|
||||
},
|
||||
{
|
||||
"type": "composite",
|
||||
@@ -1088,7 +1088,7 @@
|
||||
"props": {
|
||||
"className": "text-sm text-secondary"
|
||||
},
|
||||
"text": "$t:sirsoft-ecommerce.admin.promotion_discount_code.table.total_count"
|
||||
"text": "$t:sirsoft-ecommerce.admin.promotion_discount_code.table.total_count|count=2"
|
||||
},
|
||||
{
|
||||
"type": "basic",
|
||||
@@ -1144,15 +1144,15 @@
|
||||
"options": [
|
||||
{
|
||||
"value": "10",
|
||||
"label": "10$t:sirsoft-ecommerce.admin.promotion_discount_code.table.per_page"
|
||||
"label": "$t:sirsoft-ecommerce.admin.promotion_discount_code.table.per_page|count=10"
|
||||
},
|
||||
{
|
||||
"value": "20",
|
||||
"label": "20$t:sirsoft-ecommerce.admin.promotion_discount_code.table.per_page"
|
||||
"label": "$t:sirsoft-ecommerce.admin.promotion_discount_code.table.per_page|count=20"
|
||||
},
|
||||
{
|
||||
"value": "50",
|
||||
"label": "50$t:sirsoft-ecommerce.admin.promotion_discount_code.table.per_page"
|
||||
"label": "$t:sirsoft-ecommerce.admin.promotion_discount_code.table.per_page|count=50"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1238,7 +1238,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "more-horizontal",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1386,7 +1386,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "eye",
|
||||
"className": "w-3 h-3"
|
||||
"className": "text-xs"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1481,7 +1481,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "more-horizontal",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1629,7 +1629,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "eye",
|
||||
"className": "w-3 h-3"
|
||||
"className": "text-xs"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2016,7 +2016,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "more-horizontal",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2209,7 +2209,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "more-horizontal",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2253,7 +2253,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "chevrons-left",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2272,7 +2272,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "chevron-left",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2317,7 +2317,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "chevron-right",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2335,7 +2335,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "chevrons-right",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+5
-7
@@ -58,7 +58,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "edit",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -76,7 +76,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "trash-2",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -120,7 +120,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "edit",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -138,7 +138,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "trash-2",
|
||||
"className": "w-4 h-4"
|
||||
"className": "text-base"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -165,9 +165,7 @@
|
||||
{
|
||||
"type": "click",
|
||||
"handler": "openModal",
|
||||
"params": {
|
||||
"modalId": "modal_preset_save"
|
||||
}
|
||||
"target": "modal_preset_save"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+3
-2
@@ -73,7 +73,7 @@
|
||||
"name": "Button",
|
||||
"props": {
|
||||
"type": "button",
|
||||
"className": "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium transition-colors disabled:opacity-50",
|
||||
"className": "px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 font-medium transition-colors disabled:opacity-50",
|
||||
"disabled": "{{!(_local.selectedItems || []).length || !_local.bulkIssueStatus || coupons?.data?.abilities?.can_update !== true}}"
|
||||
},
|
||||
"text": "$t:sirsoft-ecommerce.admin.promotion_coupon.bulk.apply",
|
||||
@@ -303,6 +303,7 @@
|
||||
"type": "basic",
|
||||
"name": "A",
|
||||
"props": {
|
||||
"href": "#",
|
||||
"className": "text-blue-600 dark:text-blue-400 hover:underline cursor-pointer font-medium"
|
||||
},
|
||||
"text": "{{row.localized_name}}",
|
||||
@@ -708,7 +709,7 @@
|
||||
{
|
||||
"iteration": {
|
||||
"source": "coupons?.data?.data || []",
|
||||
"item": "coupon",
|
||||
"item_var": "coupon",
|
||||
"key": "coupon.id"
|
||||
},
|
||||
"children": [
|
||||
|
||||
+6
-6
@@ -1,14 +1,14 @@
|
||||
var iM=Object.defineProperty;var sM=(dn,i,u)=>i in dn?iM(dn,i,{enumerable:!0,configurable:!0,writable:!0,value:u}):dn[i]=u;var Ti=(dn,i,u)=>sM(dn,typeof i!="symbol"?i+"":i,u);(function(dn,i,u,Cn){"use strict";function Lt(){const e=typeof window<"u"?window.G7Core?.__runtime:void 0;if(!e)throw new Error("[layout-editor] window.G7Core.__runtime 이 없습니다. 코어 번들(template-engine.min.js)이 편집기 번들보다 먼저 로드되어야 합니다.");return e}const Di=Lt();Di.TranslationProvider;const tt=Di.useTranslation;Di.TranslationReactContext;function rt(){return typeof window>"u"?null:window.__g7Devtools??null}function Sl(e,t){const n=rt();if(!n?.trackEditorState)return;const o={templateIdentifier:e.templateIdentifier,editMode:e.editMode,selectedRoutePath:e.selectedRoute?.path??null,selectedLayoutName:e.selectedRoute?.layoutName??null,routeTreeSize:e.routeTree.length,isRouteTreeCollapsed:e.isRouteTreeCollapsed,locale:e.locale,previewDevice:e.previewDevice,previewZoom:e.previewZoom,lastAction:t,timestamp:Date.now()};try{n.trackEditorState(o)}catch{}}function Hh(){const e=rt();if(e?.clearEditorStateData)try{e.clearEditorStateData()}catch{}}function Fh(e){const t=rt();if(t?.trackEditorSampleMatch)try{t.trackEditorSampleMatch(e)}catch{}}function Kh(){const e=rt();if(e?.clearEditorSampleData)try{e.clearEditorSampleData()}catch{}}function Vh(e){const t=rt();if(t?.trackEditorHistoryEntry)try{t.trackEditorHistoryEntry(e)}catch{}}function Uh(){const e=rt();if(e?.clearEditorHistoryData)try{e.clearEditorHistoryData()}catch{}}function Cl(e){const t=rt();if(t?.trackEditorSelection)try{t.trackEditorSelection(e)}catch{}}function qh(){const e=rt();if(e?.clearEditorSelectionData)try{e.clearEditorSelectionData()}catch{}}function ir(e){const t=rt();if(t?.trackEditorPropertyPatch)try{t.trackEditorPropertyPatch(e)}catch{}}function $n(e){const t=rt();if(t?.trackEditorDnd)try{t.trackEditorDnd(e)}catch{}}function Gh(){const e=rt();if(e?.clearEditorDndData)try{e.clearEditorDndData()}catch{}}function st(e){const t=rt();if(t?.trackEditorDocument)try{t.trackEditorDocument(e)}catch{}}function Yh(){const e=rt();if(e?.clearEditorDocumentData)try{e.clearEditorDocumentData()}catch{}}function Xh(e){const t=rt();if(t?.trackEditorSpecMerge)try{t.trackEditorSpecMerge(e)}catch{}}function Jh(){const e=rt();if(e?.clearEditorSpecMergeData)try{e.clearEditorSpecMergeData()}catch{}}function Ni(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)>>>0;return t}function Et(e){const t=rt();if(t?.trackEditorI18n)try{t.trackEditorI18n(e)}catch{}}function Zh(){const e=rt();if(e?.clearEditorI18nData)try{e.clearEditorI18nData()}catch{}}function $l(e){const t=rt();if(t?.trackPageState)try{t.trackPageState(e)}catch{}}function Qh(){const e=rt();if(e?.clearPageStateData)try{e.clearPageStateData()}catch{}}const El="g7le.routeTree.collapsed";function Rh(){if(typeof window>"u")return!1;try{return window.localStorage?.getItem(El)==="1"}catch{return!1}}function ey(e){if(!(typeof window>"u"))try{window.localStorage?.setItem(El,e?"1":"0")}catch{}}function ty(e,t){return{templateIdentifier:e,routeTree:[],routesError:null,selectedRoute:null,isRouteTreeCollapsed:Rh(),editMode:"route",returnRoute:null,returnEditMode:null,locale:t,previewDevice:"desktop",previewZoom:1,previewCustomWidth:1024,previewColorScheme:"light",history:{canUndo:!1,canRedo:!1},isPaletteOpen:!1,availableStates:[],activeStateId:null,layoutVersions:{},extensionVersions:{}}}function ny(e,t){switch(t.type){case"SET_ROUTE_TREE":return{...e,routeTree:t.tree,routesError:null};case"SET_ROUTES_ERROR":return{...e,routesError:t.error};case"SELECT_ROUTE":return{...e,editMode:"route",selectedRoute:t.route,returnRoute:null,returnEditMode:null};case"TOGGLE_ROUTE_TREE":return{...e,isRouteTreeCollapsed:!e.isRouteTreeCollapsed};case"SET_LOCALE":return{...e,locale:t.locale};case"ENTER_BASE_EDIT":return{...e,editMode:"base",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,selectedRoute:{path:`__base__/${t.layoutName}`,layoutName:t.layoutName}};case"EXIT_BASE_EDIT":return{...e,editMode:"route",selectedRoute:e.returnRoute,returnRoute:null};case"ENTER_MODAL_EDIT":return{...e,editMode:"modal",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,selectedRoute:{path:`__modal__/${t.modalId}`,layoutName:t.hostLayout}};case"EXIT_MODAL_EDIT":return{...e,editMode:"route",selectedRoute:e.returnRoute,returnRoute:null};case"ENTER_EXTENSION_EDIT":return{...e,editMode:"extension",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,selectedRoute:{path:`__extension__/${t.extensionId}`,layoutName:t.extensionHost??null}};case"SET_RETURN_ROUTE":return e.editMode==="route"||e.returnRoute!==null?e:{...e,returnRoute:t.route};case"EXIT_EXTENSION_EDIT":return{...e,editMode:"route",selectedRoute:e.returnRoute,returnRoute:null};case"ENTER_ITERATION_ITEM_EDIT":return{...e,editMode:"iteration_item",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,returnEditMode:e.ediLine truncated
|
||||
var sM=Object.defineProperty;var aM=(dn,i,u)=>i in dn?sM(dn,i,{enumerable:!0,configurable:!0,writable:!0,value:u}):dn[i]=u;var Ti=(dn,i,u)=>aM(dn,typeof i!="symbol"?i+"":i,u);(function(dn,i,u,Cn){"use strict";function Lt(){const e=typeof window<"u"?window.G7Core?.__runtime:void 0;if(!e)throw new Error("[layout-editor] window.G7Core.__runtime 이 없습니다. 코어 번들(template-engine.min.js)이 편집기 번들보다 먼저 로드되어야 합니다.");return e}const Di=Lt();Di.TranslationProvider;const tt=Di.useTranslation;Di.TranslationReactContext;function rt(){return typeof window>"u"?null:window.__g7Devtools??null}function Sl(e,t){const n=rt();if(!n?.trackEditorState)return;const o={templateIdentifier:e.templateIdentifier,editMode:e.editMode,selectedRoutePath:e.selectedRoute?.path??null,selectedLayoutName:e.selectedRoute?.layoutName??null,routeTreeSize:e.routeTree.length,isRouteTreeCollapsed:e.isRouteTreeCollapsed,locale:e.locale,previewDevice:e.previewDevice,previewZoom:e.previewZoom,lastAction:t,timestamp:Date.now()};try{n.trackEditorState(o)}catch{}}function Fh(){const e=rt();if(e?.clearEditorStateData)try{e.clearEditorStateData()}catch{}}function Kh(e){const t=rt();if(t?.trackEditorSampleMatch)try{t.trackEditorSampleMatch(e)}catch{}}function Vh(){const e=rt();if(e?.clearEditorSampleData)try{e.clearEditorSampleData()}catch{}}function Uh(e){const t=rt();if(t?.trackEditorHistoryEntry)try{t.trackEditorHistoryEntry(e)}catch{}}function qh(){const e=rt();if(e?.clearEditorHistoryData)try{e.clearEditorHistoryData()}catch{}}function Cl(e){const t=rt();if(t?.trackEditorSelection)try{t.trackEditorSelection(e)}catch{}}function Gh(){const e=rt();if(e?.clearEditorSelectionData)try{e.clearEditorSelectionData()}catch{}}function sr(e){const t=rt();if(t?.trackEditorPropertyPatch)try{t.trackEditorPropertyPatch(e)}catch{}}function $n(e){const t=rt();if(t?.trackEditorDnd)try{t.trackEditorDnd(e)}catch{}}function Yh(){const e=rt();if(e?.clearEditorDndData)try{e.clearEditorDndData()}catch{}}function st(e){const t=rt();if(t?.trackEditorDocument)try{t.trackEditorDocument(e)}catch{}}function Xh(){const e=rt();if(e?.clearEditorDocumentData)try{e.clearEditorDocumentData()}catch{}}function Jh(e){const t=rt();if(t?.trackEditorSpecMerge)try{t.trackEditorSpecMerge(e)}catch{}}function Zh(){const e=rt();if(e?.clearEditorSpecMergeData)try{e.clearEditorSpecMergeData()}catch{}}function Ni(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)>>>0;return t}function Et(e){const t=rt();if(t?.trackEditorI18n)try{t.trackEditorI18n(e)}catch{}}function Qh(){const e=rt();if(e?.clearEditorI18nData)try{e.clearEditorI18nData()}catch{}}function $l(e){const t=rt();if(t?.trackPageState)try{t.trackPageState(e)}catch{}}function Rh(){const e=rt();if(e?.clearPageStateData)try{e.clearPageStateData()}catch{}}const El="g7le.routeTree.collapsed";function ey(){if(typeof window>"u")return!1;try{return window.localStorage?.getItem(El)==="1"}catch{return!1}}function ty(e){if(!(typeof window>"u"))try{window.localStorage?.setItem(El,e?"1":"0")}catch{}}function ny(e,t){return{templateIdentifier:e,routeTree:[],routesError:null,selectedRoute:null,isRouteTreeCollapsed:ey(),editMode:"route",returnRoute:null,returnEditMode:null,locale:t,previewDevice:"desktop",previewZoom:1,previewCustomWidth:1024,previewColorScheme:"light",history:{canUndo:!1,canRedo:!1},isPaletteOpen:!1,availableStates:[],activeStateId:null,layoutVersions:{},extensionVersions:{}}}function oy(e,t){switch(t.type){case"SET_ROUTE_TREE":return{...e,routeTree:t.tree,routesError:null};case"SET_ROUTES_ERROR":return{...e,routesError:t.error};case"SELECT_ROUTE":return{...e,editMode:"route",selectedRoute:t.route,returnRoute:null,returnEditMode:null};case"TOGGLE_ROUTE_TREE":return{...e,isRouteTreeCollapsed:!e.isRouteTreeCollapsed};case"SET_LOCALE":return{...e,locale:t.locale};case"ENTER_BASE_EDIT":return{...e,editMode:"base",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,selectedRoute:{path:`__base__/${t.layoutName}`,layoutName:t.layoutName}};case"EXIT_BASE_EDIT":return{...e,editMode:"route",selectedRoute:e.returnRoute,returnRoute:null};case"ENTER_MODAL_EDIT":return{...e,editMode:"modal",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,selectedRoute:{path:`__modal__/${t.modalId}`,layoutName:t.hostLayout}};case"EXIT_MODAL_EDIT":return{...e,editMode:"route",selectedRoute:e.returnRoute,returnRoute:null};case"ENTER_EXTENSION_EDIT":return{...e,editMode:"extension",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,selectedRoute:{path:`__extension__/${t.extensionId}`,layoutName:t.extensionHost??null}};case"SET_RETURN_ROUTE":return e.editMode==="route"||e.returnRoute!==null?e:{...e,returnRoute:t.route};case"EXIT_EXTENSION_EDIT":return{...e,editMode:"route",selectedRoute:e.returnRoute,returnRoute:null};case"ENTER_ITERATION_ITEM_EDIT":return{...e,editMode:"iteration_item",returnRoute:t.returnRoute!==void 0?t.returnRoute:e.selectedRoute,returnEditMode:e.ediLine truncated
|
||||
"keyword": "",
|
||||
"page": 1
|
||||
}`,shapeErrorKey:"layout_editor.json_block.must_be_object",validate:_=>{const S=Object.keys(_??{}).filter(E=>!Is(E));return S.length?a("layout_editor.json_block.invalid_keys",{keys:S.join(", ")}):null}}):i.jsxs(i.Fragment,{children:[v.length===0?i.jsx("p",{"data-testid":`g7le-initstate-${e}-empty`,style:NS,children:a("layout_editor.initstate.no_values")}):v.map(_=>{const E=Nw(b,r,_)==="inherited",C=e==="isolated"&&l[_];return i.jsx(pS,{scope:e,itemKey:_,value:o[_],inherited:E,isOrphan:!!C,onChangeValue:N=>x(_,N),onRemove:()=>k(_),t:a},_)}),i.jsxs("div",{style:$S,children:[i.jsx("input",{type:"text","data-testid":`g7le-initstate-${e}-add-name`,value:d,placeholder:a("layout_editor.initstate.value_name"),onChange:_=>{c(_.target.value),y&&g(null)},onKeyDown:_=>{_.key==="Enter"&&(_.preventDefault(),w())},"aria-invalid":y?!0:void 0,style:{...ES,...y?IS:null}}),i.jsx("select",{"data-testid":`g7le-initstate-${e}-add-kind`,value:p,onChange:_=>f(_.target.value),"aria-label":a("layout_editor.initstate.value_kind"),style:TS,children:cS.map(_=>i.jsx("option",{value:_.kind,children:a(_.labelKey)},_.kind))}),i.jsxs("button",{type:"button","data-testid":`g7le-initstate-${e}-add`,onClick:w,style:DS,children:["+ ",a("layout_editor.initstate.add_value")]})]}),y?i.jsx("p",{"data-testid":`g7le-initstate-${e}-add-error`,style:AS,children:y}):null]})]})}function pS({scope:e,itemKey:t,value:n,inherited:o,isOrphan:r,onChangeValue:s,onRemove:a,t:l}){const[d,c]=u.useState(!1);let p;try{p=JSON.stringify(n,null,2)??String(n)}catch{p=String(n)}return i.jsxs("div",{"data-testid":`g7le-initstate-${e}-item-${t}`,style:xS,children:[i.jsxs("div",{style:wS,children:[o?i.jsx("span",{"data-testid":`g7le-initstate-inherited-${t}`,style:kS,children:"🔗"}):null,i.jsx("code",{style:SS,children:t}),i.jsx("span",{style:{flex:1}}),i.jsx("button",{type:"button","data-testid":`g7le-initstate-code-${t}`,onClick:()=>c(f=>!f),style:Hs,"aria-label":l("layout_editor.value_tree.show_source"),title:l("layout_editor.value_tree.show_source"),children:"</>"}),o?i.jsx("button",{type:"button","data-testid":`g7le-initstate-revert-${t}`,onClick:a,style:Hs,children:l("layout_editor.initstate.revert")}):i.jsx("button",{type:"button","data-testid":`g7le-initstate-remove-${t}`,onClick:a,style:Hs,"aria-label":l("layout_editor.init_actions.remove"),children:"✕"})]}),d?i.jsx("pre",{"data-testid":`g7le-initstate-code-block-${t}`,style:MS,children:p}):null,i.jsx(Nn,{value:n,onChange:s,t:l,path:t,scope:e}),r?i.jsxs("p",{"data-testid":`g7le-initstate-isolated-orphan-${t}`,style:CS,children:["⚠ ",l("layout_editor.initstate.isolated_orphan")]}):null]})}const fS={display:"flex",flexDirection:"column",gap:14,minWidth:0},gS={margin:0,fontSize:13,fontWeight:700,color:"#0f172a"},hS={margin:0,fontSize:11,color:"#1d4ed8",background:"#eff6ff",padding:"6px 8px",borderRadius:6},yS={display:"flex",flexDirection:"column",gap:8,padding:10,border:"1px solid #e2e8f0",borderRadius:10,minWidth:0},bS={display:"flex",alignItems:"center",justifyContent:"space-between",gap:8},mS={fontSize:12,fontWeight:700,color:"#475569"},vS={margin:0,fontSize:11,color:"#94a3b8"},_S={padding:"2px 8px",fontSize:11,border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",color:"#475569",cursor:"pointer"},xS={display:"flex",flexDirection:"column",gap:4,padding:"6px 8px",border:"1px solid #f1f5f9",borderRadius:6,minWidth:0},wS={display:"flex",alignItems:"center",gap:6},kS={fontSize:12},SS={fontSize:12,color:"#0f172a",fontFamily:"monospace"},Hs={border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",color:"#475569",cursor:"pointer",fontSize:11,padding:"2px 6px"},CS={margin:0,fontSize:11,color:"#b45309",background:"#fef3c7",padding:"4px 6px",borderRadius:6},$S={display:"flex",gap:6,alignItems:"center"},ES={padding:"4px 8px",fontSize:12,border:"1px solid #cbd5e1",borderRadius:6},IS={border:"1px solid #dc2626"},AS={margin:0,fontSize:11,color:"#dc2626"},TS={padding:"4px 8px",fontSize:12,border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",color:"#0f172a"},DS={padding:"2px 10px",fontSize:11,border:"1px dashed #94a3b8",borderRadius:6,background:"#fff",color:"#475569",cursor:"pointer"},NS={margin:0,fontSize:12,color:"#94a3b8"},zS={margin:0,fontSize:11,color:"#94a3b8"},MS={margin:"2px 0",padding:"6px 8px",fontSize:11,fontFamily:"monospace",color:"#0f172a",background:"#f1f5f9",border:"1px solid #e2e8f0",borderRadius:6,whiteSpace:"pre-wrap",wordBreak:"break-all",overflowX:"auto"},LS={self:"layout_editor.error_rows.source_self",inherited:"layout_editor.error_rows.source_inherited",template:"layout_editor.error_rows.source_template",none:"layout_editor.error_rows.source_none"};function Fs({value:e,onChange:t,t:n,codes:o,mode:r="local",sourceOf:s,renderActionList:a,testIdPrefix:l="g7le-error-rows"}){const d=o??Object.keys(e),c=u.useCallback((f,y)=>{t({...e,[f]:y})},[e,t]),p=u.useCallback(f=>{const y={...e};delete y[f],t(y)},[e,t]);return i.jsx("div",{className:l,"data-testid":l,children:d.map(f=>{const y=e[f],g=
Line truncated
|
||||
}`,shapeErrorKey:"layout_editor.json_block.must_be_object",validate:_=>{const S=Object.keys(_??{}).filter(E=>!Is(E));return S.length?a("layout_editor.json_block.invalid_keys",{keys:S.join(", ")}):null}}):i.jsxs(i.Fragment,{children:[v.length===0?i.jsx("p",{"data-testid":`g7le-initstate-${e}-empty`,style:zS,children:a("layout_editor.initstate.no_values")}):v.map(_=>{const E=zw(b,r,_)==="inherited",C=e==="isolated"&&l[_];return i.jsx(fS,{scope:e,itemKey:_,value:o[_],inherited:E,isOrphan:!!C,onChangeValue:N=>x(_,N),onRemove:()=>k(_),t:a},_)}),i.jsxs("div",{style:ES,children:[i.jsx("input",{type:"text","data-testid":`g7le-initstate-${e}-add-name`,value:d,placeholder:a("layout_editor.initstate.value_name"),onChange:_=>{c(_.target.value),y&&g(null)},onKeyDown:_=>{_.key==="Enter"&&(_.preventDefault(),w())},"aria-invalid":y?!0:void 0,style:{...IS,...y?AS:null}}),i.jsx("select",{"data-testid":`g7le-initstate-${e}-add-kind`,value:p,onChange:_=>f(_.target.value),"aria-label":a("layout_editor.initstate.value_kind"),style:DS,children:uS.map(_=>i.jsx("option",{value:_.kind,children:a(_.labelKey)},_.kind))}),i.jsxs("button",{type:"button","data-testid":`g7le-initstate-${e}-add`,onClick:w,style:NS,children:["+ ",a("layout_editor.initstate.add_value")]})]}),y?i.jsx("p",{"data-testid":`g7le-initstate-${e}-add-error`,style:TS,children:y}):null]})]})}function fS({scope:e,itemKey:t,value:n,inherited:o,isOrphan:r,onChangeValue:s,onRemove:a,t:l}){const[d,c]=u.useState(!1);let p;try{p=JSON.stringify(n,null,2)??String(n)}catch{p=String(n)}return i.jsxs("div",{"data-testid":`g7le-initstate-${e}-item-${t}`,style:wS,children:[i.jsxs("div",{style:kS,children:[o?i.jsx("span",{"data-testid":`g7le-initstate-inherited-${t}`,style:SS,children:"🔗"}):null,i.jsx("code",{style:CS,children:t}),i.jsx("span",{style:{flex:1}}),i.jsx("button",{type:"button","data-testid":`g7le-initstate-code-${t}`,onClick:()=>c(f=>!f),style:Hs,"aria-label":l("layout_editor.value_tree.show_source"),title:l("layout_editor.value_tree.show_source"),children:"</>"}),o?i.jsx("button",{type:"button","data-testid":`g7le-initstate-revert-${t}`,onClick:a,style:Hs,children:l("layout_editor.initstate.revert")}):i.jsx("button",{type:"button","data-testid":`g7le-initstate-remove-${t}`,onClick:a,style:Hs,"aria-label":l("layout_editor.init_actions.remove"),children:"✕"})]}),d?i.jsx("pre",{"data-testid":`g7le-initstate-code-block-${t}`,style:LS,children:p}):null,i.jsx(Nn,{value:n,onChange:s,t:l,path:t,scope:e}),r?i.jsxs("p",{"data-testid":`g7le-initstate-isolated-orphan-${t}`,style:$S,children:["⚠ ",l("layout_editor.initstate.isolated_orphan")]}):null]})}const gS={display:"flex",flexDirection:"column",gap:14,minWidth:0},hS={margin:0,fontSize:13,fontWeight:700,color:"#0f172a"},yS={margin:0,fontSize:11,color:"#1d4ed8",background:"#eff6ff",padding:"6px 8px",borderRadius:6},bS={display:"flex",flexDirection:"column",gap:8,padding:10,border:"1px solid #e2e8f0",borderRadius:10,minWidth:0},mS={display:"flex",alignItems:"center",justifyContent:"space-between",gap:8},vS={fontSize:12,fontWeight:700,color:"#475569"},_S={margin:0,fontSize:11,color:"#94a3b8"},xS={padding:"2px 8px",fontSize:11,border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",color:"#475569",cursor:"pointer"},wS={display:"flex",flexDirection:"column",gap:4,padding:"6px 8px",border:"1px solid #f1f5f9",borderRadius:6,minWidth:0},kS={display:"flex",alignItems:"center",gap:6},SS={fontSize:12},CS={fontSize:12,color:"#0f172a",fontFamily:"monospace"},Hs={border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",color:"#475569",cursor:"pointer",fontSize:11,padding:"2px 6px"},$S={margin:0,fontSize:11,color:"#b45309",background:"#fef3c7",padding:"4px 6px",borderRadius:6},ES={display:"flex",gap:6,alignItems:"center"},IS={padding:"4px 8px",fontSize:12,border:"1px solid #cbd5e1",borderRadius:6},AS={border:"1px solid #dc2626"},TS={margin:0,fontSize:11,color:"#dc2626"},DS={padding:"4px 8px",fontSize:12,border:"1px solid #cbd5e1",borderRadius:6,background:"#fff",color:"#0f172a"},NS={padding:"2px 10px",fontSize:11,border:"1px dashed #94a3b8",borderRadius:6,background:"#fff",color:"#475569",cursor:"pointer"},zS={margin:0,fontSize:12,color:"#94a3b8"},MS={margin:0,fontSize:11,color:"#94a3b8"},LS={margin:"2px 0",padding:"6px 8px",fontSize:11,fontFamily:"monospace",color:"#0f172a",background:"#f1f5f9",border:"1px solid #e2e8f0",borderRadius:6,whiteSpace:"pre-wrap",wordBreak:"break-all",overflowX:"auto"},PS={self:"layout_editor.error_rows.source_self",inherited:"layout_editor.error_rows.source_inherited",template:"layout_editor.error_rows.source_template",none:"layout_editor.error_rows.source_none"};function Fs({value:e,onChange:t,t:n,codes:o,mode:r="local",sourceOf:s,renderActionList:a,testIdPrefix:l="g7le-error-rows"}){const d=o??Object.keys(e),c=u.useCallback((f,y)=>{t({...e,[f]:y})},[e,t]),p=u.useCallback(f=>{const y={...e};delete y[f],t(y)},[e,t]);return i.jsx("div",{className:l,"data-testid":l,children:d.map(f=>{const y=e[f],g=
Line truncated
|
||||
"page": "{{query.page ?? 1}}"
|
||||
}`,shapeErrorKey:"layout_editor.data_sources.error.params_object",invalidErrorKey:"layout_editor.data_sources.error.params_json",t:n,onChange:I=>x(P=>P&&{...P,paramsRawValue:I??{},paramsRawValid:!0}),onValidityChange:I=>x(P=>P&&{...P,paramsRawValid:I})}):i.jsxs("div",{"data-testid":"g7le-data-sources-field-params",children:[b.paramRows.length===0&&i.jsx("p",{"data-testid":"g7le-ds-params-empty",style:VC,children:n("layout_editor.data_sources.field.params_empty")}),i.jsx(zr,{value:b.paramRows.map(I=>({key:I.key,value:I.value})),onChange:I=>x({...b,paramRows:I.map(P=>({key:P.key??"",value:P.value??""}))}),keyField:"key",valueField:"value",t:n,candidates:d,testidPrefix:"g7le-ds-params-kv",keyPlaceholder:n("layout_editor.data_sources.field.param_key_placeholder"),valuePlaceholder:"{{query.page ?? 1}}",addLabel:n("layout_editor.data_sources.field.param_add")}),(()=>{const I=Ys(b.paramRows);return Object.keys(I).length===0?null:i.jsxs("div",{style:Nu,children:[i.jsxs("button",{type:"button","data-testid":"g7le-ds-params-preview-toggle","aria-expanded":_,onClick:()=>S(P=>!P),style:zu,children:[_?"▴":"▾"," ",n("layout_editor.data_sources.field.params_preview")]}),_&&i.jsx("pre",{"data-testid":"g7le-ds-params-preview",style:{...Mu,marginTop:4},children:JSON.stringify(I,null,2)})]})})()]}),i.jsx("div",{style:{fontSize:11,color:"#94a3b8",marginTop:4},children:n("layout_editor.data_sources.field.params_hint")})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.fallback")}),b.fallbackEnabled?i.jsxs("div",{"data-testid":"g7le-data-sources-field-fallback",style:{display:"flex",flexDirection:"column",gap:6},children:[i.jsx(Nn,{value:b.fallbackValue===void 0?{}:b.fallbackValue,onChange:I=>x({...b,fallbackValue:I}),t:n,path:"fallback",scope:"ds-fallback",renderStringLeaf:I=>i.jsx(Ve,{value:I.value,onChange:I.onChange,t:n,candidates:d,testidPrefix:I.testidPrefix})}),(()=>{const I=b.fallbackValue===void 0?{}:b.fallbackValue;return i.jsxs("div",{style:Nu,children:[i.jsxs("button",{type:"button","data-testid":"g7le-ds-fallback-preview-toggle","aria-expanded":E,onClick:()=>C(P=>!P),style:zu,children:[E?"▴":"▾"," ",n("layout_editor.data_sources.field.fallback_preview")]}),E&&i.jsx("pre",{"data-testid":"g7le-ds-fallback-preview",style:{...Mu,marginTop:4},children:JSON.stringify(I,null,2)})]})})(),i.jsx("button",{type:"button","data-testid":"g7le-data-sources-field-fallback-clear",onClick:()=>x({...b,fallbackEnabled:!1,fallbackValue:void 0}),style:{...rn,padding:"2px 8px",fontSize:11,alignSelf:"flex-start",color:"#dc2626",borderColor:"#fecaca"},children:n("layout_editor.data_sources.field.fallback_clear")})]}):i.jsxs("button",{type:"button","data-testid":"g7le-data-sources-field-fallback-add",onClick:()=>x({...b,fallbackEnabled:!0,fallbackValue:{}}),style:{...rn,padding:"4px 10px",display:"block"},children:["+ ",n("layout_editor.data_sources.field.fallback_add")]})]}),b.type==="websocket"&&i.jsxs("div",{"data-testid":"g7le-ds-ws-section",style:No,children:[i.jsx("div",{style:zo,children:n("layout_editor.data_sources.section.websocket")}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.channel")}),i.jsx("input",{"data-testid":"g7le-ds-ws-channel",style:jt,value:b.channel,onChange:I=>x({...b,channel:I.target.value}),placeholder:"core.admin.dashboard"})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.event")}),i.jsx("input",{"data-testid":"g7le-ds-ws-event",style:jt,value:b.event,onChange:I=>x({...b,event:I.target.value}),placeholder:"dashboard.stats.updated"})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.channel_type")}),i.jsx("select",{"data-testid":"g7le-ds-ws-channeltype",style:jt,value:b.channel_type,onChange:I=>x({...b,channel_type:I.target.value}),children:WC.map(I=>i.jsx("option",{value:I,children:I},I))})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.target_source")}),i.jsx("input",{"data-testid":"g7le-ds-ws-target",style:jt,value:b.target_source,onChange:I=>x({...b,target_source:I.target.value}),list:"g7le-ds-ws-target-list"}),i.jsx("datalist",{id:"g7le-ds-ws-target-list",children:h.filter(I=>I.id!==b.id).map(I=>typeof I.id=="string"?i.jsx("option",{value:I.id},I.id):null)})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.on_receive")}),i.jsx("div",{"data-testid":"g7le-ds-ws-onreceive",children:i.jsx(Gt,{actions:b.onReceive,onChange:I=>x({...b,onReceive:I}),t:n,recipes:s,candidatePools:z,chipContext:"payload",actionChipCandidates:l?.actionChipCandidates??null,testIdPrefix:"g7le-ds-onreceive-list"})})]})]}),i.jsxs("div",{style:No,children:[i.jsx("div",{style:zo,children:n("layout_editor.data_sources.section.on_success")}),i.jsx("div",{"data-testid":"g7le-ds-onsuccess",children:i.j
Line truncated
|
||||
}`,shapeErrorKey:"layout_editor.data_sources.error.params_object",invalidErrorKey:"layout_editor.data_sources.error.params_json",t:n,onChange:I=>x(P=>P&&{...P,paramsRawValue:I??{},paramsRawValid:!0}),onValidityChange:I=>x(P=>P&&{...P,paramsRawValid:I})}):i.jsxs("div",{"data-testid":"g7le-data-sources-field-params",children:[b.paramRows.length===0&&i.jsx("p",{"data-testid":"g7le-ds-params-empty",style:UC,children:n("layout_editor.data_sources.field.params_empty")}),i.jsx(zr,{value:b.paramRows.map(I=>({key:I.key,value:I.value})),onChange:I=>x({...b,paramRows:I.map(P=>({key:P.key??"",value:P.value??""}))}),keyField:"key",valueField:"value",t:n,candidates:d,testidPrefix:"g7le-ds-params-kv",keyPlaceholder:n("layout_editor.data_sources.field.param_key_placeholder"),valuePlaceholder:"{{query.page ?? 1}}",addLabel:n("layout_editor.data_sources.field.param_add")}),(()=>{const I=Ys(b.paramRows);return Object.keys(I).length===0?null:i.jsxs("div",{style:zu,children:[i.jsxs("button",{type:"button","data-testid":"g7le-ds-params-preview-toggle","aria-expanded":_,onClick:()=>S(P=>!P),style:Mu,children:[_?"▴":"▾"," ",n("layout_editor.data_sources.field.params_preview")]}),_&&i.jsx("pre",{"data-testid":"g7le-ds-params-preview",style:{...Lu,marginTop:4},children:JSON.stringify(I,null,2)})]})})()]}),i.jsx("div",{style:{fontSize:11,color:"#94a3b8",marginTop:4},children:n("layout_editor.data_sources.field.params_hint")})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.fallback")}),b.fallbackEnabled?i.jsxs("div",{"data-testid":"g7le-data-sources-field-fallback",style:{display:"flex",flexDirection:"column",gap:6},children:[i.jsx(Nn,{value:b.fallbackValue===void 0?{}:b.fallbackValue,onChange:I=>x({...b,fallbackValue:I}),t:n,path:"fallback",scope:"ds-fallback",renderStringLeaf:I=>i.jsx(Ve,{value:I.value,onChange:I.onChange,t:n,candidates:d,testidPrefix:I.testidPrefix})}),(()=>{const I=b.fallbackValue===void 0?{}:b.fallbackValue;return i.jsxs("div",{style:zu,children:[i.jsxs("button",{type:"button","data-testid":"g7le-ds-fallback-preview-toggle","aria-expanded":E,onClick:()=>C(P=>!P),style:Mu,children:[E?"▴":"▾"," ",n("layout_editor.data_sources.field.fallback_preview")]}),E&&i.jsx("pre",{"data-testid":"g7le-ds-fallback-preview",style:{...Lu,marginTop:4},children:JSON.stringify(I,null,2)})]})})(),i.jsx("button",{type:"button","data-testid":"g7le-data-sources-field-fallback-clear",onClick:()=>x({...b,fallbackEnabled:!1,fallbackValue:void 0}),style:{...rn,padding:"2px 8px",fontSize:11,alignSelf:"flex-start",color:"#dc2626",borderColor:"#fecaca"},children:n("layout_editor.data_sources.field.fallback_clear")})]}):i.jsxs("button",{type:"button","data-testid":"g7le-data-sources-field-fallback-add",onClick:()=>x({...b,fallbackEnabled:!0,fallbackValue:{}}),style:{...rn,padding:"4px 10px",display:"block"},children:["+ ",n("layout_editor.data_sources.field.fallback_add")]})]}),b.type==="websocket"&&i.jsxs("div",{"data-testid":"g7le-ds-ws-section",style:zo,children:[i.jsx("div",{style:Mo,children:n("layout_editor.data_sources.section.websocket")}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.channel")}),i.jsx("input",{"data-testid":"g7le-ds-ws-channel",style:jt,value:b.channel,onChange:I=>x({...b,channel:I.target.value}),placeholder:"core.admin.dashboard"})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.event")}),i.jsx("input",{"data-testid":"g7le-ds-ws-event",style:jt,value:b.event,onChange:I=>x({...b,event:I.target.value}),placeholder:"dashboard.stats.updated"})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.channel_type")}),i.jsx("select",{"data-testid":"g7le-ds-ws-channeltype",style:jt,value:b.channel_type,onChange:I=>x({...b,channel_type:I.target.value}),children:jC.map(I=>i.jsx("option",{value:I,children:I},I))})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.target_source")}),i.jsx("input",{"data-testid":"g7le-ds-ws-target",style:jt,value:b.target_source,onChange:I=>x({...b,target_source:I.target.value}),list:"g7le-ds-ws-target-list"}),i.jsx("datalist",{id:"g7le-ds-ws-target-list",children:h.filter(I=>I.id!==b.id).map(I=>typeof I.id=="string"?i.jsx("option",{value:I.id},I.id):null)})]}),i.jsxs("div",{style:ut,children:[i.jsx("label",{style:ct,children:n("layout_editor.data_sources.field.on_receive")}),i.jsx("div",{"data-testid":"g7le-ds-ws-onreceive",children:i.jsx(Gt,{actions:b.onReceive,onChange:I=>x({...b,onReceive:I}),t:n,recipes:s,candidatePools:z,chipContext:"payload",actionChipCandidates:l?.actionChipCandidates??null,testIdPrefix:"g7le-ds-onreceive-list"})})]})]}),i.jsxs("div",{style:zo,children:[i.jsx("div",{style:Mo,children:n("layout_editor.data_sources.section.on_success")}),i.jsx("div",{"data-testid":"g7le-ds-onsuccess",children:i.j
Line truncated
|
||||
`).split(`
|
||||
`);return t.length>0&&t[t.length-1]===""&&t.pop(),t}const Uu=4e3;function k$(e,t,n=3){const o=Vu(e),r=Vu(t);let s=0;const a=Math.min(o.length,r.length);for(;s<a&&o[s]===r[s];)s++;let l=0;const d=Math.min(o.length,r.length)-s;for(;l<d&&o[o.length-1-l]===r[r.length-1-l];)l++;const c=o.slice(s,o.length-l),p=r.slice(s,r.length-l);if(c.length>Uu||p.length>Uu)return{hunks:[],addedCount:-1,removedCount:-1,identical:!1};const f=x$(c,p),y=w$(c,p,f),g=[];let m=s,h=s,v=0,b=0;for(let w=0;w<s;w++)g.push({kind:"context",oldLine:w+1,newLine:w+1,content:o[w]});for(const w of y)w.kind==="context"?(m++,h++,g.push({kind:"context",oldLine:m,newLine:h,content:w.content})):w.kind==="remove"?(m++,b++,g.push({kind:"remove",oldLine:m,newLine:null,content:w.content})):(h++,v++,g.push({kind:"add",oldLine:null,newLine:h,content:w.content}));for(let w=0;w<l;w++){const _=o.length-l+w,S=r.length-l+w;g.push({kind:"context",oldLine:_+1,newLine:S+1,content:o[_]})}const x=v===0&&b===0;return{hunks:S$(g,n),addedCount:v,removedCount:b,identical:x}}function S$(e,t){const n=[];for(let r=0;r<e.length;r++)e[r].kind!=="context"&&n.push(r);if(n.length===0)return[];const o=[];for(const r of n){const s=Math.max(0,r-t),a=Math.min(e.length-1,r+t),l=o[o.length-1];l&&s<=l.end+1?l.end=Math.max(l.end,a):o.push({start:s,end:a})}return o.map(r=>{const s=e.slice(r.start,r.end+1);let a=0,l=0,d=0,c=0;for(const g of s)g.oldLine!==null&&(a===0&&(a=g.oldLine),d++),g.newLine!==null&&(l===0&&(l=g.newLine),c++);const p=a||(d===0?0:1),f=l||(c===0?0:1);return{header:`@@ -${p},${d} +${f},${c} @@`,oldStart:p,oldCount:d,newStart:f,newCount:c,lines:s}})}function qu(e){return JSON.stringify(Js(e),null,2)}function Js(e){if(Array.isArray(e))return e.map(Js);if(e&&typeof e=="object"){const t=e,n={};for(const o of Object.keys(t).sort())n[o]=Js(t[o]);return n}return e}const C$={display:"flex",flexDirection:"column",height:"100%",minHeight:0},$$={display:"flex",alignItems:"center",gap:8,padding:"12px 16px",borderBottom:"1px solid #e2e8f0",background:"#f8fafc"},E$={display:"inline-flex",alignItems:"center",gap:4,padding:"4px 10px",borderRadius:6,border:"1px solid #cbd5e1",background:"#ffffff",color:"#0f172a",fontSize:12,fontWeight:600,cursor:"pointer",outline:"none"},I$={marginLeft:"auto",border:"none",background:"transparent",fontSize:16,color:"#64748b",cursor:"pointer",outline:"none"},A$={display:"flex",alignItems:"center",gap:12,padding:"8px 16px",borderBottom:"1px solid #e2e8f0",background:"#ffffff",fontSize:12,fontWeight:600},T$={flex:1,minHeight:0,overflow:"auto",background:"#ffffff",fontFamily:'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',fontSize:12,lineHeight:1.5},Gu={padding:"24px 12px",textAlign:"center",color:"#94a3b8",fontSize:13},D$={padding:"2px 12px",background:"#f1f5f9",color:"#64748b",borderTop:"1px solid #e2e8f0",borderBottom:"1px solid #e2e8f0",userSelect:"none"};function N$(e){return e==="add"?{row:{background:"#e6ffed"},gutter:{background:"#cdffd8",color:"#22863a"},sign:"+",signColor:"#22863a"}:e==="remove"?{row:{background:"#ffeef0"},gutter:{background:"#ffdce0",color:"#b31d28"},sign:"-",signColor:"#b31d28"}:{row:{background:"#ffffff"},gutter:{background:"#f8fafc",color:"#94a3b8"},sign:" ",signColor:"#94a3b8"}}const Yu={display:"inline-block",width:44,textAlign:"right",padding:"0 6px",color:"#94a3b8",userSelect:"none",borderRight:"1px solid #e2e8f0",whiteSpace:"nowrap"};function z$({oldVersion:e,newVersion:t,oldContent:n,newContent:o,t:r,onBack:s,onClose:a}){const l=u.useMemo(()=>{const c=qu(n),p=qu(o);return k$(c,p)},[n,o]),d=l.addedCount<0;return i.jsxs("div",{className:"g7le-version-diff","data-testid":"g7le-version-diff",style:C$,children:[i.jsxs("div",{"data-modal-drag-handle":!0,style:$$,children:[i.jsxs("button",{type:"button",onClick:s,style:E$,"data-testid":"g7le-version-diff-back",children:["← ",r("layout_editor.version_history.diff_back")]}),i.jsx("span",{style:{fontSize:14,fontWeight:700},"data-testid":"g7le-version-diff-title",children:r("layout_editor.version_history.diff_title",{old:e,new:t})}),i.jsx("button",{type:"button","aria-label":r("layout_editor.version_history.close"),onClick:a,style:I$,"data-testid":"g7le-version-diff-close",children:"✕"})]}),!d&&i.jsxs("div",{style:A$,"data-testid":"g7le-version-diff-summary",children:[i.jsx("span",{style:{color:"#16a34a"},"data-testid":"g7le-version-diff-added",children:r("layout_editor.version_history.added",{count:Math.max(0,l.addedCount)})}),i.jsx("span",{style:{color:"#dc2626"},"data-testid":"g7le-version-diff-removed",children:r("layout_editor.version_history.removed",{count:Math.max(0,l.removedCount)})})]}),i.jsx("div",{style:T$,children:d?i.jsx("div",{style:Gu,"data-testid":"g7le-version-diff-too-large",children:r("layout_editor.version_history.diff_too_large")}):l.identical?i.jsx("div",{style:Gu,"data-testid":"g7le-version-diff-identical",children:r("layout_editor.version_history.diff_identical")}):l.hunks.map((c,p)=>i.jsxs("div",{"data-testid":`gLine truncated
|
||||
`);return t.length>0&&t[t.length-1]===""&&t.pop(),t}const qu=4e3;function S$(e,t,n=3){const o=Uu(e),r=Uu(t);let s=0;const a=Math.min(o.length,r.length);for(;s<a&&o[s]===r[s];)s++;let l=0;const d=Math.min(o.length,r.length)-s;for(;l<d&&o[o.length-1-l]===r[r.length-1-l];)l++;const c=o.slice(s,o.length-l),p=r.slice(s,r.length-l);if(c.length>qu||p.length>qu)return{hunks:[],addedCount:-1,removedCount:-1,identical:!1};const f=w$(c,p),y=k$(c,p,f),g=[];let m=s,h=s,v=0,b=0;for(let w=0;w<s;w++)g.push({kind:"context",oldLine:w+1,newLine:w+1,content:o[w]});for(const w of y)w.kind==="context"?(m++,h++,g.push({kind:"context",oldLine:m,newLine:h,content:w.content})):w.kind==="remove"?(m++,b++,g.push({kind:"remove",oldLine:m,newLine:null,content:w.content})):(h++,v++,g.push({kind:"add",oldLine:null,newLine:h,content:w.content}));for(let w=0;w<l;w++){const _=o.length-l+w,S=r.length-l+w;g.push({kind:"context",oldLine:_+1,newLine:S+1,content:o[_]})}const x=v===0&&b===0;return{hunks:C$(g,n),addedCount:v,removedCount:b,identical:x}}function C$(e,t){const n=[];for(let r=0;r<e.length;r++)e[r].kind!=="context"&&n.push(r);if(n.length===0)return[];const o=[];for(const r of n){const s=Math.max(0,r-t),a=Math.min(e.length-1,r+t),l=o[o.length-1];l&&s<=l.end+1?l.end=Math.max(l.end,a):o.push({start:s,end:a})}return o.map(r=>{const s=e.slice(r.start,r.end+1);let a=0,l=0,d=0,c=0;for(const g of s)g.oldLine!==null&&(a===0&&(a=g.oldLine),d++),g.newLine!==null&&(l===0&&(l=g.newLine),c++);const p=a||(d===0?0:1),f=l||(c===0?0:1);return{header:`@@ -${p},${d} +${f},${c} @@`,oldStart:p,oldCount:d,newStart:f,newCount:c,lines:s}})}function Gu(e){return JSON.stringify(Js(e),null,2)}function Js(e){if(Array.isArray(e))return e.map(Js);if(e&&typeof e=="object"){const t=e,n={};for(const o of Object.keys(t).sort())n[o]=Js(t[o]);return n}return e}const $$={display:"flex",flexDirection:"column",height:"100%",minHeight:0},E$={display:"flex",alignItems:"center",gap:8,padding:"12px 16px",borderBottom:"1px solid #e2e8f0",background:"#f8fafc"},I$={display:"inline-flex",alignItems:"center",gap:4,padding:"4px 10px",borderRadius:6,border:"1px solid #cbd5e1",background:"#ffffff",color:"#0f172a",fontSize:12,fontWeight:600,cursor:"pointer",outline:"none"},A$={marginLeft:"auto",border:"none",background:"transparent",fontSize:16,color:"#64748b",cursor:"pointer",outline:"none"},T$={display:"flex",alignItems:"center",gap:12,padding:"8px 16px",borderBottom:"1px solid #e2e8f0",background:"#ffffff",fontSize:12,fontWeight:600},D$={flex:1,minHeight:0,overflow:"auto",background:"#ffffff",fontFamily:'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',fontSize:12,lineHeight:1.5},Yu={padding:"24px 12px",textAlign:"center",color:"#94a3b8",fontSize:13},N$={padding:"2px 12px",background:"#f1f5f9",color:"#64748b",borderTop:"1px solid #e2e8f0",borderBottom:"1px solid #e2e8f0",userSelect:"none"};function z$(e){return e==="add"?{row:{background:"#e6ffed"},gutter:{background:"#cdffd8",color:"#22863a"},sign:"+",signColor:"#22863a"}:e==="remove"?{row:{background:"#ffeef0"},gutter:{background:"#ffdce0",color:"#b31d28"},sign:"-",signColor:"#b31d28"}:{row:{background:"#ffffff"},gutter:{background:"#f8fafc",color:"#94a3b8"},sign:" ",signColor:"#94a3b8"}}const Xu={display:"inline-block",width:44,textAlign:"right",padding:"0 6px",color:"#94a3b8",userSelect:"none",borderRight:"1px solid #e2e8f0",whiteSpace:"nowrap"};function M$({oldVersion:e,newVersion:t,oldContent:n,newContent:o,t:r,onBack:s,onClose:a}){const l=u.useMemo(()=>{const c=Gu(n),p=Gu(o);return S$(c,p)},[n,o]),d=l.addedCount<0;return i.jsxs("div",{className:"g7le-version-diff","data-testid":"g7le-version-diff",style:$$,children:[i.jsxs("div",{"data-modal-drag-handle":!0,style:E$,children:[i.jsxs("button",{type:"button",onClick:s,style:I$,"data-testid":"g7le-version-diff-back",children:["← ",r("layout_editor.version_history.diff_back")]}),i.jsx("span",{style:{fontSize:14,fontWeight:700},"data-testid":"g7le-version-diff-title",children:r("layout_editor.version_history.diff_title",{old:e,new:t})}),i.jsx("button",{type:"button","aria-label":r("layout_editor.version_history.close"),onClick:a,style:A$,"data-testid":"g7le-version-diff-close",children:"✕"})]}),!d&&i.jsxs("div",{style:T$,"data-testid":"g7le-version-diff-summary",children:[i.jsx("span",{style:{color:"#16a34a"},"data-testid":"g7le-version-diff-added",children:r("layout_editor.version_history.added",{count:Math.max(0,l.addedCount)})}),i.jsx("span",{style:{color:"#dc2626"},"data-testid":"g7le-version-diff-removed",children:r("layout_editor.version_history.removed",{count:Math.max(0,l.removedCount)})})]}),i.jsx("div",{style:D$,children:d?i.jsx("div",{style:Yu,"data-testid":"g7le-version-diff-too-large",children:r("layout_editor.version_history.diff_too_large")}):l.identical?i.jsx("div",{style:Yu,"data-testid":"g7le-version-diff-identical",children:r("layout_editor.version_history.diff_identical")}):l.hunks.map((c,p)=>i.jsxs("div",{"data-testid":`gLine truncated
|
||||
To pick up a draggable item, press the space bar.
|
||||
While dragging, use the arrow keys to move the item.
|
||||
Press space again to drop the item in its new position, or press escape to cancel.
|
||||
`},z7={onDragStart(e){let{active:t}=e;return"Picked up draggable item "+t.id+"."},onDragOver(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was moved over droppable area "+n.id+".":"Draggable item "+t.id+" is no longer over a droppable area."},onDragEnd(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was dropped over droppable area "+n.id:"Draggable item "+t.id+" was dropped."},onDragCancel(e){let{active:t}=e;return"Dragging was cancelled. Draggable item "+t.id+" was dropped."}};function M7(e){let{announcements:t=z7,container:n,hiddenTextDescribedById:o,screenReaderInstructions:r=N7}=e;const{announce:s,announcement:a}=A7(),l=ei("DndLiveRegion"),[d,c]=u.useState(!1);if(u.useEffect(()=>{c(!0)},[]),T7(u.useMemo(()=>({onDragStart(f){let{active:y}=f;s(t.onDragStart({active:y}))},onDragMove(f){let{active:y,over:g}=f;t.onDragMove&&s(t.onDragMove({active:y,over:g}))},onDragOver(f){let{active:y,over:g}=f;s(t.onDragOver({active:y,over:g}))},onDragEnd(f){let{active:y,over:g}=f;s(t.onDragEnd({active:y,over:g}))},onDragCancel(f){let{active:y,over:g}=f;s(t.onDragCancel({active:y,over:g}))}}),[s,t])),!d)return null;const p=u.createElement(u.Fragment,null,u.createElement(E7,{id:o,value:r.draggable}),u.createElement(I7,{id:l,announcement:a}));return n?Cn.createPortal(p,n):p}var it;(function(e){e.DragStart="dragStart",e.DragMove="dragMove",e.DragEnd="dragEnd",e.DragCancel="dragCancel",e.DragOver="dragOver",e.RegisterDroppable="registerDroppable",e.SetDroppableDisabled="setDroppableDisabled",e.UnregisterDroppable="unregisterDroppable"})(it||(it={}));function oi(){}function L7(e,t){return u.useMemo(()=>({sensor:e,options:t??{}}),[e,t])}function P7(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return u.useMemo(()=>[...t].filter(o=>o!=null),[...t])}const Ht=Object.freeze({x:0,y:0});function O7(e,t){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function B7(e,t){const n=ni(e);if(!n)return"0 0";const o={x:(n.x-t.left)/t.width*100,y:(n.y-t.top)/t.height*100};return o.x+"% "+o.y+"%"}function W7(e,t){let{data:{value:n}}=e,{data:{value:o}}=t;return n-o}function j7(e,t){let{data:{value:n}}=e,{data:{value:o}}=t;return o-n}function H7(e){let{left:t,top:n,height:o,width:r}=e;return[{x:t,y:n},{x:t+r,y:n},{x:t,y:n+o},{x:t+r,y:n+o}]}function F7(e,t){if(!e||e.length===0)return null;const[n]=e;return n[t]}function K7(e,t){const n=Math.max(t.top,e.top),o=Math.max(t.left,e.left),r=Math.min(t.left+t.width,e.left+e.width),s=Math.min(t.top+t.height,e.top+e.height),a=r-o,l=s-n;if(o<r&&n<s){const d=t.width*t.height,c=e.width*e.height,p=a*l,f=p/(d+c-p);return Number(f.toFixed(4))}return 0}const V7=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:o}=e;const r=[];for(const s of o){const{id:a}=s,l=n.get(a);if(l){const d=K7(l,t);d>0&&r.push({id:a,data:{droppableContainer:s,value:d}})}}return r.sort(j7)};function U7(e,t){const{top:n,left:o,bottom:r,right:s}=t;return n<=e.y&&e.y<=r&&o<=e.x&&e.x<=s}const q7=e=>{let{droppableContainers:t,droppableRects:n,pointerCoordinates:o}=e;if(!o)return[];const r=[];for(const s of t){const{id:a}=s,l=n.get(a);if(l&&U7(o,l)){const c=H7(l).reduce((f,y)=>f+O7(o,y),0),p=Number((c/4).toFixed(4));r.push({id:a,data:{droppableContainer:s,value:p}})}}return r.sort(W7)};function G7(e,t,n){return{...e,scaleX:t&&n?t.width/n.width:1,scaleY:t&&n?t.height/n.height:1}}function jp(e,t){return e&&t?{x:e.left-t.left,y:e.top-t.top}:Ht}function Y7(e){return function(n){for(var o=arguments.length,r=new Array(o>1?o-1:0),s=1;s<o;s++)r[s-1]=arguments[s];return r.reduce((a,l)=>({...a,top:a.top+e*l.y,bottom:a.bottom+e*l.y,left:a.left+e*l.x,right:a.right+e*l.x}),{...n})}}const X7=Y7(1);function Hp(e){if(e.startsWith("matrix3d(")){const t=e.slice(9,-1).split(/, /);return{x:+t[12],y:+t[13],scaleX:+t[0],scaleY:+t[5]}}else if(e.startsWith("matrix(")){const t=e.slice(7,-1).split(/, /);return{x:+t[4],y:+t[5],scaleX:+t[0],scaleY:+t[3]}}return null}function J7(e,t,n){const o=Hp(t);if(!o)return e;const{scaleX:r,scaleY:s,x:a,y:l}=o,d=e.left-a-(1-r)*parseFloat(n),c=e.top-l-(1-s)*parseFloat(n.slice(n.indexOf(" ")+1)),p=r?e.width/r:e.width,f=s?e.height/s:e.height;return{width:p,height:f,top:c,right:d+p,bottom:c+f,left:d}}const Z7={ignoreTransform:!1};function jo(e,t){t===void 0&&(t=Z7);let n=e.getBoundingClientRect();if(t.ignoreTransform){const{transform:c,transformOrigin:p}=_t(e).getComputedStyle(e);c&&(n=J7(n,c,p))}const{top:o,left:r,width:s,height:a,bottom:l,right:d}=n;return{top:o,left:r,width:s,height:a,bottom:l,right:d}}function Fp(e){return jo(e,{ignoreTransform:!0})}function Q7(e){const t=e.innerWidth,n=e.innerHeight;return{top:0,left:0,right:t,bottom:n,width:t,height:n}}function R7(e,t){return t===void 0&&(t=_t(e).getComputedStyle(e)),t.position==="fixed"}function e2(e,t){t===void 0&&(t=_t(e).getComputedStyle(e));const n=/(auto|scroll|overlay)/;return["overflow","overflowX","overflowY"].some(r=>{const s=t[r];return typeof s=="string"?n.test(s):!1})}function da(e,t){const n=[];
Line truncated
|
||||
`:"",e.message]})]}),i.jsxs("div",{style:{display:"flex",justifyContent:"center",gap:8,flexWrap:"wrap"},children:[e.kind==="unauthorized"&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-signin",onClick:a,style:Ka,children:t("layout_editor.access_error.action.sign_in")}),e.kind==="forbidden"&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-home",onClick:r,style:Ka,children:t("layout_editor.access_error.action.go_home")}),(e.kind==="not_found"||e.kind==="server_error"||e.kind==="network"||e.kind==="unknown")&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-retry",onClick:s,style:Ka,children:t("layout_editor.access_error.action.retry")}),e.kind!=="unauthorized"&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-home-secondary",onClick:r,style:_4,children:t("layout_editor.access_error.action.go_admin_home")})]})]})]})}const Ka={padding:"8px 16px",background:"#2563eb",color:"#fff",border:"1px solid #1d4ed8",borderRadius:6,fontSize:13,fontWeight:600,cursor:"pointer"},_4={padding:"8px 16px",background:"#fff",color:"#475569",border:"1px solid #cbd5e1",borderRadius:6,fontSize:13,fontWeight:500,cursor:"pointer"};function x4(e){const t=window;if(typeof t>"u")return{getSnapshot:()=>({globalState:{},localState:{}}),restore:()=>{}};const n=t.G7Core||{},o={templateApp:t.__templateApp,g7CoreState:n.state,g7CoreDispatch:n.dispatch,g7CoreData:n.data,g7CoreModal:n.modal,g7CoreToast:n.toast};let r={...e.initialGlobalState??{}};r._local||(r._local={});const s=[],a=()=>{const c={globalState:{...r},localState:{...r._local??{}}};for(const p of s)try{p(r)}catch{}e.onChange?.(c)},l={},d={getGlobalState:()=>r,setGlobalState:(c,p)=>{typeof c=="function"?r=c(r):r={...r,...c},a()},onGlobalStateChange:c=>(s.push(c),()=>{const p=s.indexOf(c);p>=0&&s.splice(p,1)}),get globalState(){return r},getActionDispatcher:()=>e.actionDispatcher,getDataSource:c=>l[c]??null,setDataSource:(c,p)=>{l[c]=p},refetchDataSource:async c=>null,updateDataSourceItem:(c,p,f)=>l[c]??null,getLocale:()=>e.locale,changeLocale:c=>{},getRouter:()=>({navigate:()=>{},replace:()=>{},push:()=>{}}),updateQueryParams:()=>{}};return t.__templateApp=d,n&&typeof n.dispatch=="function"&&(n.dispatch=(c,p)=>{try{return e.actionDispatcher.executeAction(c,p??{})}catch{return}}),n&&typeof n.modal=="object"&&n.modal!==null&&(n.modal={...n.modal,open:()=>{},close:()=>{},closeAll:()=>{}}),n&&typeof n.toast=="object"&&n.toast!==null&&(n.toast={...n.toast,push:()=>{},dismiss:()=>{}}),{getSnapshot:()=>({globalState:{...r},localState:{...r._local??{}}}),restore:()=>{t.__templateApp===d&&(t.__templateApp=o.templateApp),t.G7Core&&(o.g7CoreState!==void 0&&(t.G7Core.state=o.g7CoreState),o.g7CoreDispatch!==void 0&&(t.G7Core.dispatch=o.g7CoreDispatch),o.g7CoreData!==void 0&&(t.G7Core.data=o.g7CoreData),o.g7CoreModal!==void 0&&(t.G7Core.modal=o.g7CoreModal),o.g7CoreToast!==void 0&&(t.G7Core.toast=o.g7CoreToast)),s.length=0}}}const w4=new Set(["id","userId","postId","orderId","productId","categoryId","commentId","noticeId","inquiryId","addressId","wishlistId","notificationId","roleId","menuId"]);function k4(e){if(!e)return{};const t={},n=/:([a-zA-Z_][a-zA-Z0-9_]*)/g;let o;for(;(o=n.exec(e))!==null;){const r=o[1];t[r]=S4(r)}return t}function S4(e){return w4.has(e)||/Id$|_id$/.test(e)?"1":"sample"}const C4=dr("LayoutEditor");function Xo(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function bi(e){if(Array.isArray(e))return e.map(t=>bi(t));if(Xo(e)){const t={};for(const[n,o]of Object.entries(e))t[n]=bi(o);return t}return e}function hg(e,t="",n=new Set){for(const[o,r]of Object.entries(e)){const s=t?`${t}.${o}`:o;Xo(r)?hg(r,s,n):n.add(s)}return n}function yg(e,t,n,o,r,s=""){for(const[a,l]of Object.entries(t)){const d=s?`${s}.${a}`:a;if(n.has(d)){r(`[LayoutEditor] sampleGlobal key conflict: extension '${o}' override of core key '${d}' ignored`);continue}const c=e[a];Xo(l)&&Xo(c)?yg(c,l,n,o,r,d):e[a]=bi(l)}}function Va(e){const t=e.warn??(r=>C4.warn(r)),n=bi(e.coreSeed??{});e.isGuestOnly&&"currentUser"in n&&delete n.currentUser;const o=hg(n);for(const r of e.sources)Xo(r.sampleGlobal)&&yg(n,r.sampleGlobal,o,r.id,t);return e.isGuestOnly&&"currentUser"in n&&delete n.currentUser,n}function $4(e){const t={};if(!e||typeof e!="object"||Array.isArray(e))return t;for(const[n,o]of Object.entries(e))n.startsWith("$")&&(t[n]=o);return t}function E4(e){const t={};if(!e||typeof e!="object"||Array.isArray(e))return t;for(const[n,o]of Object.entries(e))n.startsWith("$")||(t[n]=o);return t}function I4(e){if(typeof e!="string")return null;const t=/^\s*\{\{\s*([\s\S]*?)\s*\}\}\s*$/.exec(e);if(!t)return null;let n=t[1];const o=n.indexOf("??");return o>=0&&(n=n.slice(0,o)),n=n.replace(/\?\./g,".").replace(/\s+/g,""),n===""||!/^[A-Za-z_$][A-Za-z0-9_$]*(\.[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(n)?null:n.split(".")}function A4(e,t){const n=I4(t);if(!n||n.length===0)return e;let o=e;for(let a=0;a<n.length;a++){if(o==null||typeof o!="obj
Line truncated
|
||||
`},M7={onDragStart(e){let{active:t}=e;return"Picked up draggable item "+t.id+"."},onDragOver(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was moved over droppable area "+n.id+".":"Draggable item "+t.id+" is no longer over a droppable area."},onDragEnd(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was dropped over droppable area "+n.id:"Draggable item "+t.id+" was dropped."},onDragCancel(e){let{active:t}=e;return"Dragging was cancelled. Draggable item "+t.id+" was dropped."}};function L7(e){let{announcements:t=M7,container:n,hiddenTextDescribedById:o,screenReaderInstructions:r=z7}=e;const{announce:s,announcement:a}=T7(),l=ei("DndLiveRegion"),[d,c]=u.useState(!1);if(u.useEffect(()=>{c(!0)},[]),D7(u.useMemo(()=>({onDragStart(f){let{active:y}=f;s(t.onDragStart({active:y}))},onDragMove(f){let{active:y,over:g}=f;t.onDragMove&&s(t.onDragMove({active:y,over:g}))},onDragOver(f){let{active:y,over:g}=f;s(t.onDragOver({active:y,over:g}))},onDragEnd(f){let{active:y,over:g}=f;s(t.onDragEnd({active:y,over:g}))},onDragCancel(f){let{active:y,over:g}=f;s(t.onDragCancel({active:y,over:g}))}}),[s,t])),!d)return null;const p=u.createElement(u.Fragment,null,u.createElement(I7,{id:o,value:r.draggable}),u.createElement(A7,{id:l,announcement:a}));return n?Cn.createPortal(p,n):p}var it;(function(e){e.DragStart="dragStart",e.DragMove="dragMove",e.DragEnd="dragEnd",e.DragCancel="dragCancel",e.DragOver="dragOver",e.RegisterDroppable="registerDroppable",e.SetDroppableDisabled="setDroppableDisabled",e.UnregisterDroppable="unregisterDroppable"})(it||(it={}));function oi(){}function P7(e,t){return u.useMemo(()=>({sensor:e,options:t??{}}),[e,t])}function O7(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return u.useMemo(()=>[...t].filter(o=>o!=null),[...t])}const Ht=Object.freeze({x:0,y:0});function B7(e,t){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function W7(e,t){const n=ni(e);if(!n)return"0 0";const o={x:(n.x-t.left)/t.width*100,y:(n.y-t.top)/t.height*100};return o.x+"% "+o.y+"%"}function j7(e,t){let{data:{value:n}}=e,{data:{value:o}}=t;return n-o}function H7(e,t){let{data:{value:n}}=e,{data:{value:o}}=t;return o-n}function F7(e){let{left:t,top:n,height:o,width:r}=e;return[{x:t,y:n},{x:t+r,y:n},{x:t,y:n+o},{x:t+r,y:n+o}]}function K7(e,t){if(!e||e.length===0)return null;const[n]=e;return n[t]}function V7(e,t){const n=Math.max(t.top,e.top),o=Math.max(t.left,e.left),r=Math.min(t.left+t.width,e.left+e.width),s=Math.min(t.top+t.height,e.top+e.height),a=r-o,l=s-n;if(o<r&&n<s){const d=t.width*t.height,c=e.width*e.height,p=a*l,f=p/(d+c-p);return Number(f.toFixed(4))}return 0}const U7=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:o}=e;const r=[];for(const s of o){const{id:a}=s,l=n.get(a);if(l){const d=V7(l,t);d>0&&r.push({id:a,data:{droppableContainer:s,value:d}})}}return r.sort(H7)};function q7(e,t){const{top:n,left:o,bottom:r,right:s}=t;return n<=e.y&&e.y<=r&&o<=e.x&&e.x<=s}const G7=e=>{let{droppableContainers:t,droppableRects:n,pointerCoordinates:o}=e;if(!o)return[];const r=[];for(const s of t){const{id:a}=s,l=n.get(a);if(l&&q7(o,l)){const c=F7(l).reduce((f,y)=>f+B7(o,y),0),p=Number((c/4).toFixed(4));r.push({id:a,data:{droppableContainer:s,value:p}})}}return r.sort(j7)};function Y7(e,t,n){return{...e,scaleX:t&&n?t.width/n.width:1,scaleY:t&&n?t.height/n.height:1}}function Hp(e,t){return e&&t?{x:e.left-t.left,y:e.top-t.top}:Ht}function X7(e){return function(n){for(var o=arguments.length,r=new Array(o>1?o-1:0),s=1;s<o;s++)r[s-1]=arguments[s];return r.reduce((a,l)=>({...a,top:a.top+e*l.y,bottom:a.bottom+e*l.y,left:a.left+e*l.x,right:a.right+e*l.x}),{...n})}}const J7=X7(1);function Fp(e){if(e.startsWith("matrix3d(")){const t=e.slice(9,-1).split(/, /);return{x:+t[12],y:+t[13],scaleX:+t[0],scaleY:+t[5]}}else if(e.startsWith("matrix(")){const t=e.slice(7,-1).split(/, /);return{x:+t[4],y:+t[5],scaleX:+t[0],scaleY:+t[3]}}return null}function Z7(e,t,n){const o=Fp(t);if(!o)return e;const{scaleX:r,scaleY:s,x:a,y:l}=o,d=e.left-a-(1-r)*parseFloat(n),c=e.top-l-(1-s)*parseFloat(n.slice(n.indexOf(" ")+1)),p=r?e.width/r:e.width,f=s?e.height/s:e.height;return{width:p,height:f,top:c,right:d+p,bottom:c+f,left:d}}const Q7={ignoreTransform:!1};function Ho(e,t){t===void 0&&(t=Q7);let n=e.getBoundingClientRect();if(t.ignoreTransform){const{transform:c,transformOrigin:p}=_t(e).getComputedStyle(e);c&&(n=Z7(n,c,p))}const{top:o,left:r,width:s,height:a,bottom:l,right:d}=n;return{top:o,left:r,width:s,height:a,bottom:l,right:d}}function Kp(e){return Ho(e,{ignoreTransform:!0})}function R7(e){const t=e.innerWidth,n=e.innerHeight;return{top:0,left:0,right:t,bottom:n,width:t,height:n}}function e2(e,t){return t===void 0&&(t=_t(e).getComputedStyle(e)),t.position==="fixed"}function t2(e,t){t===void 0&&(t=_t(e).getComputedStyle(e));const n=/(auto|scroll|overlay)/;return["overflow","overflowX","overflowY"].some(r=>{const s=t[r];return typeof s=="string"?n.test(s):!1})}function da(e,t){const n=[];
Line truncated
|
||||
`:"",e.message]})]}),i.jsxs("div",{style:{display:"flex",justifyContent:"center",gap:8,flexWrap:"wrap"},children:[e.kind==="unauthorized"&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-signin",onClick:a,style:Ka,children:t("layout_editor.access_error.action.sign_in")}),e.kind==="forbidden"&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-home",onClick:r,style:Ka,children:t("layout_editor.access_error.action.go_home")}),(e.kind==="not_found"||e.kind==="server_error"||e.kind==="network"||e.kind==="unknown")&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-retry",onClick:s,style:Ka,children:t("layout_editor.access_error.action.retry")}),e.kind!=="unauthorized"&&i.jsx("button",{type:"button","data-testid":"g7le-access-error-action-home-secondary",onClick:r,style:x4,children:t("layout_editor.access_error.action.go_admin_home")})]})]})]})}const Ka={padding:"8px 16px",background:"#2563eb",color:"#fff",border:"1px solid #1d4ed8",borderRadius:6,fontSize:13,fontWeight:600,cursor:"pointer"},x4={padding:"8px 16px",background:"#fff",color:"#475569",border:"1px solid #cbd5e1",borderRadius:6,fontSize:13,fontWeight:500,cursor:"pointer"};function w4(e){const t=window;if(typeof t>"u")return{getSnapshot:()=>({globalState:{},localState:{}}),restore:()=>{}};const n=t.G7Core||{},o={templateApp:t.__templateApp,g7CoreState:n.state,g7CoreDispatch:n.dispatch,g7CoreData:n.data,g7CoreModal:n.modal,g7CoreToast:n.toast};let r={...e.initialGlobalState??{}};r._local||(r._local={});const s=[],a=()=>{const c={globalState:{...r},localState:{...r._local??{}}};for(const p of s)try{p(r)}catch{}e.onChange?.(c)},l={},d={getGlobalState:()=>r,setGlobalState:(c,p)=>{typeof c=="function"?r=c(r):r={...r,...c},a()},onGlobalStateChange:c=>(s.push(c),()=>{const p=s.indexOf(c);p>=0&&s.splice(p,1)}),get globalState(){return r},getActionDispatcher:()=>e.actionDispatcher,getDataSource:c=>l[c]??null,setDataSource:(c,p)=>{l[c]=p},refetchDataSource:async c=>null,updateDataSourceItem:(c,p,f)=>l[c]??null,getLocale:()=>e.locale,changeLocale:c=>{},getRouter:()=>({navigate:()=>{},replace:()=>{},push:()=>{}}),updateQueryParams:()=>{}};return t.__templateApp=d,n&&typeof n.dispatch=="function"&&(n.dispatch=(c,p)=>{try{return e.actionDispatcher.executeAction(c,p??{})}catch{return}}),n&&typeof n.modal=="object"&&n.modal!==null&&(n.modal={...n.modal,open:()=>{},close:()=>{},closeAll:()=>{}}),n&&typeof n.toast=="object"&&n.toast!==null&&(n.toast={...n.toast,push:()=>{},dismiss:()=>{}}),{getSnapshot:()=>({globalState:{...r},localState:{...r._local??{}}}),restore:()=>{t.__templateApp===d&&(t.__templateApp=o.templateApp),t.G7Core&&(o.g7CoreState!==void 0&&(t.G7Core.state=o.g7CoreState),o.g7CoreDispatch!==void 0&&(t.G7Core.dispatch=o.g7CoreDispatch),o.g7CoreData!==void 0&&(t.G7Core.data=o.g7CoreData),o.g7CoreModal!==void 0&&(t.G7Core.modal=o.g7CoreModal),o.g7CoreToast!==void 0&&(t.G7Core.toast=o.g7CoreToast)),s.length=0}}}const k4=new Set(["id","userId","postId","orderId","productId","categoryId","commentId","noticeId","inquiryId","addressId","wishlistId","notificationId","roleId","menuId"]);function S4(e){if(!e)return{};const t={},n=/:([a-zA-Z_][a-zA-Z0-9_]*)/g;let o;for(;(o=n.exec(e))!==null;){const r=o[1];t[r]=C4(r)}return t}function C4(e){return k4.has(e)||/Id$|_id$/.test(e)?"1":"sample"}const $4=bo("LayoutEditor");function Jo(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function bi(e){if(Array.isArray(e))return e.map(t=>bi(t));if(Jo(e)){const t={};for(const[n,o]of Object.entries(e))t[n]=bi(o);return t}return e}function yg(e,t="",n=new Set){for(const[o,r]of Object.entries(e)){const s=t?`${t}.${o}`:o;Jo(r)?yg(r,s,n):n.add(s)}return n}function bg(e,t,n,o,r,s=""){for(const[a,l]of Object.entries(t)){const d=s?`${s}.${a}`:a;if(n.has(d)){r(`[LayoutEditor] sampleGlobal key conflict: extension '${o}' override of core key '${d}' ignored`);continue}const c=e[a];Jo(l)&&Jo(c)?bg(c,l,n,o,r,d):e[a]=bi(l)}}function Va(e){const t=e.warn??(r=>$4.warn(r)),n=bi(e.coreSeed??{});e.isGuestOnly&&"currentUser"in n&&delete n.currentUser;const o=yg(n);for(const r of e.sources)Jo(r.sampleGlobal)&&bg(n,r.sampleGlobal,o,r.id,t);return e.isGuestOnly&&"currentUser"in n&&delete n.currentUser,n}function E4(e){const t={};if(!e||typeof e!="object"||Array.isArray(e))return t;for(const[n,o]of Object.entries(e))n.startsWith("$")&&(t[n]=o);return t}function I4(e){const t={};if(!e||typeof e!="object"||Array.isArray(e))return t;for(const[n,o]of Object.entries(e))n.startsWith("$")||(t[n]=o);return t}function A4(e){if(typeof e!="string")return null;const t=/^\s*\{\{\s*([\s\S]*?)\s*\}\}\s*$/.exec(e);if(!t)return null;let n=t[1];const o=n.indexOf("??");return o>=0&&(n=n.slice(0,o)),n=n.replace(/\?\./g,".").replace(/\s+/g,""),n===""||!/^[A-Za-z_$][A-Za-z0-9_$]*(\.[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(n)?null:n.split(".")}function T4(e,t){const n=A4(t);if(!n||n.length===0)return e;let o=e;for(let a=0;a<n.length;a++){if(o==null||typeof o!="obj
Line truncated
|
||||
//# sourceMappingURL=layout-editor.min.js.map
|
||||
+37
-37
@@ -1,20 +1,20 @@
|
||||
var mT=Object.defineProperty;var yT=(Yt,Wr,Yr)=>Wr in Yt?mT(Yt,Wr,{enumerable:!0,configurable:!0,writable:!0,value:Yr}):Yt[Wr]=Yr;var $=(Yt,Wr,Yr)=>yT(Yt,typeof Wr!="symbol"?Wr+"":Wr,Yr);(function(Yt){"use strict";function Wr(o,e){for(var n=0;n<e.length;n++){const a=e[n];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in o)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(o,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}))}function Yr(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var Xu={exports:{}},Ye={};var zg;function jS(){if(zg)return Ye;zg=1;var o=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),l=Symbol.for("react.consumer"),u=Symbol.for("react.context"),d=Symbol.for("react.forward_ref"),f=Symbol.for("react.suspense"),g=Symbol.for("react.memo"),m=Symbol.for("react.lazy"),b=Symbol.for("react.activity"),v=Symbol.iterator;function S(N){return N===null||typeof N!="object"?null:(N=v&&N[v]||N["@@iterator"],typeof N=="function"?N:null)}var _={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},A=Object.assign,k={};function O(N,T,fe){this.props=N,this.context=T,this.refs=k,this.updater=fe||_}O.prototype.isReactComponent={},O.prototype.setState=function(N,T){if(typeof N!="object"&&typeof N!="function"&&N!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,N,T,"setState")},O.prototype.forceUpdate=function(N){this.updater.enqueueForceUpdate(this,N,"forceUpdate")};function M(){}M.prototype=O.prototype;function x(N,T,fe){this.props=N,this.context=T,this.refs=k,this.updater=fe||_}var D=x.prototype=new M;D.constructor=x,A(D,O.prototype),D.isPureReactComponent=!0;var U=Array.isArray;function z(){}var q={H:null,A:null,T:null,S:null},X=Object.prototype.hasOwnProperty;function pe(N,T,fe){var he=fe.ref;return{$$typeof:o,type:N,key:T,ref:he!==void 0?he:null,props:fe}}function Ce(N,T){return pe(N.type,T,N.props)}function ve(N){return typeof N=="object"&&N!==null&&N.$$typeof===o}function Le(N){var T={"=":"=0",":":"=2"};return"$"+N.replace(/[=:]/g,function(fe){return T[fe]})}var Ve=/\/+/g;function Fe(N,T){return typeof N=="object"&&N!==null&&N.key!=null?Le(""+N.key):T.toString(36)}function Tt(N){switch(N.status){case"fulfilled":return N.value;case"rejected":throw N.reason;default:switch(typeof N.status=="string"?N.then(z,z):(N.status="pending",N.then(function(T){N.status==="pending"&&(N.status="fulfilled",N.value=T)},function(T){N.status==="pending"&&(N.status="rejected",N.reason=T)})),N.status){case"fulfilled":return N.value;case"rejected":throw N.reason}}throw N}function J(N,T,fe,he,Ae){var te=typeof N;(te==="undefined"||te==="boolean")&&(N=null);var we=!1;if(N===null)we=!0;else switch(te){case"bigint":case"string":case"number":we=!0;break;case"object":switch(N.$$typeof){case o:case e:we=!0;break;case m:return we=N._init,J(we(N._payload),T,fe,he,Ae)}}if(we)return Ae=Ae(N),we=he===""?"."+Fe(N,0):he,U(Ae)?(fe="",we!=null&&(fe=we.replace(Ve,"$&/")+"/"),J(Ae,T,fe,"",function(ze){return ze})):Ae!=null&&(ve(Ae)&&(Ae=Ce(Ae,fe+(Ae.key==null||N&&N.key===Ae.key?"":(""+Ae.key).replace(Ve,"$&/")+"/")+we)),T.push(Ae)),1;we=0;var qe=he===""?".":he+":";if(U(N))for(var ke=0;ke<N.length;ke++)he=N[ke],te=qe+Fe(he,ke),we+=J(he,T,fe,te,Ae);else if(ke=S(N),typeof ke=="function")for(N=ke.call(N),ke=0;!(he=N.next()).done;)he=he.value,te=qe+Fe(he,ke++),we+=J(he,T,fe,te,Ae);else if(te==="object"){if(typeof N.then=="function")return J(Tt(N),T,fe,he,Ae);throw T=String(N),Error("Objects are not valid as a React child (found: "+(T==="[object Object]"?"object with keys {"+Object.keys(N).join(", ")+"}":T)+"). If you meant to render a collection of children, use an array instead.")}return we}function de(N,T,fe){if(N==null)return N;var he=[],Ae=0;return J(N,he,"","",function(te){return T.call(fe,te,Ae++)}),he}function Ne(N){if(N._status===-1){var T=N._result;T=T(),T.then(function(fe){(N._status===0||N._status===-1)&&(N._status=1,N._result=fe)},function(fe){(N._status===0||N._status===-1)&&(N._status=2,N._result=fe)}),N._status===-1&&(N._status=0,N._result=T)}if(N._status===1)return N._result.default;throw N._result}var ee=typeof reportError=="function"?reportError:function(N){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var T=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof N=="object"&&N!==null&&typeof N.message=="string"?String(N.message):String(N),error:N});if(!window.dispatchEvent(T))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",N);return}console.error(N)},ge={map:de,forEach:function(N,TLine truncated
|
||||
var ET=Object.defineProperty;var _T=(Qt,Xr,Jr)=>Xr in Qt?ET(Qt,Xr,{enumerable:!0,configurable:!0,writable:!0,value:Jr}):Qt[Xr]=Jr;var $=(Qt,Xr,Jr)=>_T(Qt,typeof Xr!="symbol"?Xr+"":Xr,Jr);(function(Qt){"use strict";function Xr(o,e){for(var n=0;n<e.length;n++){const a=e[n];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in o)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(o,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}))}function Jr(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var td={exports:{}},Ye={};var Vg;function YS(){if(Vg)return Ye;Vg=1;var o=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),l=Symbol.for("react.consumer"),c=Symbol.for("react.context"),d=Symbol.for("react.forward_ref"),f=Symbol.for("react.suspense"),g=Symbol.for("react.memo"),m=Symbol.for("react.lazy"),y=Symbol.for("react.activity"),S=Symbol.iterator;function v(N){return N===null||typeof N!="object"?null:(N=S&&N[S]||N["@@iterator"],typeof N=="function"?N:null)}var _={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},A=Object.assign,x={};function O(N,R,de){this.props=N,this.context=R,this.refs=x,this.updater=de||_}O.prototype.isReactComponent={},O.prototype.setState=function(N,R){if(typeof N!="object"&&typeof N!="function"&&N!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,N,R,"setState")},O.prototype.forceUpdate=function(N){this.updater.enqueueForceUpdate(this,N,"forceUpdate")};function M(){}M.prototype=O.prototype;function T(N,R,de){this.props=N,this.context=R,this.refs=x,this.updater=de||_}var D=T.prototype=new M;D.constructor=T,A(D,O.prototype),D.isPureReactComponent=!0;var z=Array.isArray;function P(){}var q={H:null,A:null,T:null,S:null},W=Object.prototype.hasOwnProperty;function he(N,R,de){var fe=de.ref;return{$$typeof:o,type:N,key:R,ref:fe!==void 0?fe:null,props:de}}function Ee(N,R){return he(N.type,R,N.props)}function be(N){return typeof N=="object"&&N!==null&&N.$$typeof===o}function Ue(N){var R={"=":"=0",":":"=2"};return"$"+N.replace(/[=:]/g,function(de){return R[de]})}var Fe=/\/+/g;function Ge(N,R){return typeof N=="object"&&N!==null&&N.key!=null?Ue(""+N.key):R.toString(36)}function wt(N){switch(N.status){case"fulfilled":return N.value;case"rejected":throw N.reason;default:switch(typeof N.status=="string"?N.then(P,P):(N.status="pending",N.then(function(R){N.status==="pending"&&(N.status="fulfilled",N.value=R)},function(R){N.status==="pending"&&(N.status="rejected",N.reason=R)})),N.status){case"fulfilled":return N.value;case"rejected":throw N.reason}}throw N}function J(N,R,de,fe,xe){var ee=typeof N;(ee==="undefined"||ee==="boolean")&&(N=null);var _e=!1;if(N===null)_e=!0;else switch(ee){case"bigint":case"string":case"number":_e=!0;break;case"object":switch(N.$$typeof){case o:case e:_e=!0;break;case m:return _e=N._init,J(_e(N._payload),R,de,fe,xe)}}if(_e)return xe=xe(N),_e=fe===""?"."+Ge(N,0):fe,z(xe)?(de="",_e!=null&&(de=_e.replace(Fe,"$&/")+"/"),J(xe,R,de,"",function(Ie){return Ie})):xe!=null&&(be(xe)&&(xe=Ee(xe,de+(xe.key==null||N&&N.key===xe.key?"":(""+xe.key).replace(Fe,"$&/")+"/")+_e)),R.push(xe)),1;_e=0;var Be=fe===""?".":fe+":";if(z(N))for(var ke=0;ke<N.length;ke++)fe=N[ke],ee=Be+Ge(fe,ke),_e+=J(fe,R,de,ee,xe);else if(ke=v(N),typeof ke=="function")for(N=ke.call(N),ke=0;!(fe=N.next()).done;)fe=fe.value,ee=Be+Ge(fe,ke++),_e+=J(fe,R,de,ee,xe);else if(ee==="object"){if(typeof N.then=="function")return J(wt(N),R,de,fe,xe);throw R=String(N),Error("Objects are not valid as a React child (found: "+(R==="[object Object]"?"object with keys {"+Object.keys(N).join(", ")+"}":R)+"). If you meant to render a collection of children, use an array instead.")}return _e}function ue(N,R,de){if(N==null)return N;var fe=[],xe=0;return J(N,fe,"","",function(ee){return R.call(de,ee,xe++)}),fe}function Me(N){if(N._status===-1){var R=N._result;R=R(),R.then(function(de){(N._status===0||N._status===-1)&&(N._status=1,N._result=de)},function(de){(N._status===0||N._status===-1)&&(N._status=2,N._result=de)}),N._status===-1&&(N._status=0,N._result=R)}if(N._status===1)return N._result.default;throw N._result}var te=typeof reportError=="function"?reportError:function(N){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var R=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof N=="object"&&N!==null&&typeof N.message=="string"?String(N.message):String(N),error:N});if(!window.dispatchEvent(R))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",N);return}console.error(N)},pe={map:ue,forEach:function(N,RLine truncated
|
||||
at`)?" (<anonymous>)":-1<s.stack.indexOf("@")?"@unknown:0:0":""}return`
|
||||
`+it+t+Ot}var _t=!1;function Me(t,r){if(!t||_t)return"";_t=!0;var s=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var c={DetermineComponentFrameRoot:function(){try{if(r){var ce=function(){throw Error()};if(Object.defineProperty(ce.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(ce,[])}catch(ne){var Q=ne}Reflect.construct(t,[],ce)}else{try{ce.call()}catch(ne){Q=ne}t.call(ce.prototype)}}else{try{throw Error()}catch(ne){Q=ne}(ce=t())&&typeof ce.catch=="function"&&ce.catch(function(){})}}catch(ne){if(ne&&Q&&typeof ne.stack=="string")return[ne.stack,Q.stack]}return[null,null]}};c.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var h=Object.getOwnPropertyDescriptor(c.DetermineComponentFrameRoot,"name");h&&h.configurable&&Object.defineProperty(c.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var p=c.DetermineComponentFrameRoot(),C=p[0],R=p[1];if(C&&R){var B=C.split(`
|
||||
`),Y=R.split(`
|
||||
`);for(h=c=0;c<B.length&&!B[c].includes("DetermineComponentFrameRoot");)c++;for(;h<Y.length&&!Y[h].includes("DetermineComponentFrameRoot");)h++;if(c===B.length||h===Y.length)for(c=B.length-1,h=Y.length-1;1<=c&&0<=h&&B[c]!==Y[h];)h--;for(;1<=c&&0<=h;c--,h--)if(B[c]!==Y[h]){if(c!==1||h!==1)do if(c--,h--,0>h||B[c]!==Y[h]){var ae=`
|
||||
`+B[c].replace(" at new "," at ");return t.displayName&&ae.includes("<anonymous>")&&(ae=ae.replace("<anonymous>",t.displayName)),ae}while(1<=c&&0<=h);break}}}finally{_t=!1,Error.prepareStackTrace=s}return(s=t?t.displayName||t.name:"")?vt(s):""}function tt(t,r){switch(t.tag){case 26:case 27:case 5:return vt(t.type);case 16:return vt("Lazy");case 13:return t.child!==r&&r!==null?vt("Suspense Fallback"):vt("Suspense");case 19:return vt("SuspenseList");case 0:case 15:return Me(t.type,!1);case 11:return Me(t.type.render,!1);case 1:return Me(t.type,!0);case 31:return vt("Activity");default:return""}}function nn(t){try{var r="",s=null;do r+=tt(t,s),s=t,t=t.return;while(t);return r}catch(c){return`
|
||||
Error generating stack: `+c.message+`
|
||||
`+c.stack}}var Mt=Object.prototype.hasOwnProperty,Pt=o.unstable_scheduleCallback,rn=o.unstable_cancelCallback,an=o.unstable_shouldYield,ea=o.unstable_requestPaint,qt=o.unstable_now,bi=o.unstable_getCurrentPriorityLevel,ta=o.unstable_ImmediatePriority,yn=o.unstable_UserBlockingPriority,Ln=o.unstable_NormalPriority,nt=o.unstable_LowPriority,Hn=o.unstable_IdlePriority,bn=o.log,na=o.unstable_setDisableYieldValue,rr=null,sn=null;function Tn(t){if(typeof bn=="function"&&na(t),sn&&typeof sn.setStrictMode=="function")try{sn.setStrictMode(rr,t)}catch{}}var on=Math.clz32?Math.clz32:le,V=Math.log,se=Math.LN2;function le(t){return t>>>=0,t===0?32:31-(V(t)/se|0)|0}var Z=256,ye=262144,ue=4194304;function Ee(t){var r=t&42;if(r!==0)return r;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function _e(t,r,s){var c=t.pendingLanes;if(c===0)return 0;var h=0,p=t.suspendedLanes,C=t.pingedLanes;t=t.warmLanes;var R=c&134217727;return R!==0?(c=R&~p,c!==0?h=Ee(c):(C&=R,C!==0?h=Ee(C):s||(s=R&~t,s!==0&&(h=Ee(s))))):(R=c&~p,R!==0?h=Ee(R):C!==0?h=Ee(C):s||(s=c&~t,s!==0&&(h=Ee(s)))),h===0?0:r!==0&&r!==h&&(r&p)===0&&(p=h&-h,s=r&-r,p>=s||p===32&&(s&4194048)!==0)?r:h}function Ke(t,r){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&r)===0}function xe(t,r){switch(t){case 1:case 2:case 4:case 8:case 64:return r+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return r+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Ie(){var t=ue;return ue<<=1,(ue&62914560)===0&&(ue=4194304),t}function Re(t){for(var r=[],s=0;31>s;s++)r.push(t);return r}function et(t,r){t.pendingLanes|=r,r!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function je(t,r,s,c,h,p){var C=t.pendingLanes;t.pendingLanes=s,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=s,t.entangledLanes&=s,t.errorRecoveryDisabledLanes&=s,t.shellSuspendCounter=0;var R=t.entanglements,B=t.expirationTimes,Y=t.hiddenUpdates;for(s=C&~s;0<s;){var ae=31-on(s),ce=1<<ae;R[ae]=0,B[ae]=-1;var Q=Y[ae];if(Q!==null)for(Y[ae]=null,ae=0;ae<Q.length;ae++){var ne=Q[ae];ne!==null&&(ne.lane&=-536870913)}s&=~ce}c!==0&&De(t,c,0),p!==0&&h===0&&t.tag!==0&&(t.suspendedLanes|=p&~(C&~r))}function De(t,r,s){t.pendingLanes|=r,t.suspendedLanes&=~r;var c=31-on(r);t.entangledLanes|=r,t.entanglements[c]=t.entanglements[c]|1073741824|s&261930}function Te(t,r){var s=t.entangledLanes|=r;for(t=t.entanglements;s;){var c=31-on(s),h=1<<c;h&r|t[c]&r&&(t[c]|=r),s&=~h}}function Ue(t,r){var s=r&-r;return s=(s&42)!==0?1:gt(s),(s&(t.suspendedLanes|r))!==0?0:s}function gt(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function St(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function ln(){var t=de.p;return t!==0?t:(t=window.event,t===void 0?32:DS(t.type))}function cn(t,r){var s=de.p;try{return de.p=t,r()}finally{de.p=s}}var Rn=Math.random().toString(36).slice(2),un="__reactFiber$"+Rn,kn="__reactProps$"+Rn,Da="__reactContainer$"+Rn,Lo="__reactEvents$"+Rn,Sf="__reactListeners$"+Rn,_c="__reactHandles$"+Rn,Br="__reactResources$"+Rn,vi="__reactMarker$"+Rn;function Mo(t){delete t[un],delete t[kn],delete t[Lo],delete t[Sf],delete t[_c]}function Oa(t){var r=t[un];if(r)return r;for(var s=t.parentNode;s;){if(r=s[Da]||s[un]){if(s=r.alternate,r.child!==null||s!==null&&s.child!==null)for(t=gS(t);t!==null;){if(s=t[un])return s;t=gS(t)}return r}t=s,s=t.parentNode}return null}function Pr(t){if(t=t[un]||t[Da]){var r=t.tag;if(r===5||r===6||r===13||r===31||r===26||r===27||r===3)return t}return null}function ra(t){var r=t.tag;if(r===5||r===26||r===27||r===6)return t.stateNode;throw Error(a(33))}function La(t){var r=t[Br];return r||(r=t[Br]={hoistableStyles:new Map,hoistableScripts:new Map}),r}function Xt(t){t[vi]=!0}var Ac=new Set,xc={};function Ar(t,r){Ma(t,r),Ma(t+"Capture",r)}function Ma(t,r){for(xc[t]=r,t=0;t<r.length;t++)Ac.add(r[t])}var wf=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u2
Line truncated
|
||||
`).replace(zx,"")}function aS(t,r){return r=rS(r),rS(t)===r}function kt(t,r,s,c,h,p){switch(s){case"children":typeof c=="string"?r==="body"||r==="textarea"&&c===""||Ha(t,c):(typeof c=="number"||typeof c=="bigint")&&r!=="body"&&Ha(t,""+c);break;case"className":Si(t,"class",c);break;case"tabIndex":Si(t,"tabindex",c);break;case"dir":case"role":case"viewBox":case"width":case"height":Si(t,s,c);break;case"style":Mc(t,c,p);break;case"data":if(r!=="object"){Si(t,"data",c);break}case"src":case"href":if(c===""&&(r!=="a"||s!=="href")){t.removeAttribute(s);break}if(c==null||typeof c=="function"||typeof c=="symbol"||typeof c=="boolean"){t.removeAttribute(s);break}c=ar(""+c),t.setAttribute(s,c);break;case"action":case"formAction":if(typeof c=="function"){t.setAttribute(s,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof p=="function"&&(s==="formAction"?(r!=="input"&&kt(t,r,"name",h.name,h,null),kt(t,r,"formEncType",h.formEncType,h,null),kt(t,r,"formMethod",h.formMethod,h,null),kt(t,r,"formTarget",h.formTarget,h,null)):(kt(t,r,"encType",h.encType,h,null),kt(t,r,"method",h.method,h,null),kt(t,r,"target",h.target,h,null)));if(c==null||typeof c=="symbol"||typeof c=="boolean"){t.removeAttribute(s);break}c=ar(""+c),t.setAttribute(s,c);break;case"onClick":c!=null&&(t.onclick=xr);break;case"onScroll":c!=null&&at("scroll",t);break;case"onScrollEnd":c!=null&&at("scrollend",t);break;case"dangerouslySetInnerHTML":if(c!=null){if(typeof c!="object"||!("__html"in c))throw Error(a(61));if(s=c.__html,s!=null){if(h.children!=null)throw Error(a(60));t.innerHTML=s}}break;case"multiple":t.multiple=c&&typeof c!="function"&&typeof c!="symbol";break;case"muted":t.muted=c&&typeof c!="function"&&typeof c!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(c==null||typeof c=="function"||typeof c=="boolean"||typeof c=="symbol"){t.removeAttribute("xlink:href");break}s=ar(""+c),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",s);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":c!=null&&typeof c!="function"&&typeof c!="symbol"?t.setAttribute(s,""+c):t.removeAttribute(s);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":c&&typeof c!="function"&&typeof c!="symbol"?t.setAttribute(s,""):t.removeAttribute(s);break;case"capture":case"download":c===!0?t.setAttribute(s,""):c!==!1&&c!=null&&typeof c!="function"&&typeof c!="symbol"?t.setAttribute(s,c):t.removeAttribute(s);break;case"cols":case"rows":case"size":case"span":c!=null&&typeof c!="function"&&typeof c!="symbol"&&!isNaN(c)&&1<=c?t.setAttribute(s,c):t.removeAttribute(s);break;case"rowSpan":case"start":c==null||typeof c=="function"||typeof c=="symbol"||isNaN(c)?t.removeAttribute(s):t.setAttribute(s,c);break;case"popover":at("beforetoggle",t),at("toggle",t),zn(t,"popover",c);break;case"xlinkActuate":vn(t,"http://www.w3.org/1999/xlink","xlink:actuate",c);break;case"xlinkArcrole":vn(t,"http://www.w3.org/1999/xlink","xlink:arcrole",c);break;case"xlinkRole":vn(t,"http://www.w3.org/1999/xlink","xlink:role",c);break;case"xlinkShow":vn(t,"http://www.w3.org/1999/xlink","xlink:show",c);break;case"xlinkTitle":vn(t,"http://www.w3.org/1999/xlink","xlink:title",c);break;case"xlinkType":vn(t,"http://www.w3.org/1999/xlink","xlink:type",c);break;case"xmlBase":vn(t,"http://www.w3.org/XML/1998/namespace","xml:base",c);break;case"xmlLang":vn(t,"http://www.w3.org/XML/1998/namespace","xml:lang",c);break;case"xmlSpace":vn(t,"http://www.w3.org/XML/1998/namespace","xml:space",c);break;case"is":zn(t,"is",c);break;case"innerText":case"textContent":break;default:(!(2<s.length)||s[0]!=="o"&&s[0]!=="O"||s[1]!=="n"&&s[1]!=="N")&&(s=xf.get(s)||s,zn(t,s,c))}}function Sg(t,r,s,c,h,p){switch(s){case"style":Mc(t,c,p);break;case"dangerouslySetInnerHTML":if(c!=null){if(typeof c!="object"||!("__html"in c))throw Error(a(61));if(s=c.__html,s!=null){if(h.children!=null)throw Error(a(60));t.innerHTML=s}}break;case"children":typeof c=="string"?Ha(t,c):(typeof c=="number"||typeof c=="bigint")&&Ha(t,""+c);break;case"onScroll":c!=null&&at("scroll",t);break;case"onScrollEnd":c!=null&&at("scrollend",t);break;case"onClick":c!=null&&(t.onclick=xr);break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"inne
Line truncated
|
||||
`).forEach(function(u){i=u.indexOf(":"),n=u.substring(0,i).trim().toLowerCase(),a=u.substring(i+1).trim(),!(!n||e[n]&&a0[n])&&(n==="set-cookie"?e[n]?e[n].push(a):e[n]=[a]:e[n]=e[n]?e[n]+", "+a:a)}),e};function s0(o){let e=0,n=o.length;for(;e<n;){const a=o.charCodeAt(e);if(a!==9&&a!==32)break;e+=1}for(;n>e;){const a=o.charCodeAt(n-1);if(a!==9&&a!==32)break;n-=1}return e===0&&n===o.length?o:o.slice(e,n)}const o0=new RegExp("[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+","g"),l0=new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+","g");function yd(o,e){return F.isArray(o)?o.map(n=>yd(n,e)):s0(String(o).replace(e,""))}const c0=o=>yd(o,o0),u0=o=>yd(o,l0);function kp(o){const e=Object.create(null);return F.forEach(o.toJSON(),(n,a)=>{e[a]=u0(n)}),e}const Dp=Symbol("internals");function so(o){return o&&String(o).trim().toLowerCase()}function Bl(o){return o===!1||o==null?o:F.isArray(o)?o.map(Bl):c0(String(o))}function d0(o){const e=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let a;for(;a=n.exec(o);)e[a[1]]=a[2];return e}const f0=o=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(o.trim());function bd(o,e,n,a,i){if(F.isFunction(a))return a.call(this,e,n);if(i&&(e=n),!!F.isString(e)){if(F.isString(a))return e.indexOf(a)!==-1;if(F.isRegExp(a))return a.test(e)}}function h0(o){return o.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,n,a)=>n.toUpperCase()+a)}function g0(o,e){const n=F.toCamelCase(" "+e);["get","set","has"].forEach(a=>{Object.defineProperty(o,a+n,{__proto__:null,value:function(i,l,u){return this[a].call(this,e,i,l,u)},configurable:!0})})}let An=class{constructor(e){e&&this.set(e)}set(e,n,a){const i=this;function l(d,f,g){const m=so(f);if(!m)return;const b=F.findKey(i,m);(!b||i[b]===void 0||g===!0||g===void 0&&i[b]!==!1)&&(i[b||f]=Bl(d))}const u=(d,f)=>F.forEach(d,(g,m)=>l(g,m,f));if(F.isPlainObject(e)||e instanceof this.constructor)u(e,n);else if(F.isString(e)&&(e=e.trim())&&!f0(e))u(i0(e),n);else if(F.isObject(e)&&F.isSafeIterable(e)){let d=Object.create(null),f,g;for(const m of e){if(!F.isArray(m))throw new TypeError("Object iterator must return a key-value pair");g=m[0],F.hasOwnProp(d,g)?(f=d[g],d[g]=F.isArray(f)?[...f,m[1]]:[f,m[1]]):d[g]=m[1]}u(d,n)}else e!=null&&l(n,e,a);return this}get(e,n){if(e=so(e),e){const a=F.findKey(this,e);if(a){const i=this[a];if(!n)return i;if(n===!0)return d0(i);if(F.isFunction(n))return n.call(this,i,a);if(F.isRegExp(n))return n.exec(i);throw new TypeError("parser must be boolean|regexp|function")}}}has(e,n){if(e=so(e),e){const a=F.findKey(this,e);return!!(a&&this[a]!==void 0&&(!n||bd(this,this[a],a,n)))}return!1}delete(e,n){const a=this;let i=!1;function l(u){if(u=so(u),u){const d=F.findKey(a,u);d&&(!n||bd(a,a[d],d,n))&&(delete a[d],i=!0)}}return F.isArray(e)?e.forEach(l):l(e),i}clear(e){const n=Object.keys(this);let a=n.length,i=!1;for(;a--;){const l=n[a];(!e||bd(this,this[l],l,e,!0))&&(delete this[l],i=!0)}return i}normalize(e){const n=this,a={};return F.forEach(this,(i,l)=>{const u=F.findKey(a,l);if(u){n[u]=Bl(i),delete n[l];return}const d=e?h0(l):String(l).trim();d!==l&&delete n[l],n[d]=Bl(i),a[d]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){const n=Object.create(null);return F.forEach(this,(a,i)=>{a!=null&&a!==!1&&(n[i]=e&&F.isArray(a)?a.join(", "):a)}),n}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,n])=>e+": "+n).join(`
|
||||
`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static concat(e,...n){const a=new this(e);return n.forEach(i=>a.set(i)),a}static accessor(e){const a=(this[Dp]=this[Dp]={accessors:{}}).accessors,i=this.prototype;function l(u){const d=so(u);a[d]||(g0(i,u),a[d]=!0)}return F.isArray(e)?e.forEach(l):l(e),this}};An.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]),F.reduceDescriptors(An.prototype,({value:o},e)=>{let n=e[0].toUpperCase()+e.slice(1);return{get:()=>o,set(a){this[n]=a}}}),F.freezeMethods(An);const p0="[REDACTED ****]";function m0(o){if(F.hasOwnProp(o,"toJSON"))return!0;let e=Object.getPrototypeOf(o);for(;e&&e!==Object.prototype;){if(F.hasOwnProp(e,"toJSON"))return!0;e=Object.getPrototypeOf(e)}return!1}function y0(o,e){const n=new Set(e.map(l=>String(l).toLowerCase())),a=[],i=l=>{if(l===null||typeof l!="object"||F.isBuffer(l))return l;if(a.indexOf(l)!==-1)return;l instanceof An&&(l=l.toJSON()),a.push(l);let u;if(F.isArray(l))u=[],l.forEach((d,f)=>{const g=i(d);F.isUndefined(g)||(u[f]=g)});else{if(!F.isPlainObject(l)&&m0(l))return a.pop(),l;u=Object.create(null);for(const[d,f]of Object.entries(l)){const g=n.has(d.toLowerCase())?p0:i(f);F.isUndefined(g)||(u[d]=g)}}return a.pop(),u};return i(o)}let Se=class HS extends Error{static from(e,n,a,i,l,u){const d=new HS(e.message,n||e.code,a,i,l);return Object.defineProperty(d,"cause",{__proto__:null,value:e,writable:!0,enumerable:!1,configurable:!0}),d.name=e.name,e.status!=null&&d.status==null&&(d.status=e.status),u&&Object.assign(d,u),d}constructor(e,n,a,i,l){super(e),Object.defineProperty(this,"message",{__proto__:null,value:e,enumerable:!0,writable:!0,configurable:!0}),this.name="AxiosError",this.isAxiosError=!0,n&&(this.code=n),a&&(this.config=a),i&&(this.request=i),l&&(this.response=l,this.status=l.status)}toJSON(){const e=this.config,n=e&&F.hasOwnProp(e,"redact")?e.redact:void 0,a=F.isArray(n)&&n.length>0?y0(e,n):F.toJSONObject(e);return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:a,code:this.code,status:this.status}}};Se.ERR_BAD_OPTION_VALUE="ERR_BAD_OPTION_VALUE",Se.ERR_BAD_OPTION="ERR_BAD_OPTION",Se.ECONNABORTED="ECONNABORTED",Se.ETIMEDOUT="ETIMEDOUT",Se.ECONNREFUSED="ECONNREFUSED",Se.ERR_NETWORK="ERR_NETWORK",Se.ERR_FR_TOO_MANY_REDIRECTS="ERR_FR_TOO_MANY_REDIRECTS",Se.ERR_DEPRECATED="ERR_DEPRECATED",Se.ERR_BAD_RESPONSE="ERR_BAD_RESPONSE",Se.ERR_BAD_REQUEST="ERR_BAD_REQUEST",Se.ERR_CANCELED="ERR_CANCELED",Se.ERR_NOT_SUPPORT="ERR_NOT_SUPPORT",Se.ERR_INVALID_URL="ERR_INVALID_URL",Se.ERR_FORM_DATA_DEPTH_EXCEEDED="ERR_FORM_DATA_DEPTH_EXCEEDED";const b0=null,Op=100;function vd(o){return F.isPlainObject(o)||F.isArray(o)}function Lp(o){return F.endsWith(o,"[]")?o.slice(0,-2):o}function Sd(o,e,n){return o?o.concat(e).map(function(i,l){return i=Lp(i),!n&&l?"["+i+"]":i}).join(n?".":""):e}function v0(o){return F.isArray(o)&&!o.some(vd)}const S0=F.toFlatObject(F,{},null,function(e){return/^is[A-Z]/.test(e)});function Pl(o,e,n){if(!F.isObject(o))throw new TypeError("target must be an object");e=e||new FormData,n=F.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(M,x){return!F.isUndefined(x[M])});const a=n.metaTokens,i=n.visitor||_,l=n.dots,u=n.indexes,d=n.Blob||typeof Blob<"u"&&Blob,f=n.maxDepth===void 0?Op:n.maxDepth,g=d&&F.isSpecCompliantForm(e),m=[];if(!F.isFunction(i))throw new TypeError("visitor must be a function");function b(O){if(O===null)return"";if(F.isDate(O))return O.toISOString();if(F.isBoolean(O))return O.toString();if(!g&&F.isBlob(O))throw new Se("Blob is not supported. Use a Buffer instead.");if(F.isArrayBuffer(O)||F.isTypedArray(O)){if(g&&typeof d=="function")return new d([O]);if(typeof Buffer<"u")return Buffer.from(O);throw new Se("Blob is not supported. Use a Buffer instead.",Se.ERR_NOT_SUPPORT)}return O}function v(O){if(O>f)throw new Se("Object is too deeply nested ("+O+" levels). Max depth: "+f,Se.ERR_FORM_DATA_DEPTH_EXCEEDED)}function S(O,M){if(f===1/0)return JSON.stringify(O);const x=[];return JSON.stringify(O,function(U,z){if(!F.isObject(z))return z;for(;x.length&&x[x.length-1]!==this;)x.pop();return x.push(z),v(M+x.length-1),z})}function _(O,M,x){let D=O;if(F.isReactNative(e)&&F.isReactNativeBlob(O))return e.append(Sd(x,M,l),b(O)),!1;if(O&&!x&&typeof O=="object"){if(F.endsWith(M,"{}"))M=a?M:M.slice(0,-2),O=S(O,1);else if(F.isArray(O)&&v0(O)||(F.isFileList(O)||F.endsWith(M,"[]"))&&(D=F.toArray(O)))return M=Lp(M),D.forEach(function(z,q){!(F.isUndefined(z)||z===null)&&e.append(u===!0?Sd([M],q,l):u===null?M:M+"[]",b(z))}),!1}return vd(O)?!0:(e.append(Sd(x,M,l),b(O)),!1)}const A=Object.assign(S0,{defaultVisitor:_,convertValue:b,isVisitable:vd});function k(O,M,x=0){if(!F.isUndefined(O)){if(v(x),m.indexOf(O)!==-1)throw new Error("Circula
Line truncated
|
||||
`+u.map(Zp).join(`
|
||||
`):" "+Zp(u[0]):"as no adapter specified";throw new Se("There is no suitable adapter to dispatch the request "+d,Se.ERR_NOT_SUPPORT)}return i}const em={getAdapter:nC,adapters:Td};function Rd(o){if(o.cancelToken&&o.cancelToken.throwIfRequested(),o.signal&&o.signal.aborted)throw new lo(null,o)}function tm(o){return Rd(o),o.headers=An.from(o.headers),o.data=Ad.call(o,o.transformRequest),["post","put","patch"].indexOf(o.method)!==-1&&o.headers.setContentType("application/x-www-form-urlencoded",!1),em.getAdapter(o.adapter||oo.adapter,o)(o).then(function(a){Rd(o),o.response=a;try{a.data=Ad.call(o,o.transformResponse,a)}finally{delete o.response}return a.headers=An.from(a.headers),a},function(a){if(!Up(a)&&(Rd(o),a&&a.response)){o.response=a.response;try{a.response.data=Ad.call(o,o.transformResponse,a.response)}finally{delete o.response}a.response.headers=An.from(a.response.headers)}return Promise.reject(a)})}const Fl={};["object","boolean","number","function","string","symbol"].forEach((o,e)=>{Fl[o]=function(a){return typeof a===o||"a"+(e<1?"n ":" ")+o}});const nm={};Fl.transitional=function(e,n,a){function i(l,u){return"[Axios v"+xd+"] Transitional option '"+l+"'"+u+(a?". "+a:"")}return(l,u,d)=>{if(e===!1)throw new Se(i(u," has been removed"+(n?" in "+n:"")),Se.ERR_DEPRECATED);return n&&!nm[u]&&(nm[u]=!0,console.warn(i(u," has been deprecated since v"+n+" and will be removed in the near future"))),e?e(l,u,d):!0}},Fl.spelling=function(e){return(n,a)=>(console.warn(`${a} is likely a misspelling of ${e}`),!0)};function rC(o,e,n){if(typeof o!="object"||o===null)throw new Se("options must be an object",Se.ERR_BAD_OPTION_VALUE);const a=Object.keys(o);let i=a.length;for(;i-- >0;){const l=a[i],u=Object.prototype.hasOwnProperty.call(e,l)?e[l]:void 0;if(u){const d=o[l],f=d===void 0||u(d,l,o);if(f!==!0)throw new Se("option "+l+" must be "+f,Se.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new Se("Unknown option "+l,Se.ERR_BAD_OPTION)}}const Kl={assertOptions:rC,validators:Fl},xn=Kl.validators;let hi=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Ip,response:new Ip}}async request(e,n){try{return await this._request(e,n)}catch(a){if(a instanceof Error){let i={};Error.captureStackTrace?Error.captureStackTrace(i):i=new Error;const l=(()=>{if(!i.stack)return"";const u=i.stack.indexOf(`
|
||||
`);return u===-1?"":i.stack.slice(u+1)})();try{if(!a.stack)a.stack=l;else if(l){const u=l.indexOf(`
|
||||
`),d=u===-1?-1:l.indexOf(`
|
||||
`,u+1),f=d===-1?"":l.slice(d+1);String(a.stack).endsWith(f)||(a.stack+=`
|
||||
`+l)}}catch{}}throw a}}_request(e,n){typeof e=="string"?(n=n||{},n.url=e):n=e||{},n=fi(this.defaults,n);const{transitional:a,paramsSerializer:i,headers:l}=n;a!==void 0&&Kl.assertOptions(a,{silentJSONParsing:xn.transitional(xn.boolean),forcedJSONParsing:xn.transitional(xn.boolean),clarifyTimeoutError:xn.transitional(xn.boolean),legacyInterceptorReqResOrdering:xn.transitional(xn.boolean),advertiseZstdAcceptEncoding:xn.transitional(xn.boolean),validateStatusUndefinedResolves:xn.transitional(xn.boolean)},!1),i!=null&&(F.isFunction(i)?n.paramsSerializer={serialize:i}:Kl.assertOptions(i,{encode:xn.function,serialize:xn.function},!0)),n.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?n.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:n.allowAbsoluteUrls=!0),Kl.assertOptions(n,{baseUrl:xn.spelling("baseURL"),withXsrfToken:xn.spelling("withXSRFToken")},!0),n.method=(n.method||this.defaults.method||"get").toLowerCase();let u=l&&F.merge(l.common,l[n.method]);l&&F.forEach(["delete","get","head","post","put","patch","query","common"],_=>{delete l[_]}),n.headers=An.concat(u,l);const d=[];let f=!0;this.interceptors.request.forEach(function(A){if(typeof A.runWhen=="function"&&A.runWhen(n)===!1)return;f=f&&A.synchronous;const k=n.transitional||Cd;k&&k.legacyInterceptorReqResOrdering?d.unshift(A.fulfilled,A.rejected):d.push(A.fulfilled,A.rejected)});const g=[];this.interceptors.response.forEach(function(A){g.push(A.fulfilled,A.rejected)});let m,b=0,v;if(!f){const _=[tm.bind(this),void 0];for(_.unshift(...d),_.push(...g),v=_.length,m=Promise.resolve(n);b<v;)m=m.then(_[b++],_[b++]);return m}v=d.length;let S=n;for(;b<v;){const _=d[b++],A=d[b++];try{S=_(S)}catch(k){A.call(this,k);break}}try{m=tm.call(this,S)}catch(_){return Promise.reject(_)}for(b=0,v=g.length;b<v;)m=m.then(g[b++],g[b++]);return m}getUri(e){e=fi(this.defaults,e);const n=Vp(e.baseURL,e.url,e.allowAbsoluteUrls,e);return Np(n,e.params,e.paramsSerializer)}};F.forEach(["delete","get","head","options"],function(e){hi.prototype[e]=function(n,a){return this.request(fi(a||{},{method:e,url:n,data:a&&F.hasOwnProp(a,"data")?a.data:void 0}))}}),F.forEach(["post","put","patch","query"],function(e){function n(a){return function(l,u,d){return this.request(fi(d||{},{method:e,headers:a?{"Content-Type":"multipart/form-data"}:{},url:l,data:u}))}}hi.prototype[e]=n(),e!=="query"&&(hi.prototype[e+"Form"]=n(!0))});let aC=class zS{constructor(e){if(typeof e!="function")throw new TypeError("executor must be a function.");let n;this.promise=new Promise(function(l){n=l});const a=this;this.promise.then(i=>{if(!a._listeners)return;let l=a._listeners.length;for(;l-- >0;)a._listeners[l](i);a._listeners=null}),this.promise.then=i=>{let l;const u=new Promise(d=>{a.subscribe(d),l=d}).then(i);return u.cancel=function(){a.unsubscribe(l)},u},e(function(l,u,d){a.reason||(a.reason=new lo(l,u,d),n(a.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;const n=this._listeners.indexOf(e);n!==-1&&this._listeners.splice(n,1)}toAbortSignal(){const e=new AbortController,n=a=>{e.abort(a)};return this.subscribe(n),e.signal.unsubscribe=()=>this.unsubscribe(n),e.signal}static source(){let e;return{token:new zS(function(i){e=i}),cancel:e}}};function iC(o){return function(n){return o.apply(null,n)}}function sC(o){return F.isObject(o)&&o.isAxiosError===!0}const kd={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(kd).forEach(([o,e])=>{kd[e]=o});function rm(o){const e=new hi(o),n=bp(hi.prototype.request,e);return F.extend(n,hi.prototype,e,{allOwnKeys:!0}),F.exte
Line truncated
|
||||
`+ot+t+Ot}var _t=!1;function Oe(t,r){if(!t||_t)return"";_t=!0;var s=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var u={DetermineComponentFrameRoot:function(){try{if(r){var le=function(){throw Error()};if(Object.defineProperty(le.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(le,[])}catch(ne){var Q=ne}Reflect.construct(t,[],le)}else{try{le.call()}catch(ne){Q=ne}t.call(le.prototype)}}else{try{throw Error()}catch(ne){Q=ne}(le=t())&&typeof le.catch=="function"&&le.catch(function(){})}}catch(ne){if(ne&&Q&&typeof ne.stack=="string")return[ne.stack,Q.stack]}return[null,null]}};u.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var h=Object.getOwnPropertyDescriptor(u.DetermineComponentFrameRoot,"name");h&&h.configurable&&Object.defineProperty(u.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var p=u.DetermineComponentFrameRoot(),C=p[0],k=p[1];if(C&&k){var U=C.split(`
|
||||
`),X=k.split(`
|
||||
`);for(h=u=0;u<U.length&&!U[u].includes("DetermineComponentFrameRoot");)u++;for(;h<X.length&&!X[h].includes("DetermineComponentFrameRoot");)h++;if(u===U.length||h===X.length)for(u=U.length-1,h=X.length-1;1<=u&&0<=h&&U[u]!==X[h];)h--;for(;1<=u&&0<=h;u--,h--)if(U[u]!==X[h]){if(u!==1||h!==1)do if(u--,h--,0>h||U[u]!==X[h]){var ae=`
|
||||
`+U[u].replace(" at new "," at ");return t.displayName&&ae.includes("<anonymous>")&&(ae=ae.replace("<anonymous>",t.displayName)),ae}while(1<=u&&0<=h);break}}}finally{_t=!1,Error.prepareStackTrace=s}return(s=t?t.displayName||t.name:"")?vt(s):""}function nt(t,r){switch(t.tag){case 26:case 27:case 5:return vt(t.type);case 16:return vt("Lazy");case 13:return t.child!==r&&r!==null?vt("Suspense Fallback"):vt("Suspense");case 19:return vt("SuspenseList");case 0:case 15:return Oe(t.type,!1);case 11:return Oe(t.type.render,!1);case 1:return Oe(t.type,!0);case 31:return vt("Activity");default:return""}}function sn(t){try{var r="",s=null;do r+=nt(t,s),s=t,t=t.return;while(t);return r}catch(u){return`
|
||||
Error generating stack: `+u.message+`
|
||||
`+u.stack}}var Mt=Object.prototype.hasOwnProperty,Vt=o.unstable_scheduleCallback,on=o.unstable_cancelCallback,ln=o.unstable_shouldYield,Br=o.unstable_requestPaint,Ft=o.unstable_now,Ci=o.unstable_getCurrentPriorityLevel,na=o.unstable_ImmediatePriority,yn=o.unstable_UserBlockingPriority,On=o.unstable_NormalPriority,rt=o.unstable_LowPriority,jn=o.unstable_IdlePriority,bn=o.log,ra=o.unstable_setDisableYieldValue,sr=null,cn=null;function Tn(t){if(typeof bn=="function"&&ra(t),cn&&typeof cn.setStrictMode=="function")try{cn.setStrictMode(sr,t)}catch{}}var un=Math.clz32?Math.clz32:ce,V=Math.log,se=Math.LN2;function ce(t){return t>>>=0,t===0?32:31-(V(t)/se|0)|0}var Z=256,ve=262144,ge=4194304;function we(t){var r=t&42;if(r!==0)return r;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function Ae(t,r,s){var u=t.pendingLanes;if(u===0)return 0;var h=0,p=t.suspendedLanes,C=t.pingedLanes;t=t.warmLanes;var k=u&134217727;return k!==0?(u=k&~p,u!==0?h=we(u):(C&=k,C!==0?h=we(C):s||(s=k&~t,s!==0&&(h=we(s))))):(k=u&~p,k!==0?h=we(k):C!==0?h=we(C):s||(s=u&~t,s!==0&&(h=we(s)))),h===0?0:r!==0&&r!==h&&(r&p)===0&&(p=h&-h,s=r&-r,p>=s||p===32&&(s&4194048)!==0)?r:h}function Ke(t,r){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&r)===0}function Te(t,r){switch(t){case 1:case 2:case 4:case 8:case 64:return r+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return r+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function $e(){var t=ge;return ge<<=1,(ge&62914560)===0&&(ge=4194304),t}function Re(t){for(var r=[],s=0;31>s;s++)r.push(t);return r}function et(t,r){t.pendingLanes|=r,r!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function je(t,r,s,u,h,p){var C=t.pendingLanes;t.pendingLanes=s,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=s,t.entangledLanes&=s,t.errorRecoveryDisabledLanes&=s,t.shellSuspendCounter=0;var k=t.entanglements,U=t.expirationTimes,X=t.hiddenUpdates;for(s=C&~s;0<s;){var ae=31-un(s),le=1<<ae;k[ae]=0,U[ae]=-1;var Q=X[ae];if(Q!==null)for(X[ae]=null,ae=0;ae<Q.length;ae++){var ne=Q[ae];ne!==null&&(ne.lane&=-536870913)}s&=~le}u!==0&&Ve(t,u,0),p!==0&&h===0&&t.tag!==0&&(t.suspendedLanes|=p&~(C&~r))}function Ve(t,r,s){t.pendingLanes|=r,t.suspendedLanes&=~r;var u=31-un(r);t.entangledLanes|=r,t.entanglements[u]=t.entanglements[u]|1073741824|s&261930}function Se(t,r){var s=t.entangledLanes|=r;for(t=t.entanglements;s;){var u=31-un(s),h=1<<u;h&r|t[u]&r&&(t[u]|=r),s&=~h}}function He(t,r){var s=r&-r;return s=(s&42)!==0?1:tt(s),(s&(t.suspendedLanes|r))!==0?0:s}function tt(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function Tt(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function It(){var t=ue.p;return t!==0?t:(t=window.event,t===void 0?32:US(t.type))}function Ln(t,r){var s=ue.p;try{return ue.p=t,r()}finally{ue.p=s}}var zt=Math.random().toString(36).slice(2),Ut="__reactFiber$"+zt,Rn="__reactProps$"+zt,$a="__reactContainer$"+zt,jo="__reactEvents$"+zt,Tf="__reactListeners$"+zt,kc="__reactHandles$"+zt,qr="__reactResources$"+zt,Ei="__reactMarker$"+zt;function zo(t){delete t[Ut],delete t[Rn],delete t[jo],delete t[Tf],delete t[kc]}function Na(t){var r=t[Ut];if(r)return r;for(var s=t.parentNode;s;){if(r=s[$a]||s[Ut]){if(s=r.alternate,r.child!==null||s!==null&&s.child!==null)for(t=_S(t);t!==null;){if(s=t[Ut])return s;t=_S(t)}return r}t=s,s=t.parentNode}return null}function Gr(t){if(t=t[Ut]||t[$a]){var r=t.tag;if(r===5||r===6||r===13||r===31||r===26||r===27||r===3)return t}return null}function aa(t){var r=t.tag;if(r===5||r===26||r===27||r===6)return t.stateNode;throw Error(a(33))}function Ia(t){var r=t[qr];return r||(r=t[qr]={hoistableStyles:new Map,hoistableScripts:new Map}),r}function Zt(t){t[Ei]=!0}var Dc=new Set,Oc={};function Tr(t,r){Ha(t,r),Ha(t+"Capture",r)}function Ha(t,r){for(Oc[t]=r,t=0;t<r.length;t++)Dc.add(r[t])}var Rf=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u2
Line truncated
|
||||
`).replace(Vx,"")}function gS(t,r){return r=hS(r),hS(t)===r}function kt(t,r,s,u,h,p){switch(s){case"children":typeof u=="string"?r==="body"||r==="textarea"&&u===""||Pa(t,u):(typeof u=="number"||typeof u=="bigint")&&r!=="body"&&Pa(t,""+u);break;case"className":_i(t,"class",u);break;case"tabIndex":_i(t,"tabindex",u);break;case"dir":case"role":case"viewBox":case"width":case"height":_i(t,s,u);break;case"style":jc(t,u,p);break;case"data":if(r!=="object"){_i(t,"data",u);break}case"src":case"href":if(u===""&&(r!=="a"||s!=="href")){t.removeAttribute(s);break}if(u==null||typeof u=="function"||typeof u=="symbol"||typeof u=="boolean"){t.removeAttribute(s);break}u=or(""+u),t.setAttribute(s,u);break;case"action":case"formAction":if(typeof u=="function"){t.setAttribute(s,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof p=="function"&&(s==="formAction"?(r!=="input"&&kt(t,r,"name",h.name,h,null),kt(t,r,"formEncType",h.formEncType,h,null),kt(t,r,"formMethod",h.formMethod,h,null),kt(t,r,"formTarget",h.formTarget,h,null)):(kt(t,r,"encType",h.encType,h,null),kt(t,r,"method",h.method,h,null),kt(t,r,"target",h.target,h,null)));if(u==null||typeof u=="symbol"||typeof u=="boolean"){t.removeAttribute(s);break}u=or(""+u),t.setAttribute(s,u);break;case"onClick":u!=null&&(t.onclick=Rr);break;case"onScroll":u!=null&&it("scroll",t);break;case"onScrollEnd":u!=null&&it("scrollend",t);break;case"dangerouslySetInnerHTML":if(u!=null){if(typeof u!="object"||!("__html"in u))throw Error(a(61));if(s=u.__html,s!=null){if(h.children!=null)throw Error(a(60));t.innerHTML=s}}break;case"multiple":t.multiple=u&&typeof u!="function"&&typeof u!="symbol";break;case"muted":t.muted=u&&typeof u!="function"&&typeof u!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(u==null||typeof u=="function"||typeof u=="boolean"||typeof u=="symbol"){t.removeAttribute("xlink:href");break}s=or(""+u),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",s);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":u!=null&&typeof u!="function"&&typeof u!="symbol"?t.setAttribute(s,""+u):t.removeAttribute(s);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":u&&typeof u!="function"&&typeof u!="symbol"?t.setAttribute(s,""):t.removeAttribute(s);break;case"capture":case"download":u===!0?t.setAttribute(s,""):u!==!1&&u!=null&&typeof u!="function"&&typeof u!="symbol"?t.setAttribute(s,u):t.removeAttribute(s);break;case"cols":case"rows":case"size":case"span":u!=null&&typeof u!="function"&&typeof u!="symbol"&&!isNaN(u)&&1<=u?t.setAttribute(s,u):t.removeAttribute(s);break;case"rowSpan":case"start":u==null||typeof u=="function"||typeof u=="symbol"||isNaN(u)?t.removeAttribute(s):t.setAttribute(s,u);break;case"popover":it("beforetoggle",t),it("toggle",t),zn(t,"popover",u);break;case"xlinkActuate":vn(t,"http://www.w3.org/1999/xlink","xlink:actuate",u);break;case"xlinkArcrole":vn(t,"http://www.w3.org/1999/xlink","xlink:arcrole",u);break;case"xlinkRole":vn(t,"http://www.w3.org/1999/xlink","xlink:role",u);break;case"xlinkShow":vn(t,"http://www.w3.org/1999/xlink","xlink:show",u);break;case"xlinkTitle":vn(t,"http://www.w3.org/1999/xlink","xlink:title",u);break;case"xlinkType":vn(t,"http://www.w3.org/1999/xlink","xlink:type",u);break;case"xmlBase":vn(t,"http://www.w3.org/XML/1998/namespace","xml:base",u);break;case"xmlLang":vn(t,"http://www.w3.org/XML/1998/namespace","xml:lang",u);break;case"xmlSpace":vn(t,"http://www.w3.org/XML/1998/namespace","xml:space",u);break;case"is":zn(t,"is",u);break;case"innerText":case"textContent":break;default:(!(2<s.length)||s[0]!=="o"&&s[0]!=="O"||s[1]!=="n"&&s[1]!=="N")&&(s=Mf.get(s)||s,zn(t,s,u))}}function Tg(t,r,s,u,h,p){switch(s){case"style":jc(t,u,p);break;case"dangerouslySetInnerHTML":if(u!=null){if(typeof u!="object"||!("__html"in u))throw Error(a(61));if(s=u.__html,s!=null){if(h.children!=null)throw Error(a(60));t.innerHTML=s}}break;case"children":typeof u=="string"?Pa(t,u):(typeof u=="number"||typeof u=="bigint")&&Pa(t,""+u);break;case"onScroll":u!=null&&it("scroll",t);break;case"onScrollEnd":u!=null&&it("scrollend",t);break;case"onClick":u!=null&&(t.onclick=Rr);break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"inne
Line truncated
|
||||
`).forEach(function(c){i=c.indexOf(":"),n=c.substring(0,i).trim().toLowerCase(),a=c.substring(i+1).trim(),!(!n||e[n]&&f0[n])&&(n==="set-cookie"?e[n]?e[n].push(a):e[n]=[a]:e[n]=e[n]?e[n]+", "+a:a)}),e};function g0(o){let e=0,n=o.length;for(;e<n;){const a=o.charCodeAt(e);if(a!==9&&a!==32)break;e+=1}for(;n>e;){const a=o.charCodeAt(n-1);if(a!==9&&a!==32)break;n-=1}return e===0&&n===o.length?o:o.slice(e,n)}const p0=new RegExp("[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+","g"),m0=new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+","g");function _d(o,e){return F.isArray(o)?o.map(n=>_d(n,e)):g0(String(o).replace(e,""))}const y0=o=>_d(o,p0),b0=o=>_d(o,m0);function jp(o){const e=Object.create(null);return F.forEach(o.toJSON(),(n,a)=>{e[a]=b0(n)}),e}const zp=Symbol("internals");function ho(o){return o&&String(o).trim().toLowerCase()}function Fl(o){return o===!1||o==null?o:F.isArray(o)?o.map(Fl):y0(String(o))}function v0(o){const e=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let a;for(;a=n.exec(o);)e[a[1]]=a[2];return e}const S0=o=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(o.trim());function Ad(o,e,n,a,i){if(F.isFunction(a))return a.call(this,e,n);if(i&&(e=n),!!F.isString(e)){if(F.isString(a))return e.indexOf(a)!==-1;if(F.isRegExp(a))return a.test(e)}}function w0(o){return o.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,n,a)=>n.toUpperCase()+a)}function C0(o,e){const n=F.toCamelCase(" "+e);["get","set","has"].forEach(a=>{Object.defineProperty(o,a+n,{__proto__:null,value:function(i,l,c){return this[a].call(this,e,i,l,c)},configurable:!0})})}let An=class{constructor(e){e&&this.set(e)}set(e,n,a){const i=this;function l(d,f,g){const m=ho(f);if(!m)return;const y=F.findKey(i,m);(!y||i[y]===void 0||g===!0||g===void 0&&i[y]!==!1)&&(i[y||f]=Fl(d))}const c=(d,f)=>F.forEach(d,(g,m)=>l(g,m,f));if(F.isPlainObject(e)||e instanceof this.constructor)c(e,n);else if(F.isString(e)&&(e=e.trim())&&!S0(e))c(h0(e),n);else if(F.isObject(e)&&F.isSafeIterable(e)){let d=Object.create(null),f,g;for(const m of e){if(!F.isArray(m))throw new TypeError("Object iterator must return a key-value pair");g=m[0],F.hasOwnProp(d,g)?(f=d[g],d[g]=F.isArray(f)?[...f,m[1]]:[f,m[1]]):d[g]=m[1]}c(d,n)}else e!=null&&l(n,e,a);return this}get(e,n){if(e=ho(e),e){const a=F.findKey(this,e);if(a){const i=this[a];if(!n)return i;if(n===!0)return v0(i);if(F.isFunction(n))return n.call(this,i,a);if(F.isRegExp(n))return n.exec(i);throw new TypeError("parser must be boolean|regexp|function")}}}has(e,n){if(e=ho(e),e){const a=F.findKey(this,e);return!!(a&&this[a]!==void 0&&(!n||Ad(this,this[a],a,n)))}return!1}delete(e,n){const a=this;let i=!1;function l(c){if(c=ho(c),c){const d=F.findKey(a,c);d&&(!n||Ad(a,a[d],d,n))&&(delete a[d],i=!0)}}return F.isArray(e)?e.forEach(l):l(e),i}clear(e){const n=Object.keys(this);let a=n.length,i=!1;for(;a--;){const l=n[a];(!e||Ad(this,this[l],l,e,!0))&&(delete this[l],i=!0)}return i}normalize(e){const n=this,a={};return F.forEach(this,(i,l)=>{const c=F.findKey(a,l);if(c){n[c]=Fl(i),delete n[l];return}const d=e?w0(l):String(l).trim();d!==l&&delete n[l],n[d]=Fl(i),a[d]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){const n=Object.create(null);return F.forEach(this,(a,i)=>{a!=null&&a!==!1&&(n[i]=e&&F.isArray(a)?a.join(", "):a)}),n}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,n])=>e+": "+n).join(`
|
||||
`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static concat(e,...n){const a=new this(e);return n.forEach(i=>a.set(i)),a}static accessor(e){const a=(this[zp]=this[zp]={accessors:{}}).accessors,i=this.prototype;function l(c){const d=ho(c);a[d]||(C0(i,c),a[d]=!0)}return F.isArray(e)?e.forEach(l):l(e),this}};An.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]),F.reduceDescriptors(An.prototype,({value:o},e)=>{let n=e[0].toUpperCase()+e.slice(1);return{get:()=>o,set(a){this[n]=a}}}),F.freezeMethods(An);const E0="[REDACTED ****]";function _0(o){if(F.hasOwnProp(o,"toJSON"))return!0;let e=Object.getPrototypeOf(o);for(;e&&e!==Object.prototype;){if(F.hasOwnProp(e,"toJSON"))return!0;e=Object.getPrototypeOf(e)}return!1}function A0(o,e){const n=new Set(e.map(l=>String(l).toLowerCase())),a=[],i=l=>{if(l===null||typeof l!="object"||F.isBuffer(l))return l;if(a.indexOf(l)!==-1)return;l instanceof An&&(l=l.toJSON()),a.push(l);let c;if(F.isArray(l))c=[],l.forEach((d,f)=>{const g=i(d);F.isUndefined(g)||(c[f]=g)});else{if(!F.isPlainObject(l)&&_0(l))return a.pop(),l;c=Object.create(null);for(const[d,f]of Object.entries(l)){const g=n.has(d.toLowerCase())?E0:i(f);F.isUndefined(g)||(c[d]=g)}}return a.pop(),c};return i(o)}let Ce=class KS extends Error{static from(e,n,a,i,l,c){const d=new KS(e.message,n||e.code,a,i,l);return Object.defineProperty(d,"cause",{__proto__:null,value:e,writable:!0,enumerable:!1,configurable:!0}),d.name=e.name,e.status!=null&&d.status==null&&(d.status=e.status),c&&Object.assign(d,c),d}constructor(e,n,a,i,l){super(e),Object.defineProperty(this,"message",{__proto__:null,value:e,enumerable:!0,writable:!0,configurable:!0}),this.name="AxiosError",this.isAxiosError=!0,n&&(this.code=n),a&&(this.config=a),i&&(this.request=i),l&&(this.response=l,this.status=l.status)}toJSON(){const e=this.config,n=e&&F.hasOwnProp(e,"redact")?e.redact:void 0,a=F.isArray(n)&&n.length>0?A0(e,n):F.toJSONObject(e);return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:a,code:this.code,status:this.status}}};Ce.ERR_BAD_OPTION_VALUE="ERR_BAD_OPTION_VALUE",Ce.ERR_BAD_OPTION="ERR_BAD_OPTION",Ce.ECONNABORTED="ECONNABORTED",Ce.ETIMEDOUT="ETIMEDOUT",Ce.ECONNREFUSED="ECONNREFUSED",Ce.ERR_NETWORK="ERR_NETWORK",Ce.ERR_FR_TOO_MANY_REDIRECTS="ERR_FR_TOO_MANY_REDIRECTS",Ce.ERR_DEPRECATED="ERR_DEPRECATED",Ce.ERR_BAD_RESPONSE="ERR_BAD_RESPONSE",Ce.ERR_BAD_REQUEST="ERR_BAD_REQUEST",Ce.ERR_CANCELED="ERR_CANCELED",Ce.ERR_NOT_SUPPORT="ERR_NOT_SUPPORT",Ce.ERR_INVALID_URL="ERR_INVALID_URL",Ce.ERR_FORM_DATA_DEPTH_EXCEEDED="ERR_FORM_DATA_DEPTH_EXCEEDED";const x0=null,Up=100;function xd(o){return F.isPlainObject(o)||F.isArray(o)}function Pp(o){return F.endsWith(o,"[]")?o.slice(0,-2):o}function Td(o,e,n){return o?o.concat(e).map(function(i,l){return i=Pp(i),!n&&l?"["+i+"]":i}).join(n?".":""):e}function T0(o){return F.isArray(o)&&!o.some(xd)}const R0=F.toFlatObject(F,{},null,function(e){return/^is[A-Z]/.test(e)});function Kl(o,e,n){if(!F.isObject(o))throw new TypeError("target must be an object");e=e||new FormData,n=F.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(M,T){return!F.isUndefined(T[M])});const a=n.metaTokens,i=n.visitor||_,l=n.dots,c=n.indexes,d=n.Blob||typeof Blob<"u"&&Blob,f=n.maxDepth===void 0?Up:n.maxDepth,g=d&&F.isSpecCompliantForm(e),m=[];if(!F.isFunction(i))throw new TypeError("visitor must be a function");function y(O){if(O===null)return"";if(F.isDate(O))return O.toISOString();if(F.isBoolean(O))return O.toString();if(!g&&F.isBlob(O))throw new Ce("Blob is not supported. Use a Buffer instead.");if(F.isArrayBuffer(O)||F.isTypedArray(O)){if(g&&typeof d=="function")return new d([O]);if(typeof Buffer<"u")return Buffer.from(O);throw new Ce("Blob is not supported. Use a Buffer instead.",Ce.ERR_NOT_SUPPORT)}return O}function S(O){if(O>f)throw new Ce("Object is too deeply nested ("+O+" levels). Max depth: "+f,Ce.ERR_FORM_DATA_DEPTH_EXCEEDED)}function v(O,M){if(f===1/0)return JSON.stringify(O);const T=[];return JSON.stringify(O,function(z,P){if(!F.isObject(P))return P;for(;T.length&&T[T.length-1]!==this;)T.pop();return T.push(P),S(M+T.length-1),P})}function _(O,M,T){let D=O;if(F.isReactNative(e)&&F.isReactNativeBlob(O))return e.append(Td(T,M,l),y(O)),!1;if(O&&!T&&typeof O=="object"){if(F.endsWith(M,"{}"))M=a?M:M.slice(0,-2),O=v(O,1);else if(F.isArray(O)&&T0(O)||(F.isFileList(O)||F.endsWith(M,"[]"))&&(D=F.toArray(O)))return M=Pp(M),D.forEach(function(P,q){!(F.isUndefined(P)||P===null)&&e.append(c===!0?Td([M],q,l):c===null?M:M+"[]",y(P))}),!1}return xd(O)?!0:(e.append(Td(T,M,l),y(O)),!1)}const A=Object.assign(R0,{defaultVisitor:_,convertValue:y,isVisitable:xd});function x(O,M,T=0){if(!F.isUndefined(O)){if(S(T),m.indexOf(O)!==-1)throw new Error("Circula
Line truncated
|
||||
`+c.map(lm).join(`
|
||||
`):" "+lm(c[0]):"as no adapter specified";throw new Ce("There is no suitable adapter to dispatch the request "+d,Ce.ERR_NOT_SUPPORT)}return i}const cm={getAdapter:uC,adapters:$d};function Nd(o){if(o.cancelToken&&o.cancelToken.throwIfRequested(),o.signal&&o.signal.aborted)throw new po(null,o)}function um(o){return Nd(o),o.headers=An.from(o.headers),o.data=Ld.call(o,o.transformRequest),["post","put","patch"].indexOf(o.method)!==-1&&o.headers.setContentType("application/x-www-form-urlencoded",!1),cm.getAdapter(o.adapter||go.adapter,o)(o).then(function(a){Nd(o),o.response=a;try{a.data=Ld.call(o,o.transformResponse,a)}finally{delete o.response}return a.headers=An.from(a.headers),a},function(a){if(!Yp(a)&&(Nd(o),a&&a.response)){o.response=a.response;try{a.response.data=Ld.call(o,o.transformResponse,a.response)}finally{delete o.response}a.response.headers=An.from(a.response.headers)}return Promise.reject(a)})}const Jl={};["object","boolean","number","function","string","symbol"].forEach((o,e)=>{Jl[o]=function(a){return typeof a===o||"a"+(e<1?"n ":" ")+o}});const dm={};Jl.transitional=function(e,n,a){function i(l,c){return"[Axios v"+Md+"] Transitional option '"+l+"'"+c+(a?". "+a:"")}return(l,c,d)=>{if(e===!1)throw new Ce(i(c," has been removed"+(n?" in "+n:"")),Ce.ERR_DEPRECATED);return n&&!dm[c]&&(dm[c]=!0,console.warn(i(c," has been deprecated since v"+n+" and will be removed in the near future"))),e?e(l,c,d):!0}},Jl.spelling=function(e){return(n,a)=>(console.warn(`${a} is likely a misspelling of ${e}`),!0)};function dC(o,e,n){if(typeof o!="object"||o===null)throw new Ce("options must be an object",Ce.ERR_BAD_OPTION_VALUE);const a=Object.keys(o);let i=a.length;for(;i-- >0;){const l=a[i],c=Object.prototype.hasOwnProperty.call(e,l)?e[l]:void 0;if(c){const d=o[l],f=d===void 0||c(d,l,o);if(f!==!0)throw new Ce("option "+l+" must be "+f,Ce.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new Ce("Unknown option "+l,Ce.ERR_BAD_OPTION)}}const Ql={assertOptions:dC,validators:Jl},xn=Ql.validators;let yi=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Vp,response:new Vp}}async request(e,n){try{return await this._request(e,n)}catch(a){if(a instanceof Error){let i={};Error.captureStackTrace?Error.captureStackTrace(i):i=new Error;const l=(()=>{if(!i.stack)return"";const c=i.stack.indexOf(`
|
||||
`);return c===-1?"":i.stack.slice(c+1)})();try{if(!a.stack)a.stack=l;else if(l){const c=l.indexOf(`
|
||||
`),d=c===-1?-1:l.indexOf(`
|
||||
`,c+1),f=d===-1?"":l.slice(d+1);String(a.stack).endsWith(f)||(a.stack+=`
|
||||
`+l)}}catch{}}throw a}}_request(e,n){typeof e=="string"?(n=n||{},n.url=e):n=e||{},n=mi(this.defaults,n);const{transitional:a,paramsSerializer:i,headers:l}=n;a!==void 0&&Ql.assertOptions(a,{silentJSONParsing:xn.transitional(xn.boolean),forcedJSONParsing:xn.transitional(xn.boolean),clarifyTimeoutError:xn.transitional(xn.boolean),legacyInterceptorReqResOrdering:xn.transitional(xn.boolean),advertiseZstdAcceptEncoding:xn.transitional(xn.boolean),validateStatusUndefinedResolves:xn.transitional(xn.boolean)},!1),i!=null&&(F.isFunction(i)?n.paramsSerializer={serialize:i}:Ql.assertOptions(i,{encode:xn.function,serialize:xn.function},!0)),n.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?n.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:n.allowAbsoluteUrls=!0),Ql.assertOptions(n,{baseUrl:xn.spelling("baseURL"),withXsrfToken:xn.spelling("withXSRFToken")},!0),n.method=(n.method||this.defaults.method||"get").toLowerCase();let c=l&&F.merge(l.common,l[n.method]);l&&F.forEach(["delete","get","head","post","put","patch","query","common"],_=>{delete l[_]}),n.headers=An.concat(c,l);const d=[];let f=!0;this.interceptors.request.forEach(function(A){if(typeof A.runWhen=="function"&&A.runWhen(n)===!1)return;f=f&&A.synchronous;const x=n.transitional||kd;x&&x.legacyInterceptorReqResOrdering?d.unshift(A.fulfilled,A.rejected):d.push(A.fulfilled,A.rejected)});const g=[];this.interceptors.response.forEach(function(A){g.push(A.fulfilled,A.rejected)});let m,y=0,S;if(!f){const _=[um.bind(this),void 0];for(_.unshift(...d),_.push(...g),S=_.length,m=Promise.resolve(n);y<S;)m=m.then(_[y++],_[y++]);return m}S=d.length;let v=n;for(;y<S;){const _=d[y++],A=d[y++];try{v=_(v)}catch(x){A.call(this,x);break}}try{m=um.call(this,v)}catch(_){return Promise.reject(_)}for(y=0,S=g.length;y<S;)m=m.then(g[y++],g[y++]);return m}getUri(e){e=mi(this.defaults,e);const n=em(e.baseURL,e.url,e.allowAbsoluteUrls,e);return Gp(n,e.params,e.paramsSerializer)}};F.forEach(["delete","get","head","options"],function(e){yi.prototype[e]=function(n,a){return this.request(mi(a||{},{method:e,url:n,data:a&&F.hasOwnProp(a,"data")?a.data:void 0}))}}),F.forEach(["post","put","patch","query"],function(e){function n(a){return function(l,c,d){return this.request(mi(d||{},{method:e,headers:a?{"Content-Type":"multipart/form-data"}:{},url:l,data:c}))}}yi.prototype[e]=n(),e!=="query"&&(yi.prototype[e+"Form"]=n(!0))});let fC=class WS{constructor(e){if(typeof e!="function")throw new TypeError("executor must be a function.");let n;this.promise=new Promise(function(l){n=l});const a=this;this.promise.then(i=>{if(!a._listeners)return;let l=a._listeners.length;for(;l-- >0;)a._listeners[l](i);a._listeners=null}),this.promise.then=i=>{let l;const c=new Promise(d=>{a.subscribe(d),l=d}).then(i);return c.cancel=function(){a.unsubscribe(l)},c},e(function(l,c,d){a.reason||(a.reason=new po(l,c,d),n(a.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;const n=this._listeners.indexOf(e);n!==-1&&this._listeners.splice(n,1)}toAbortSignal(){const e=new AbortController,n=a=>{e.abort(a)};return this.subscribe(n),e.signal.unsubscribe=()=>this.unsubscribe(n),e.signal}static source(){let e;return{token:new WS(function(i){e=i}),cancel:e}}};function hC(o){return function(n){return o.apply(null,n)}}function gC(o){return F.isObject(o)&&o.isAxiosError===!0}const Id={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(Id).forEach(([o,e])=>{Id[e]=o});function fm(o){const e=new yi(o),n=Tp(yi.prototype.request,e);return F.extend(n,yi.prototype,e,{allOwnKeys:!0}),F.exte
Line truncated
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
@@ -47,11 +47,11 @@ Error generating stack: `+c.message+`
|
||||
justify-content: center;
|
||||
color: #666;
|
||||
transition: background-color 0.2s;
|
||||
`,i.onmouseenter=()=>{i.style.backgroundColor="rgba(0, 0, 0, 0.2)"},i.onmouseleave=()=>{i.style.backgroundColor="rgba(0, 0, 0, 0.1)"};const l=()=>{n.parentNode&&n.parentNode.removeChild(n)};i.onclick=l;const u=d=>{this.isImeComposing(d)||d.key==="Escape"&&(l(),document.removeEventListener("keydown",u))};return document.addEventListener("keydown",u),n.onclick=d=>{d.target===n&&l()},a.appendChild(i),n.appendChild(a),document.body.appendChild(n),{overlay:n,layer:a,closeLayer:l}}getNestedProperty(e,n){return n.split(".").reduce((a,i)=>a&&a[i]!==void 0?a[i]:void 0,e)}async handleSaveToLocalStorage(e,n){const{key:a,value:i}=e;if(!a)return j.error("saveToLocalStorage: key parameter is required"),!1;try{const l=typeof i=="string"?i:JSON.stringify(i);return localStorage.setItem(a,l),j.log(`saveToLocalStorage: saved ${a}`,i),!0}catch(l){return j.error("saveToLocalStorage: failed to save",{key:a,error:l}),!1}}async handleLoadFromLocalStorage(e,n){const{key:a,target:i,stateKey:l,defaultValue:u}=e;if(!a)return j.error("loadFromLocalStorage: key parameter is required"),u;try{const d=localStorage.getItem(a);if(d===null)return j.log(`loadFromLocalStorage: no value found for ${a}, using default`),l&&n.setState&&n.setState({[l]:u}),u;let f;try{f=JSON.parse(d)}catch{f=d}return j.log(`loadFromLocalStorage: loaded ${a}`,f),l&&n.setState&&n.setState({[l]:f}),f}catch(d){return j.error("loadFromLocalStorage: failed to load",{key:a,error:d}),u}}async handleCustomAction(e,n){const a=this.customHandlers.get(e.handler);if(!a){if(this.previewMode){j.warn(`[Preview] Unknown action handler "${e.handler}" — skipped (preview mode)`);return}const i=new jt(`Unknown action handler: ${e.handler}`,e);throw i.unknownHandler=!0,i}return await a(e,n)}resolveParams(e,n){if(!e)return{};const a={};for(const[i,l]of Object.entries(e))i.includes("{{")&&j.warn(`[resolveParams] setState params의 키에 표현식이 포함되어 있습니다: "${i}". 키는 해석되지 않습니다. 배열 항목 수정은 .map()/.filter() 패턴을 사용하세요.`),typeof l=="string"&&l.includes("{{")?a[i]=this.evaluateExpression(l,n):typeof l=="string"?a[i]=l:Array.isArray(l)?a[i]=l.map(u=>typeof u=="string"&&u.includes("{{")?this.evaluateExpression(u,n):u):typeof l=="object"&&l!==null&&!Array.isArray(l)&&Object.getPrototypeOf(l)!==Object.prototype&&Object.getPrototypeOf(l)!==null?a[i]=l:typeof l=="object"&&l!==null?a[i]=this.resolveParams(l,n):a[i]=l;return a}resolveValue(e,n){return n?this.bindingEngine.resolveBindings(e,n,{skipCache:!0}):e}evaluateExpressions(e,n){let a={};for(const[i,l]of Object.entries(e))if(i==="..."){let u=l;typeof l=="string"&&l.includes("{{")&&(u=this.evaluateExpression(l,n)),u&&typeof u=="object"&&!Array.isArray(u)&&(a={...a,...u})}else typeof l=="string"&&l.includes("{{")?a[i]=this.evaluateExpression(l,n):typeof l=="object"&&l!==null&&!Array.isArray(l)?a[i]=this.evaluateExpressions(l,n):a[i]=l;return a}evaluateExpressionsIfNeeded(e,n){const a={};for(const[i,l]of Object.entries(e))typeof l=="string"&&l.includes("{{")?a[i]=this.evaluateExpression(l,n):a[i]=l;return a}evaluateExpression(e,n){if(!n)return e;const a=e.match(/^\{\{(.+)\}\}$/);if(!(a&&!a[1].includes("}}")&&!a[1].includes("{{"))){let d=n;if(e.includes("_global")){const g=window.G7Core?.state?.get()?._global;g&&(d={...n,_global:g})}if(e.includes("_computed")||e.includes("$computed")){const g=window.__g7ActionContext?.computedRef?.current;g&&Object.keys(g).length>0&&(d={...d,_computed:g,$computed:g})}return this.bindingEngine.resolveBindings(e,d,{skipCache:!0})}let l=a[1].trim();l=l.replace(/\$args\.(\d+)/g,"$args[$1]");let u=n;if(l.includes("_global")){const f=window.G7Core?.state?.get()?._global;f&&(u={...n,_global:f})}if(l.includes("_computed")||l.includes("$computed")){const f=window.__g7ActionContext?.computedRef?.current;f&&Object.keys(f).length>0&&(u={...u,_computed:f,$computed:f})}l.includes("_global.modules")&&(j.log("[evaluateExpression] expression:",l),j.log("[evaluateExpression] dataContext._global:",u._global),j.log("[evaluateExpression] dataContext._global?.modules:",u._global?.modules));try{const d=this.bindingEngine.evaluateExpression(l,u);return l.includes("_global.modules")&&j.log("[evaluateExpression] result:",d),typeof d=="string"&&d.startsWith("$t:")&&this.translationEngine&&this.translationContext?this.translationEngine.resolveTranslations(d,this.translationContext,n):d}catch(d){return j.error("Expression evaluation failed:",e,d),e}}setDefaultContext(e){this.defaultContext={...this.defaultContext,...e}}setGlobalStateUpdater(e){this.globalStateUpdater=e}getGlobalStateUpdater(){return this.globalStateUpdater}async dispatchAction(e,n){const a={...this.defaultContext,...n,data:{...this.defaultContext.data,...n?.data}};if(e.confirm){let i=this.resolveValue(e.confirm,a.data);if(this.translationEngine&&this.translationContext&&i.startsWith("$t:")&&(i=this.translationEngine.resolveTranslations(i,this.translationContext,a.data)),!confirm(i))return{success:!1}}try
Line truncated
|
||||
`,i.onmouseenter=()=>{i.style.backgroundColor="rgba(0, 0, 0, 0.2)"},i.onmouseleave=()=>{i.style.backgroundColor="rgba(0, 0, 0, 0.1)"};const l=()=>{n.parentNode&&n.parentNode.removeChild(n)};i.onclick=l;const c=d=>{this.isImeComposing(d)||d.key==="Escape"&&(l(),document.removeEventListener("keydown",c))};return document.addEventListener("keydown",c),n.onclick=d=>{d.target===n&&l()},a.appendChild(i),n.appendChild(a),document.body.appendChild(n),{overlay:n,layer:a,closeLayer:l}}getNestedProperty(e,n){return n.split(".").reduce((a,i)=>a&&a[i]!==void 0?a[i]:void 0,e)}async handleSaveToLocalStorage(e,n){const{key:a,value:i}=e;if(!a)return j.error("saveToLocalStorage: key parameter is required"),!1;try{const l=typeof i=="string"?i:JSON.stringify(i);return localStorage.setItem(a,l),j.log(`saveToLocalStorage: saved ${a}`,i),!0}catch(l){return j.error("saveToLocalStorage: failed to save",{key:a,error:l}),!1}}async handleLoadFromLocalStorage(e,n){const{key:a,target:i,stateKey:l,defaultValue:c}=e;if(!a)return j.error("loadFromLocalStorage: key parameter is required"),c;try{const d=localStorage.getItem(a);if(d===null)return j.log(`loadFromLocalStorage: no value found for ${a}, using default`),l&&n.setState&&n.setState({[l]:c}),c;let f;try{f=JSON.parse(d)}catch{f=d}return j.log(`loadFromLocalStorage: loaded ${a}`,f),l&&n.setState&&n.setState({[l]:f}),f}catch(d){return j.error("loadFromLocalStorage: failed to load",{key:a,error:d}),c}}async handleCustomAction(e,n){const a=this.customHandlers.get(e.handler);if(!a){if(this.previewMode){j.warn(`[Preview] Unknown action handler "${e.handler}" — skipped (preview mode)`);return}const i=new Bt(`Unknown action handler: ${e.handler}`,e);throw i.unknownHandler=!0,i}return await a(e,n)}resolveParams(e,n){if(!e)return{};const a={};for(const[i,l]of Object.entries(e))i.includes("{{")&&j.warn(`[resolveParams] setState params의 키에 표현식이 포함되어 있습니다: "${i}". 키는 해석되지 않습니다. 배열 항목 수정은 .map()/.filter() 패턴을 사용하세요.`),typeof l=="string"&&l.includes("{{")?a[i]=this.evaluateExpression(l,n):typeof l=="string"?a[i]=l:Array.isArray(l)?a[i]=l.map(c=>typeof c=="string"&&c.includes("{{")?this.evaluateExpression(c,n):c):typeof l=="object"&&l!==null&&!Array.isArray(l)&&Object.getPrototypeOf(l)!==Object.prototype&&Object.getPrototypeOf(l)!==null?a[i]=l:typeof l=="object"&&l!==null?a[i]=this.resolveParams(l,n):a[i]=l;return a}resolveValue(e,n){return n?this.bindingEngine.resolveBindings(e,n,{skipCache:!0}):e}evaluateExpressions(e,n){let a={};for(const[i,l]of Object.entries(e))if(i==="..."){let c=l;typeof l=="string"&&l.includes("{{")&&(c=this.evaluateExpression(l,n)),c&&typeof c=="object"&&!Array.isArray(c)&&(a={...a,...c})}else typeof l=="string"&&l.includes("{{")?a[i]=this.evaluateExpression(l,n):typeof l=="object"&&l!==null&&!Array.isArray(l)?a[i]=this.evaluateExpressions(l,n):a[i]=l;return a}evaluateExpressionsIfNeeded(e,n){const a={};for(const[i,l]of Object.entries(e))typeof l=="string"&&l.includes("{{")?a[i]=this.evaluateExpression(l,n):a[i]=l;return a}evaluateExpression(e,n){if(!n)return e;const a=Aa(e);if(a===null){let c=n;if(e.includes("_global")){const f=window.G7Core?.state?.get()?._global;f&&(c={...n,_global:f})}if(e.includes("_computed")||e.includes("$computed")){const f=window.__g7ActionContext?.computedRef?.current;f&&Object.keys(f).length>0&&(c={...c,_computed:f,$computed:f})}return this.bindingEngine.resolveBindings(e,c,{skipCache:!0})}let i=a;i=i.replace(/\$args\.(\d+)/g,"$args[$1]");let l=n;if(i.includes("_global")){const d=window.G7Core?.state?.get()?._global;d&&(l={...n,_global:d})}if(i.includes("_computed")||i.includes("$computed")){const d=window.__g7ActionContext?.computedRef?.current;d&&Object.keys(d).length>0&&(l={...l,_computed:d,$computed:d})}i.includes("_global.modules")&&(j.log("[evaluateExpression] expression:",i),j.log("[evaluateExpression] dataContext._global:",l._global),j.log("[evaluateExpression] dataContext._global?.modules:",l._global?.modules));try{const c=$n(i)?this.bindingEngine.evaluatePipeExpression(i,l,{skipCache:!0}):this.bindingEngine.evaluateExpression(i,l);return i.includes("_global.modules")&&j.log("[evaluateExpression] result:",c),typeof c=="string"&&c.startsWith("$t:")&&this.translationEngine&&this.translationContext?this.translationEngine.resolveTranslations(c,this.translationContext,n):c}catch(c){return j.error("Expression evaluation failed:",e,c),e}}setDefaultContext(e){this.defaultContext={...this.defaultContext,...e}}setGlobalStateUpdater(e){this.globalStateUpdater=e}getGlobalStateUpdater(){return this.globalStateUpdater}async dispatchAction(e,n){const a={...this.defaultContext,...n,data:{...this.defaultContext.data,...n?.data}};if(e.confirm){let i=this.resolveValue(e.confirm,a.data);if(this.translationEngine&&this.translationContext&&i.startsWith("$t:")&&(i=this.translationEngine.resolveTranslations(i,this.translationContext,a.data)),!confirm(i))return{success:!1}}try{r
Line truncated
|
||||
To pick up a draggable item, press the space bar.
|
||||
While dragging, use the arrow keys to move the item.
|
||||
Press space again to drop the item in its new position, or press escape to cancel.
|
||||
`},JC={onDragStart(o){let{active:e}=o;return"Picked up draggable item "+e.id+"."},onDragOver(o){let{active:e,over:n}=o;return n?"Draggable item "+e.id+" was moved over droppable area "+n.id+".":"Draggable item "+e.id+" is no longer over a droppable area."},onDragEnd(o){let{active:e,over:n}=o;return n?"Draggable item "+e.id+" was dropped over droppable area "+n.id:"Draggable item "+e.id+" was dropped."},onDragCancel(o){let{active:e}=o;return"Dragging was cancelled. Draggable item "+e.id+" was dropped."}};function QC(o){let{announcements:e=JC,container:n,hiddenTextDescribedById:a,screenReaderInstructions:i=XC}=o;const{announce:l,announcement:u}=KC(),d=mo("DndLiveRegion"),[f,g]=H.useState(!1);if(H.useEffect(()=>{g(!0)},[]),WC(H.useMemo(()=>({onDragStart(b){let{active:v}=b;l(e.onDragStart({active:v}))},onDragMove(b){let{active:v,over:S}=b;e.onDragMove&&l(e.onDragMove({active:v,over:S}))},onDragOver(b){let{active:v,over:S}=b;l(e.onDragOver({active:v,over:S}))},onDragEnd(b){let{active:v,over:S}=b;l(e.onDragEnd({active:v,over:S}))},onDragCancel(b){let{active:v,over:S}=b;l(e.onDragCancel({active:v,over:S}))}}),[l,e])),!f)return null;const m=Xe.createElement(Xe.Fragment,null,Xe.createElement(VC,{id:a,value:i.draggable}),Xe.createElement(FC,{id:d,announcement:u}));return n?Aa.createPortal(m,n):m}var tn;(function(o){o.DragStart="dragStart",o.DragMove="dragMove",o.DragEnd="dragEnd",o.DragCancel="dragCancel",o.DragOver="dragOver",o.RegisterDroppable="registerDroppable",o.SetDroppableDisabled="setDroppableDisabled",o.UnregisterDroppable="unregisterDroppable"})(tn||(tn={}));function rc(){}function Em(o,e){return H.useMemo(()=>({sensor:o,options:e??{}}),[o,e])}function ZC(){for(var o=arguments.length,e=new Array(o),n=0;n<o;n++)e[n]=arguments[n];return H.useMemo(()=>[...e].filter(a=>a!=null),[...e])}const br=Object.freeze({x:0,y:0});function _m(o,e){return Math.sqrt(Math.pow(o.x-e.x,2)+Math.pow(o.y-e.y,2))}function Am(o,e){let{data:{value:n}}=o,{data:{value:a}}=e;return n-a}function eE(o,e){let{data:{value:n}}=o,{data:{value:a}}=e;return a-n}function xm(o){let{left:e,top:n,height:a,width:i}=o;return[{x:e,y:n},{x:e+i,y:n},{x:e,y:n+a},{x:e+i,y:n+a}]}function Tm(o,e){if(!o||o.length===0)return null;const[n]=o;return n[e]}function Rm(o,e,n){return e===void 0&&(e=o.left),n===void 0&&(n=o.top),{x:e+o.width*.5,y:n+o.height*.5}}const tE=o=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=o;const i=Rm(e,e.left,e.top),l=[];for(const u of a){const{id:d}=u,f=n.get(d);if(f){const g=_m(Rm(f),i);l.push({id:d,data:{droppableContainer:u,value:g}})}}return l.sort(Am)},nE=o=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=o;const i=xm(e),l=[];for(const u of a){const{id:d}=u,f=n.get(d);if(f){const g=xm(f),m=i.reduce((v,S,_)=>v+_m(g[_],S),0),b=Number((m/4).toFixed(4));l.push({id:d,data:{droppableContainer:u,value:b}})}}return l.sort(Am)};function rE(o,e){const n=Math.max(e.top,o.top),a=Math.max(e.left,o.left),i=Math.min(e.left+e.width,o.left+o.width),l=Math.min(e.top+e.height,o.top+o.height),u=i-a,d=l-n;if(a<i&&n<l){const f=e.width*e.height,g=o.width*o.height,m=u*d,b=m/(f+g-m);return Number(b.toFixed(4))}return 0}const aE=o=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=o;const i=[];for(const l of a){const{id:u}=l,d=n.get(u);if(d){const f=rE(d,e);f>0&&i.push({id:u,data:{droppableContainer:l,value:f}})}}return i.sort(eE)};function iE(o,e,n){return{...o,scaleX:e&&n?e.width/n.width:1,scaleY:e&&n?e.height/n.height:1}}function km(o,e){return o&&e?{x:o.left-e.left,y:o.top-e.top}:br}function sE(o){return function(n){for(var a=arguments.length,i=new Array(a>1?a-1:0),l=1;l<a;l++)i[l-1]=arguments[l];return i.reduce((u,d)=>({...u,top:u.top+o*d.y,bottom:u.bottom+o*d.y,left:u.left+o*d.x,right:u.right+o*d.x}),{...n})}}const oE=sE(1);function lE(o){if(o.startsWith("matrix3d(")){const e=o.slice(9,-1).split(/, /);return{x:+e[12],y:+e[13],scaleX:+e[0],scaleY:+e[5]}}else if(o.startsWith("matrix(")){const e=o.slice(7,-1).split(/, /);return{x:+e[4],y:+e[5],scaleX:+e[0],scaleY:+e[3]}}return null}function cE(o,e,n){const a=lE(e);if(!a)return o;const{scaleX:i,scaleY:l,x:u,y:d}=a,f=o.left-u-(1-i)*parseFloat(n),g=o.top-d-(1-l)*parseFloat(n.slice(n.indexOf(" ")+1)),m=i?o.width/i:o.width,b=l?o.height/l:o.height;return{width:m,height:b,top:g,right:f+m,bottom:g+b,left:f}}const uE={ignoreTransform:!1};function us(o,e){e===void 0&&(e=uE);let n=o.getBoundingClientRect();if(e.ignoreTransform){const{transform:g,transformOrigin:m}=Nn(o).getComputedStyle(o);g&&(n=cE(n,g,m))}const{top:a,left:i,width:l,height:u,bottom:d,right:f}=n;return{top:a,left:i,width:l,height:u,bottom:d,right:f}}function Dm(o){return us(o,{ignoreTransform:!0})}function dE(o){const e=o.innerWidth,n=o.innerHeight;return{top:0,left:0,right:e,bottom:n,width:e,height:n}}function fE(o,e){return e===void 0&&(e=Nn(o).getComputedStyle(o)),e.position==="fixed"}function hE(o,e){e===void 0&&(e=Nn(o).getComputedStyle(o));const n=/(auto|scroll|overlay)/;return["overflow","overflowX
Line truncated
|
||||
`},iE={onDragStart(o){let{active:e}=o;return"Picked up draggable item "+e.id+"."},onDragOver(o){let{active:e,over:n}=o;return n?"Draggable item "+e.id+" was moved over droppable area "+n.id+".":"Draggable item "+e.id+" is no longer over a droppable area."},onDragEnd(o){let{active:e,over:n}=o;return n?"Draggable item "+e.id+" was dropped over droppable area "+n.id:"Draggable item "+e.id+" was dropped."},onDragCancel(o){let{active:e}=o;return"Dragging was cancelled. Draggable item "+e.id+" was dropped."}};function sE(o){let{announcements:e=iE,container:n,hiddenTextDescribedById:a,screenReaderInstructions:i=aE}=o;const{announce:l,announcement:c}=tE(),d=Co("DndLiveRegion"),[f,g]=H.useState(!1);if(H.useEffect(()=>{g(!0)},[]),nE(H.useMemo(()=>({onDragStart(y){let{active:S}=y;l(e.onDragStart({active:S}))},onDragMove(y){let{active:S,over:v}=y;e.onDragMove&&l(e.onDragMove({active:S,over:v}))},onDragOver(y){let{active:S,over:v}=y;l(e.onDragOver({active:S,over:v}))},onDragEnd(y){let{active:S,over:v}=y;l(e.onDragEnd({active:S,over:v}))},onDragCancel(y){let{active:S,over:v}=y;l(e.onDragCancel({active:S,over:v}))}}),[l,e])),!f)return null;const m=Xe.createElement(Xe.Fragment,null,Xe.createElement(ZC,{id:a,value:i.draggable}),Xe.createElement(eE,{id:d,announcement:c}));return n?ka.createPortal(m,n):m}var an;(function(o){o.DragStart="dragStart",o.DragMove="dragMove",o.DragEnd="dragEnd",o.DragCancel="dragCancel",o.DragOver="dragOver",o.RegisterDroppable="registerDroppable",o.SetDroppableDisabled="setDroppableDisabled",o.UnregisterDroppable="unregisterDroppable"})(an||(an={}));function lc(){}function Lm(o,e){return H.useMemo(()=>({sensor:o,options:e??{}}),[o,e])}function oE(){for(var o=arguments.length,e=new Array(o),n=0;n<o;n++)e[n]=arguments[n];return H.useMemo(()=>[...e].filter(a=>a!=null),[...e])}const wr=Object.freeze({x:0,y:0});function Mm(o,e){return Math.sqrt(Math.pow(o.x-e.x,2)+Math.pow(o.y-e.y,2))}function $m(o,e){let{data:{value:n}}=o,{data:{value:a}}=e;return n-a}function lE(o,e){let{data:{value:n}}=o,{data:{value:a}}=e;return a-n}function Nm(o){let{left:e,top:n,height:a,width:i}=o;return[{x:e,y:n},{x:e+i,y:n},{x:e,y:n+a},{x:e+i,y:n+a}]}function Im(o,e){if(!o||o.length===0)return null;const[n]=o;return n[e]}function Hm(o,e,n){return e===void 0&&(e=o.left),n===void 0&&(n=o.top),{x:e+o.width*.5,y:n+o.height*.5}}const cE=o=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=o;const i=Hm(e,e.left,e.top),l=[];for(const c of a){const{id:d}=c,f=n.get(d);if(f){const g=Mm(Hm(f),i);l.push({id:d,data:{droppableContainer:c,value:g}})}}return l.sort($m)},uE=o=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=o;const i=Nm(e),l=[];for(const c of a){const{id:d}=c,f=n.get(d);if(f){const g=Nm(f),m=i.reduce((S,v,_)=>S+Mm(g[_],v),0),y=Number((m/4).toFixed(4));l.push({id:d,data:{droppableContainer:c,value:y}})}}return l.sort($m)};function dE(o,e){const n=Math.max(e.top,o.top),a=Math.max(e.left,o.left),i=Math.min(e.left+e.width,o.left+o.width),l=Math.min(e.top+e.height,o.top+o.height),c=i-a,d=l-n;if(a<i&&n<l){const f=e.width*e.height,g=o.width*o.height,m=c*d,y=m/(f+g-m);return Number(y.toFixed(4))}return 0}const fE=o=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=o;const i=[];for(const l of a){const{id:c}=l,d=n.get(c);if(d){const f=dE(d,e);f>0&&i.push({id:c,data:{droppableContainer:l,value:f}})}}return i.sort(lE)};function hE(o,e,n){return{...o,scaleX:e&&n?e.width/n.width:1,scaleY:e&&n?e.height/n.height:1}}function jm(o,e){return o&&e?{x:o.left-e.left,y:o.top-e.top}:wr}function gE(o){return function(n){for(var a=arguments.length,i=new Array(a>1?a-1:0),l=1;l<a;l++)i[l-1]=arguments[l];return i.reduce((c,d)=>({...c,top:c.top+o*d.y,bottom:c.bottom+o*d.y,left:c.left+o*d.x,right:c.right+o*d.x}),{...n})}}const pE=gE(1);function mE(o){if(o.startsWith("matrix3d(")){const e=o.slice(9,-1).split(/, /);return{x:+e[12],y:+e[13],scaleX:+e[0],scaleY:+e[5]}}else if(o.startsWith("matrix(")){const e=o.slice(7,-1).split(/, /);return{x:+e[4],y:+e[5],scaleX:+e[0],scaleY:+e[3]}}return null}function yE(o,e,n){const a=mE(e);if(!a)return o;const{scaleX:i,scaleY:l,x:c,y:d}=a,f=o.left-c-(1-i)*parseFloat(n),g=o.top-d-(1-l)*parseFloat(n.slice(n.indexOf(" ")+1)),m=i?o.width/i:o.width,y=l?o.height/l:o.height;return{width:m,height:y,top:g,right:f+m,bottom:g+y,left:f}}const bE={ignoreTransform:!1};function gs(o,e){e===void 0&&(e=bE);let n=o.getBoundingClientRect();if(e.ignoreTransform){const{transform:g,transformOrigin:m}=In(o).getComputedStyle(o);g&&(n=yE(n,g,m))}const{top:a,left:i,width:l,height:c,bottom:d,right:f}=n;return{top:a,left:i,width:l,height:c,bottom:d,right:f}}function zm(o){return gs(o,{ignoreTransform:!0})}function vE(o){const e=o.innerWidth,n=o.innerHeight;return{top:0,left:0,right:e,bottom:n,width:e,height:n}}function SE(o,e){return e===void 0&&(e=In(o).getComputedStyle(o)),e.position==="fixed"}function wE(o,e){e===void 0&&(e=In(o).getComputedStyle(o));const n=/(auto|scroll|overlay)/;return["overflow","overflowX
Line truncated
|
||||
<div style="padding: 1rem; border: 2px dashed #ccc; background: #f9f9f9; text-align: center;">
|
||||
<p style="margin: 0; color: #666;">Component not found: <strong>${e}</strong></p>
|
||||
</div>
|
||||
@@ -60,7 +60,7 @@ Error generating stack: `+c.message+`
|
||||
<p style="margin: 0 0 0.5rem 0; color: #c62828; font-weight: bold;">Component Error: ${e}</p>
|
||||
<p style="margin: 0; color: #666; font-size: 0.875rem;">${n?.message||"Unknown error"}</p>
|
||||
</div>
|
||||
`,a}renderErrorState(e,n){const a=n instanceof Error?n.message:String(n),i=n instanceof Sr?n.code:"UNKNOWN_ERROR";e.innerHTML=`
|
||||
`,a}renderErrorState(e,n){const a=n instanceof Error?n.message:String(n),i=n instanceof Er?n.code:"UNKNOWN_ERROR";e.innerHTML=`
|
||||
<div class="layout-error" data-error-code="${i}" style="
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
@@ -102,8 +102,8 @@ Error generating stack: `+c.message+`
|
||||
"
|
||||
>페이지 새로고침</button>
|
||||
</div>
|
||||
`,Fn.error("Error state rendered:",i,a)}getCurrentLayout(){return this.currentLayout}clear(){this.currentLayout=null,this.layoutCache.clear(),Fn.log("Layout data and cache cleared")}}const z_=ft("TranslationContext"),cf=H.createContext(null),gc=({children:o,translationEngine:e,translationContext:n})=>{const a=H.useMemo(()=>(l,u)=>{if(u){const d="|"+Object.entries(u).map(([f,g])=>`${f}=${g}`).join("|");return e.translate(l,n,d)}return e.translate(l,n)},[e,n]),i=H.useMemo(()=>({translationEngine:e,translationContext:n,t:a}),[e,n,a]);return ht.jsx(cf.Provider,{value:i,children:o})},ny=()=>{const o=H.useContext(cf);return o||(z_.warn("TranslationProvider 외부에서 호출되었습니다. 키를 그대로 반환합니다."),{t:e=>e,translationEngine:null,translationContext:null})},Ra=ft("SlotContext"),j_={registerToSlot:()=>{Ra.warn("SlotContext not available: registerToSlot called outside SlotProvider")},unregisterFromSlot:()=>{Ra.warn("SlotContext not available: unregisterFromSlot called outside SlotProvider")},getSlotComponents:()=>[],subscribeToSlot:()=>()=>{},clearAllSlots:()=>{},isEnabled:!1},U_=H.createContext(j_),uf=({children:o})=>{const e=H.useRef(new Map),n=H.useRef(new Map),a=H.useCallback(m=>{const b=n.current.get(m);b&&b.forEach(v=>{try{v()}catch(S){Ra.error(`Slot subscriber error (slotId: ${m}):`,S)}})},[]),i=H.useCallback((m,b,v)=>{if(!m||!b){Ra.warn("registerToSlot: slotId and componentId are required");return}e.current.has(m)||e.current.set(m,new Map);const S=e.current.get(m),_=S.get(b);_&&_.registrationKey===v.registrationKey||(S.set(b,v),Ra.log(`Component registered to slot: ${b} -> ${m} (order: ${v.order})`),a(m))},[a]),l=H.useCallback((m,b)=>{const v=e.current.get(m);v&&v.has(b)&&(v.delete(b),Ra.log(`Component unregistered from slot: ${b} <- ${m}`),v.size===0&&e.current.delete(m),a(m))},[a]),u=H.useCallback(m=>{const b=e.current.get(m);return b?Array.from(b.values()).sort((v,S)=>v.order-S.order):[]},[]),d=H.useCallback((m,b)=>{n.current.has(m)||n.current.set(m,new Set);const v=n.current.get(m);return v.add(b),()=>{v.delete(b),v.size===0&&n.current.delete(m)}},[]),f=H.useCallback(()=>{e.current.forEach((m,b)=>{a(b)}),e.current.clear(),n.current.clear(),Ra.log("All slots cleared")},[a]),g=H.useMemo(()=>({registerToSlot:i,unregisterFromSlot:l,getSlotComponents:u,subscribeToSlot:d,clearAllSlots:f,isEnabled:!0}),[i,l,u,d,f]);return H.useLayoutEffect(()=>{typeof window<"u"&&(window.__slotContextValue=g,Ra.log("SlotContext exposed to window.__slotContextValue"))},[g]),ht.jsx(U_.Provider,{value:g,children:o})};class df{constructor(){this.notificationCreatedEvent=".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated"}listenForWhisper(e,n){return this.listen(".client-"+e,n)}notification(e){return this.listen(this.notificationCreatedEvent,e)}stopListeningForNotification(e){return this.stopListening(this.notificationCreatedEvent,e)}stopListeningForWhisper(e,n){return this.stopListening(".client-"+e,n)}}class ry{constructor(e){this.namespace=e}format(e){return[".","\\"].includes(e.charAt(0))?e.substring(1):(this.namespace&&(e=this.namespace+"."+e),e.replace(/\./g,"\\"))}setNamespace(e){this.namespace=e}}function B_(o){try{return Reflect.construct(String,[],o),!0}catch{return!1}}class ff extends df{constructor(e,n,a){super(),this.name=n,this.pusher=e,this.options=a,this.eventFormatter=new ry(this.options.namespace),this.subscribe()}subscribe(){this.subscription=this.pusher.subscribe(this.name)}unsubscribe(){this.pusher.unsubscribe(this.name)}listen(e,n){return this.on(this.eventFormatter.format(e),n),this}listenToAll(e){return this.subscription.bind_global((n,a)=>{if(n.startsWith("pusher:"))return;let i=String(this.options.namespace??"").replace(/\./g,"\\"),l=n.startsWith(i)?n.substring(i.length+1):"."+n;e(l,a)}),this}stopListening(e,n){return n?this.subscription.unbind(this.eventFormatter.format(e),n):this.subscription.unbind(this.eventFormatter.format(e)),this}stopListeningToAll(e){return e?this.subscription.unbind_global(e):this.subscription.unbind_global(),this}subscribed(e){return this.on("pusher:subscription_succeeded",()=>{e()}),this}error(e){return this.on("pusher:subscription_error",n=>{e(n)}),this}on(e,n){return this.subscription.bind(e,n),this}}class ay extends ff{whisper(e,n){return this.pusher.channels.channels[this.name].trigger(`client-${e}`,n),this}}class P_ extends ff{whisper(e,n){return this.pusher.channels.channels[this.name].trigger(`client-${e}`,n),this}}class q_ extends ay{here(e){return this.on("pusher:subscription_succeeded",n=>{e(Object.keys(n.members).map(a=>n.members[a]))}),this}joining(e){return this.on("pusher:member_added",n=>{e(n.info)}),this}whisper(e,n){return this.pusher.channels.channels[this.name].trigger(`client-${e}`,n),this}leaving(e){return this.on("pusher:member_removed",n=>{e(n.info)}),this}}class iy extends df{constructor(e,n,a){super(),this.events={},this.listeners={},this.name=n,this.socket=e,this.options=a,this.eventFormatter=new ry(this.op
Line truncated
|
||||
`);return L!==-1?(this.position+=L+1,w.slice(0,L)):null}isBufferTooLong(y){return this.position===y.length&&y.length>No}}var Io;(function(E){E[E.CONNECTING=0]="CONNECTING",E[E.OPEN=1]="OPEN",E[E.CLOSED=3]="CLOSED"})(Io||(Io={}));const ar=Io;var xr=1;class Ho{constructor(y,w){this.hooks=y,this.session=ms(1e3)+"/"+Nc(8),this.location=zo(w),this.readyState=ar.CONNECTING,this.openStream()}send(y){return this.sendRaw(JSON.stringify([y]))}ping(){this.hooks.sendHeartbeat(this)}close(y,w){this.onClose(y,w,!0)}sendRaw(y){if(this.readyState===ar.OPEN)try{return Be.createSocketRequest("POST",ia(za(this.location,this.session))).start(y),!0}catch{return!1}else return!1}reconnect(){this.closeStream(),this.openStream()}onClose(y,w,L){this.closeStream(),this.readyState=ar.CLOSED,this.onclose&&this.onclose({code:y,reason:w,wasClean:L})}onChunk(y){if(y.status===200){this.readyState===ar.OPEN&&this.onActivity();var w,L=y.data.slice(0,1);switch(L){case"o":w=JSON.parse(y.data.slice(1)||"{}"),this.onOpen(w);break;case"a":w=JSON.parse(y.data.slice(1)||"[]");for(var I=0;I<w.length;I++)this.onEvent(w[I]);break;case"m":w=JSON.parse(y.data.slice(1)||"null"),this.onEvent(w);break;case"h":this.hooks.onHeartbeat(this);break;case"c":w=JSON.parse(y.data.slice(1)||"[]"),this.onClose(w[0],w[1],!0);break}}}onOpen(y){this.readyState===ar.CONNECTING?(y&&y.hostname&&(this.location.base=$c(this.location.base,y.hostname)),this.readyState=ar.OPEN,this.onopen&&this.onopen()):this.onClose(1006,"Server lost session",!0)}onEvent(y){this.readyState===ar.OPEN&&this.onmessage&&this.onmessage({data:y})}onActivity(){this.onactivity&&this.onactivity()}onError(y){this.onerror&&this.onerror(y)}openStream(){this.stream=Be.createSocketRequest("POST",ia(this.hooks.getReceiveURL(this.location,this.session))),this.stream.bind("chunk",y=>{this.onChunk(y)}),this.stream.bind("finished",y=>{this.hooks.onFinished(this,y)}),this.stream.bind("buffer_too_long",()=>{this.reconnect()});try{this.stream.start()}catch(y){ke.defer(()=>{this.onError(y),this.onClose(1006,"Could not start streaming",!1)})}}closeStream(){this.stream&&(this.stream.unbind_all(),this.stream.close(),this.stream=null)}}function zo(E){var y=/([^\?]*)\/*(\??.*)/.exec(E);return{base:y[1],queryString:y[2]}}function za(E,y){return E.base+"/"+y+"/xhr_send"}function ia(E){var y=E.indexOf("?")===-1?"?":"&";return E+y+"t="+ +new Date+"&n="+xr++}function $c(E,y){var w=/(https?:\/\/)([^\/:]+)((\/|:)?.*)/.exec(E);return w[1]+y+w[3]}function ms(E){return Be.randomInt(E)}function Nc(E){for(var y=[],w=0;w<E;w++)y.push(ms(32).toString(32));return y.join("")}const Ci=Ho;var Tr={getReceiveURL:function(E,y){return E.base+"/"+y+"/xhr_streaming"+E.queryString},onHeartbeat:function(E){E.sendRaw("[]")},sendHeartbeat:function(E){E.sendRaw("[]")},onFinished:function(E,y){E.onClose(1006,"Connection interrupted ("+y+")",!1)}};const jo=Tr;var Ei={getReceiveURL:function(E,y){return E.base+"/"+y+"/xhr"+E.queryString},onHeartbeat:function(){},sendHeartbeat:function(E){E.sendRaw("[]")},onFinished:function(E,y){y===200?E.reconnect():E.onClose(1006,"Connection interrupted ("+y+")",!1)}};const qr=Ei;var Uo={getRequest:function(E){var y=Be.getXHRAPI(),w=new y;return w.onreadystatechange=w.onprogress=function(){switch(w.readyState){case 3:w.responseText&&w.responseText.length>0&&E.onChunk(w.status,w.responseText);break;case 4:w.responseText&&w.responseText.length>0&&E.onChunk(w.status,w.responseText),E.emit("finished",w.status),E.close();break}},w},abortRequest:function(E){E.onreadystatechange=null,E.abort()}};const ys=Uo;var Ic={createStreamingSocket(E){return this.createSocket(jo,E)},createPollingSocket(E){return this.createSocket(qr,E)},createSocket(E,y){return new Ci(E,y)},createXHR(E,y){return this.createRequest(ys,E,y)},createRequest(E,y,w){return new xf(E,y,w)}};const _i=Ic;_i.createXDR=function(E,y){return this.createRequest(Mc,E,y)};var Hc={nextAuthCallbackID:1,auth_callbacks:{},ScriptReceivers:m,DependenciesReceivers:_,getDefaultStrategy:Ha,Transports:kn,transportConnectionInitializer:Af,HTTPFactory:_i,TimelineTransport:on,getXHRAPI(){return window.XMLHttpRequest},getWebSocketAPI(){return window.WebSocket||window.MozWebSocket},setup(E){if(typeof window<"u"){window.Pusher=E;var y=()=>{this.onDocumentBody(E.ready)};window.JSON?y():A.load("json2",{},y)}},getDocument(){return document},getProtocol(){return this.getDocument().location.protocol},getAuthorizers(){return{ajax:Fe,jsonp:bn}},onDocumentBody(E){document.body?E():setTimeout(()=>{this.onDocumentBody(E)},0)},createJSONPRequest(E,y){return new rr(E,y)},createScriptRequest(E){return new na(E)},getLocalStorage(){try{return window.localStorage}catch{return}},createXHR(){return this.getXHRAPI()?this.createXMLHttpRequest():this.createMicrosoftXHR()},createXMLHttpRequest(){var E=this.getXHRAPI();return new E},createMicrosoftXHR(){return new ActiveXObject("Microsoft.XMLHTTP")},getNetwork(){return Lo},createWebSocket(E){var y=this.getWebSocketAPI();return new y(E)},createSocketRequest(
Line truncated
|
||||
`,Kn.error("Error state rendered:",i,a)}getCurrentLayout(){return this.currentLayout}clear(){this.currentLayout=null,this.layoutCache.clear(),Kn.log("Layout data and cache cleared")}}const F_=ht("TranslationContext"),mf=H.createContext(null),vc=({children:o,translationEngine:e,translationContext:n})=>{const a=H.useMemo(()=>(l,c)=>{if(c){const d="|"+Object.entries(c).map(([f,g])=>`${f}=${g}`).join("|");return e.translate(l,n,d)}return e.translate(l,n)},[e,n]),i=H.useMemo(()=>({translationEngine:e,translationContext:n,t:a}),[e,n,a]);return gt.jsx(mf.Provider,{value:i,children:o})},dy=()=>{const o=H.useContext(mf);return o||(F_.warn("TranslationProvider 외부에서 호출되었습니다. 키를 그대로 반환합니다."),{t:e=>e,translationEngine:null,translationContext:null})},La=ht("SlotContext"),K_={registerToSlot:()=>{La.warn("SlotContext not available: registerToSlot called outside SlotProvider")},unregisterFromSlot:()=>{La.warn("SlotContext not available: unregisterFromSlot called outside SlotProvider")},getSlotComponents:()=>[],subscribeToSlot:()=>()=>{},clearAllSlots:()=>{},isEnabled:!1},W_=H.createContext(K_),yf=({children:o})=>{const e=H.useRef(new Map),n=H.useRef(new Map),a=H.useCallback(m=>{const y=n.current.get(m);y&&y.forEach(S=>{try{S()}catch(v){La.error(`Slot subscriber error (slotId: ${m}):`,v)}})},[]),i=H.useCallback((m,y,S)=>{if(!m||!y){La.warn("registerToSlot: slotId and componentId are required");return}e.current.has(m)||e.current.set(m,new Map);const v=e.current.get(m),_=v.get(y);_&&_.registrationKey===S.registrationKey||(v.set(y,S),La.log(`Component registered to slot: ${y} -> ${m} (order: ${S.order})`),a(m))},[a]),l=H.useCallback((m,y)=>{const S=e.current.get(m);S&&S.has(y)&&(S.delete(y),La.log(`Component unregistered from slot: ${y} <- ${m}`),S.size===0&&e.current.delete(m),a(m))},[a]),c=H.useCallback(m=>{const y=e.current.get(m);return y?Array.from(y.values()).sort((S,v)=>S.order-v.order):[]},[]),d=H.useCallback((m,y)=>{n.current.has(m)||n.current.set(m,new Set);const S=n.current.get(m);return S.add(y),()=>{S.delete(y),S.size===0&&n.current.delete(m)}},[]),f=H.useCallback(()=>{e.current.forEach((m,y)=>{a(y)}),e.current.clear(),n.current.clear(),La.log("All slots cleared")},[a]),g=H.useMemo(()=>({registerToSlot:i,unregisterFromSlot:l,getSlotComponents:c,subscribeToSlot:d,clearAllSlots:f,isEnabled:!0}),[i,l,c,d,f]);return H.useLayoutEffect(()=>{typeof window<"u"&&(window.__slotContextValue=g,La.log("SlotContext exposed to window.__slotContextValue"))},[g]),gt.jsx(W_.Provider,{value:g,children:o})};class bf{constructor(){this.notificationCreatedEvent=".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated"}listenForWhisper(e,n){return this.listen(".client-"+e,n)}notification(e){return this.listen(this.notificationCreatedEvent,e)}stopListeningForNotification(e){return this.stopListening(this.notificationCreatedEvent,e)}stopListeningForWhisper(e,n){return this.stopListening(".client-"+e,n)}}class fy{constructor(e){this.namespace=e}format(e){return[".","\\"].includes(e.charAt(0))?e.substring(1):(this.namespace&&(e=this.namespace+"."+e),e.replace(/\./g,"\\"))}setNamespace(e){this.namespace=e}}function Y_(o){try{return Reflect.construct(String,[],o),!0}catch{return!1}}class vf extends bf{constructor(e,n,a){super(),this.name=n,this.pusher=e,this.options=a,this.eventFormatter=new fy(this.options.namespace),this.subscribe()}subscribe(){this.subscription=this.pusher.subscribe(this.name)}unsubscribe(){this.pusher.unsubscribe(this.name)}listen(e,n){return this.on(this.eventFormatter.format(e),n),this}listenToAll(e){return this.subscription.bind_global((n,a)=>{if(n.startsWith("pusher:"))return;let i=String(this.options.namespace??"").replace(/\./g,"\\"),l=n.startsWith(i)?n.substring(i.length+1):"."+n;e(l,a)}),this}stopListening(e,n){return n?this.subscription.unbind(this.eventFormatter.format(e),n):this.subscription.unbind(this.eventFormatter.format(e)),this}stopListeningToAll(e){return e?this.subscription.unbind_global(e):this.subscription.unbind_global(),this}subscribed(e){return this.on("pusher:subscription_succeeded",()=>{e()}),this}error(e){return this.on("pusher:subscription_error",n=>{e(n)}),this}on(e,n){return this.subscription.bind(e,n),this}}class hy extends vf{whisper(e,n){return this.pusher.channels.channels[this.name].trigger(`client-${e}`,n),this}}class X_ extends vf{whisper(e,n){return this.pusher.channels.channels[this.name].trigger(`client-${e}`,n),this}}class J_ extends hy{here(e){return this.on("pusher:subscription_succeeded",n=>{e(Object.keys(n.members).map(a=>n.members[a]))}),this}joining(e){return this.on("pusher:member_added",n=>{e(n.info)}),this}whisper(e,n){return this.pusher.channels.channels[this.name].trigger(`client-${e}`,n),this}leaving(e){return this.on("pusher:member_removed",n=>{e(n.info)}),this}}class gy extends bf{constructor(e,n,a){super(),this.events={},this.listeners={},this.name=n,this.socket=e,this.options=a,this.eventFormatter=new fy(this.op
Line truncated
|
||||
`);return L!==-1?(this.position+=L+1,w.slice(0,L)):null}isBufferTooLong(b){return this.position===b.length&&b.length>Po}}var Bo;(function(E){E[E.CONNECTING=0]="CONNECTING",E[E.OPEN=1]="OPEN",E[E.CLOSED=3]="CLOSED"})(Bo||(Bo={}));const or=Bo;var Rr=1;class qo{constructor(b,w){this.hooks=b,this.session=Ss(1e3)+"/"+Uc(8),this.location=Go(w),this.readyState=or.CONNECTING,this.openStream()}send(b){return this.sendRaw(JSON.stringify([b]))}ping(){this.hooks.sendHeartbeat(this)}close(b,w){this.onClose(b,w,!0)}sendRaw(b){if(this.readyState===or.OPEN)try{return ze.createSocketRequest("POST",sa(Ba(this.location,this.session))).start(b),!0}catch{return!1}else return!1}reconnect(){this.closeStream(),this.openStream()}onClose(b,w,L){this.closeStream(),this.readyState=or.CLOSED,this.onclose&&this.onclose({code:b,reason:w,wasClean:L})}onChunk(b){if(b.status===200){this.readyState===or.OPEN&&this.onActivity();var w,L=b.data.slice(0,1);switch(L){case"o":w=JSON.parse(b.data.slice(1)||"{}"),this.onOpen(w);break;case"a":w=JSON.parse(b.data.slice(1)||"[]");for(var I=0;I<w.length;I++)this.onEvent(w[I]);break;case"m":w=JSON.parse(b.data.slice(1)||"null"),this.onEvent(w);break;case"h":this.hooks.onHeartbeat(this);break;case"c":w=JSON.parse(b.data.slice(1)||"[]"),this.onClose(w[0],w[1],!0);break}}}onOpen(b){this.readyState===or.CONNECTING?(b&&b.hostname&&(this.location.base=zc(this.location.base,b.hostname)),this.readyState=or.OPEN,this.onopen&&this.onopen()):this.onClose(1006,"Server lost session",!0)}onEvent(b){this.readyState===or.OPEN&&this.onmessage&&this.onmessage({data:b})}onActivity(){this.onactivity&&this.onactivity()}onError(b){this.onerror&&this.onerror(b)}openStream(){this.stream=ze.createSocketRequest("POST",sa(this.hooks.getReceiveURL(this.location,this.session))),this.stream.bind("chunk",b=>{this.onChunk(b)}),this.stream.bind("finished",b=>{this.hooks.onFinished(this,b)}),this.stream.bind("buffer_too_long",()=>{this.reconnect()});try{this.stream.start()}catch(b){ke.defer(()=>{this.onError(b),this.onClose(1006,"Could not start streaming",!1)})}}closeStream(){this.stream&&(this.stream.unbind_all(),this.stream.close(),this.stream=null)}}function Go(E){var b=/([^\?]*)\/*(\??.*)/.exec(E);return{base:b[1],queryString:b[2]}}function Ba(E,b){return E.base+"/"+b+"/xhr_send"}function sa(E){var b=E.indexOf("?")===-1?"?":"&";return E+b+"t="+ +new Date+"&n="+Rr++}function zc(E,b){var w=/(https?:\/\/)([^\/:]+)((\/|:)?.*)/.exec(E);return w[1]+b+w[3]}function Ss(E){return ze.randomInt(E)}function Uc(E){for(var b=[],w=0;w<E;w++)b.push(Ss(32).toString(32));return b.join("")}const xi=qo;var kr={getReceiveURL:function(E,b){return E.base+"/"+b+"/xhr_streaming"+E.queryString},onHeartbeat:function(E){E.sendRaw("[]")},sendHeartbeat:function(E){E.sendRaw("[]")},onFinished:function(E,b){E.onClose(1006,"Connection interrupted ("+b+")",!1)}};const Vo=kr;var Ti={getReceiveURL:function(E,b){return E.base+"/"+b+"/xhr"+E.queryString},onHeartbeat:function(){},sendHeartbeat:function(E){E.sendRaw("[]")},onFinished:function(E,b){b===200?E.reconnect():E.onClose(1006,"Connection interrupted ("+b+")",!1)}};const Vr=Ti;var Fo={getRequest:function(E){var b=ze.getXHRAPI(),w=new b;return w.onreadystatechange=w.onprogress=function(){switch(w.readyState){case 3:w.responseText&&w.responseText.length>0&&E.onChunk(w.status,w.responseText);break;case 4:w.responseText&&w.responseText.length>0&&E.onChunk(w.status,w.responseText),E.emit("finished",w.status),E.close();break}},w},abortRequest:function(E){E.onreadystatechange=null,E.abort()}};const ws=Fo;var Pc={createStreamingSocket(E){return this.createSocket(Vo,E)},createPollingSocket(E){return this.createSocket(Vr,E)},createSocket(E,b){return new xi(E,b)},createXHR(E,b){return this.createRequest(ws,E,b)},createRequest(E,b,w){return new Mf(E,b,w)}};const Ri=Pc;Ri.createXDR=function(E,b){return this.createRequest(jc,E,b)};var Bc={nextAuthCallbackID:1,auth_callbacks:{},ScriptReceivers:m,DependenciesReceivers:_,getDefaultStrategy:Pa,Transports:Rn,transportConnectionInitializer:Lf,HTTPFactory:Ri,TimelineTransport:un,getXHRAPI(){return window.XMLHttpRequest},getWebSocketAPI(){return window.WebSocket||window.MozWebSocket},setup(E){if(typeof window<"u"){window.Pusher=E;var b=()=>{this.onDocumentBody(E.ready)};window.JSON?b():A.load("json2",{},b)}},getDocument(){return document},getProtocol(){return this.getDocument().location.protocol},getAuthorizers(){return{ajax:Ge,jsonp:bn}},onDocumentBody(E){document.body?E():setTimeout(()=>{this.onDocumentBody(E)},0)},createJSONPRequest(E,b){return new sr(E,b)},createScriptRequest(E){return new ra(E)},getLocalStorage(){try{return window.localStorage}catch{return}},createXHR(){return this.getXHRAPI()?this.createXMLHttpRequest():this.createMicrosoftXHR()},createXMLHttpRequest(){var E=this.getXHRAPI();return new E},createMicrosoftXHR(){return new ActiveXObject("Microsoft.XMLHTTP")},getNetwork(){return jo},createWebSocket(E){var b=this.getWebSocketAPI();return new b(E)},createSocketRequest(
Line truncated
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -174,46 +174,46 @@ Error generating stack: `+c.message+`
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
`};a.innerHTML=`
|
||||
<div style="${u.container}">
|
||||
<div style="${u.card}">
|
||||
<div style="${c.container}">
|
||||
<div style="${c.card}">
|
||||
<!-- 아이콘 -->
|
||||
<div style="${u.iconContainer}">
|
||||
<i class="fa-solid ${n.icon}" style="${u.icon}"></i>
|
||||
<div style="${c.iconContainer}">
|
||||
<i class="fa-solid ${n.icon}" style="${c.icon}"></i>
|
||||
</div>
|
||||
|
||||
<!-- 제목 -->
|
||||
<h1 style="${u.title}">
|
||||
<h1 style="${c.title}">
|
||||
${this.escapeHtml(n.title)}
|
||||
</h1>
|
||||
|
||||
<!-- 사용자 메시지 -->
|
||||
<p style="${u.message}">
|
||||
<p style="${c.message}">
|
||||
${this.escapeHtml(n.message)}
|
||||
</p>
|
||||
|
||||
<!-- 상세 메시지 (API 응답 등) -->
|
||||
${n.detailMessage?`
|
||||
<p style="${u.detailMessage}">
|
||||
<p style="${c.detailMessage}">
|
||||
${this.escapeHtml(n.detailMessage)}
|
||||
</p>
|
||||
`:`<div style="${u.spacer}"></div>`}
|
||||
`:`<div style="${c.spacer}"></div>`}
|
||||
|
||||
<!-- Stack Trace (디버그 모드일 때만) -->
|
||||
${i?`
|
||||
<div style="${u.stackContainer}">
|
||||
<p style="${u.stackTitle}">
|
||||
<div style="${c.stackContainer}">
|
||||
<p style="${c.stackTitle}">
|
||||
Stack Trace:
|
||||
</p>
|
||||
<pre style="${u.stackTrace}">${this.escapeHtml(n.stackTrace)}</pre>
|
||||
<pre style="${c.stackTrace}">${this.escapeHtml(n.stackTrace)}</pre>
|
||||
</div>
|
||||
`:""}
|
||||
|
||||
<!-- 새로고침 버튼 (복구 가능할 때만) -->
|
||||
${n.showReloadButton?`
|
||||
<div style="${u.buttonContainer}">
|
||||
<div style="${c.buttonContainer}">
|
||||
<button
|
||||
onclick="window.location.reload()"
|
||||
style="${u.button}"
|
||||
style="${c.button}"
|
||||
onmouseover="this.style.backgroundColor='#1d4ed8'"
|
||||
onmouseout="this.style.backgroundColor='#2563eb'"
|
||||
>
|
||||
@@ -224,7 +224,7 @@ Error generating stack: `+c.message+`
|
||||
`:""}
|
||||
</div>
|
||||
</div>
|
||||
`}static renderFromError(e,n,a,i=!1,l,u,d){let f,g;if(n.userMessageKey.startsWith("$t:"))if(u&&l)try{f=u.translate(n.userMessageKey,l)}catch{f=n.userMessageKey.replace("$t:","")}else f=n.userMessageKey.replace("$t:","");else f=n.userMessageKey;if(d){const m=d;m.details?.apiMessage?g=m.details.apiMessage:m.response?.data?.message&&(g=m.response.data.message)}this.render(e,{title:a,message:f,detailMessage:g,icon:n.icon,showStack:n.showStack,stackTrace:n.stack,showReloadButton:n.recoverable,debug:i})}static escapeHtml(e){if(!e)return"";const n=document.createElement("div");return n.textContent=e,n.innerHTML}}const ds=ft("Router");class aA{constructor(e){$(this,"routes",[]);$(this,"templateIdentifier");$(this,"eventHandlers",new Map);$(this,"pendingNavigation",null);$(this,"isNavigating",!1);$(this,"handlePopState",()=>{this.navigateToCurrentPath()});this.templateIdentifier=e,this.initPopstateListener()}initPopstateListener(){window.addEventListener("popstate",this.handlePopState)}on(e,n){this.eventHandlers.has(e)||this.eventHandlers.set(e,[]),this.eventHandlers.get(e).push(n)}async emit(e,...n){const a=this.eventHandlers.get(e);a&&await Promise.all(a.map(i=>i(...n)))}async loadRoutes(e){try{const n=e!==void 0&&e>0?e:null,a=await fetch(Mr(`/api/templates/${this.templateIdentifier}/routes`,"json",n));if(!a.ok)throw new Error(`Failed to load routes: ${a.statusText}`);const i=await a.json();if(!i.success)throw new Error("Failed to load routes from API");if(i.data&&Array.isArray(i.data.routes))this.routes=i.data.routes,ds.log(`Loaded ${this.routes.length} routes${e?` (v=${e})`:""}`);else throw new Error("Invalid routes data format")}catch(n){throw ds.error("Error loading routes:",n),n}}setRoutes(e){this.routes=e,ds.log(`Set ${this.routes.length} routes`)}match(e){const n=this.normalizePathname(e);for(const a of this.routes){const i=this.matchPattern(a.path,n);if(i!==null)return{route:a,params:i}}return null}normalizePathname(e){return e.length>1&&e.endsWith("/")?e.replace(/\/+$/,"")||"/":e}matchPattern(e,n){const a=[];let i=e.replace(/:([^/]+)/g,(f,g)=>(a.push(g),"([^/]+)"));i.startsWith("*/")&&(i="(?:/[^/]+)?"+i.slice(1)),i=i.replace(/\//g,"\\/");const l=new RegExp(`^${i}$`),u=n.match(l);if(!u)return null;const d={};return a.forEach((f,g)=>{d[f]=u[g+1]}),d}getRoutes(){return[...this.routes]}getAuthType(e,n){return e.auth_type?e.auth_type:n.startsWith("/admin")?"admin":"user"}async navigateToCurrentPath(){const e=window.location.pathname,n=window.location.search,a=this.match(e);if(!a){ds.warn(`No route matched for path: ${e}`),this.emit("routeNotFound",e);return}if(a.route.redirect){ds.log(`Redirecting from ${e} to ${a.route.redirect}`),this.navigate(a.route.redirect);return}if(a.route.auth_required){const u=this.getAuthType(a.route,e),d=pr.getInstance();let f=d.isAuthenticated()&&d.getAuthType()===u;if(f||(f=await d.checkAuth(u)),!f){const g=d.getLoginRedirectUrl(u,e+n);ds.log(`Not authenticated, redirecting to: ${g}`),window.location.href=g;return}}const i=new URLSearchParams(n),l={};for(const u of i.keys()){if(u in l)continue;const d=i.getAll(u);d.length>1?l[u]=d:d.length===1&&(u.endsWith("[]")?l[u]=d:l[u]=d[0])}await this.emit("routeChange",{path:e,layout:a.route.layout,endpoint:a.route.endpoint,params:{...a.route.params||{},...a.params},query:l,auth_required:a.route.auth_required,auth_type:a.route.auth_type,meta:a.route.meta})}navigate(e){if(this.isNavigating){this.pendingNavigation=e;return}this.executeNavigation(e)}async executeNavigation(e){this.isNavigating=!0,this.pendingNavigation=null;try{window.history.pushState({},"",e),await this.navigateToCurrentPath()}finally{if(this.isNavigating=!1,this.pendingNavigation){const n=this.pendingNavigation;this.executeNavigation(n)}}}}const iA=/^\/admin\/layout-editor\/([^/?#]+)\/?$/;function gy(o){const e=iA.exec(o);return e?{templateIdentifier:e[1]}:null}const Bt=ft("ErrorPageHandler");class sA{constructor(e){$(this,"templateId");$(this,"layoutLoader");$(this,"locale");$(this,"debug");$(this,"renderFunction");$(this,"dataSourceManager");$(this,"globalState");$(this,"errorConfig",null);$(this,"configLoaded",!1);this.templateId=e.templateId,this.layoutLoader=e.layoutLoader,this.locale=e.locale,this.debug=e.debug,this.renderFunction=e.renderFunction,this.dataSourceManager=e.dataSourceManager,this.globalState=e.globalState||{}}async loadConfig(){if(this.configLoaded)return this.errorConfig;try{const e=await fetch(Mr(`/api/templates/${this.templateId}/config`,"json"));if(!e.ok)return this.debug&&Bt.warn("Failed to load template config:",e.statusText),this.configLoaded=!0,null;const n=await e.json();if(!n.success||!n.data)return this.debug&&Bt.warn("Invalid template config response"),this.configLoaded=!0,null;const a=n.data;return!a.error_config||!a.error_config.layouts?(this.debug&&Bt.warn("No error_config found in template.json"),this.configLoaded=!0,null):(this.errorConfig=a.error_config,this.configLoaded=!0,this.debug&&Bt.log("Error config loaded:",this.error
Line truncated
|
||||
`}static renderFromError(e,n,a,i=!1,l,c,d){let f,g;if(n.userMessageKey.startsWith("$t:"))if(c&&l)try{f=c.translate(n.userMessageKey,l)}catch{f=n.userMessageKey.replace("$t:","")}else f=n.userMessageKey.replace("$t:","");else f=n.userMessageKey;if(d){const m=d;m.details?.apiMessage?g=m.details.apiMessage:m.response?.data?.message&&(g=m.response.data.message)}this.render(e,{title:a,message:f,detailMessage:g,icon:n.icon,showStack:n.showStack,stackTrace:n.stack,showReloadButton:n.recoverable,debug:i})}static escapeHtml(e){if(!e)return"";const n=document.createElement("div");return n.textContent=e,n.innerHTML}}const ps=ht("Router");class dA{constructor(e){$(this,"routes",[]);$(this,"templateIdentifier");$(this,"eventHandlers",new Map);$(this,"pendingNavigation",null);$(this,"isNavigating",!1);$(this,"handlePopState",()=>{this.navigateToCurrentPath()});this.templateIdentifier=e,this.initPopstateListener()}initPopstateListener(){window.addEventListener("popstate",this.handlePopState)}on(e,n){this.eventHandlers.has(e)||this.eventHandlers.set(e,[]),this.eventHandlers.get(e).push(n)}async emit(e,...n){const a=this.eventHandlers.get(e);a&&await Promise.all(a.map(i=>i(...n)))}async loadRoutes(e){try{const n=e!==void 0&&e>0?e:null,a=await fetch(Nr(`/api/templates/${this.templateIdentifier}/routes`,"json",n));if(!a.ok)throw new Error(`Failed to load routes: ${a.statusText}`);const i=await a.json();if(!i.success)throw new Error("Failed to load routes from API");if(i.data&&Array.isArray(i.data.routes))this.routes=i.data.routes,ps.log(`Loaded ${this.routes.length} routes${e?` (v=${e})`:""}`);else throw new Error("Invalid routes data format")}catch(n){throw ps.error("Error loading routes:",n),n}}setRoutes(e){this.routes=e,ps.log(`Set ${this.routes.length} routes`)}match(e){const n=this.normalizePathname(e);for(const a of this.routes){const i=this.matchPattern(a.path,n);if(i!==null)return{route:a,params:i}}return null}normalizePathname(e){return e.length>1&&e.endsWith("/")?e.replace(/\/+$/,"")||"/":e}matchPattern(e,n){const a=[];let i=e.replace(/:([^/]+)/g,(f,g)=>(a.push(g),"([^/]+)"));i.startsWith("*/")&&(i="(?:/[^/]+)?"+i.slice(1)),i=i.replace(/\//g,"\\/");const l=new RegExp(`^${i}$`),c=n.match(l);if(!c)return null;const d={};return a.forEach((f,g)=>{d[f]=c[g+1]}),d}getRoutes(){return[...this.routes]}getAuthType(e,n){return e.auth_type?e.auth_type:n.startsWith("/admin")?"admin":"user"}async navigateToCurrentPath(){const e=window.location.pathname,n=window.location.search,a=this.match(e);if(!a){ps.warn(`No route matched for path: ${e}`),this.emit("routeNotFound",e);return}if(a.route.redirect){ps.log(`Redirecting from ${e} to ${a.route.redirect}`),this.navigate(a.route.redirect);return}if(a.route.auth_required){const c=this.getAuthType(a.route,e),d=br.getInstance();let f=d.isAuthenticated()&&d.getAuthType()===c;if(f||(f=await d.checkAuth(c)),!f){const g=d.getLoginRedirectUrl(c,e+n);ps.log(`Not authenticated, redirecting to: ${g}`),window.location.href=g;return}}const i=new URLSearchParams(n),l={};for(const c of i.keys()){if(c in l)continue;const d=i.getAll(c);d.length>1?l[c]=d:d.length===1&&(c.endsWith("[]")?l[c]=d:l[c]=d[0])}await this.emit("routeChange",{path:e,layout:a.route.layout,endpoint:a.route.endpoint,params:{...a.route.params||{},...a.params},query:l,auth_required:a.route.auth_required,auth_type:a.route.auth_type,meta:a.route.meta})}navigate(e){if(this.isNavigating){this.pendingNavigation=e;return}this.executeNavigation(e)}async executeNavigation(e){this.isNavigating=!0,this.pendingNavigation=null;try{window.history.pushState({},"",e),await this.navigateToCurrentPath()}finally{if(this.isNavigating=!1,this.pendingNavigation){const n=this.pendingNavigation;this.executeNavigation(n)}}}}const fA=/^\/admin\/layout-editor\/([^/?#]+)\/?$/;function _y(o){const e=fA.exec(o);return e?{templateIdentifier:e[1]}:null}const Gt=ht("ErrorPageHandler");class hA{constructor(e){$(this,"templateId");$(this,"layoutLoader");$(this,"locale");$(this,"debug");$(this,"renderFunction");$(this,"dataSourceManager");$(this,"globalState");$(this,"errorConfig",null);$(this,"configLoaded",!1);this.templateId=e.templateId,this.layoutLoader=e.layoutLoader,this.locale=e.locale,this.debug=e.debug,this.renderFunction=e.renderFunction,this.dataSourceManager=e.dataSourceManager,this.globalState=e.globalState||{}}async loadConfig(){if(this.configLoaded)return this.errorConfig;try{const e=await fetch(Nr(`/api/templates/${this.templateId}/config`,"json"));if(!e.ok)return this.debug&&Gt.warn("Failed to load template config:",e.statusText),this.configLoaded=!0,null;const n=await e.json();if(!n.success||!n.data)return this.debug&&Gt.warn("Invalid template config response"),this.configLoaded=!0,null;const a=n.data;return!a.error_config||!a.error_config.layouts?(this.debug&&Gt.warn("No error_config found in template.json"),this.configLoaded=!0,null):(this.errorConfig=a.error_config,this.configLoaded=!0,this.debug&&Gt.log("Error config loaded:",this.error
Line truncated
|
||||
with(ctx) {
|
||||
try {
|
||||
return Boolean(${a});
|
||||
@@ -232,7 +232,7 @@ Error generating stack: `+c.message+`
|
||||
return false;
|
||||
}
|
||||
}
|
||||
`)(n)}return!!e}catch(a){return P.warn(`Failed to evaluate script condition: ${e}`,a),!1}}showInitError(e){if(od()){P.warn("Init failed while document is unloading — skipping error screen",e);return}const n=hy(e);gf.renderFromError("app",n,"초기화 실패",this.config.debug,{templateId:this.config.templateId,locale:this.config.locale},void 0,e)}showTransitionOverlay(e,n){const a=typeof e=="boolean"?{enabled:e,style:"opaque",target:void 0,skeleton:void 0,spinner:void 0}:{enabled:e.enabled,style:e.style||"opaque",target:e.target,skeleton:e.skeleton,spinner:e.spinner,fallback_target:e.fallback_target};if(!a.enabled)return;if(this.hideTransitionOverlay(),a.style==="skeleton"&&a.skeleton?.component&&a.target&&n){this.renderSkeletonOverlay(a.target,a.skeleton,n,a.fallback_target);return}if(a.style==="spinner"&&a.target){this.renderSpinnerOverlay(a.target,a.spinner,a.fallback_target);return}const i=document.documentElement.classList.contains("dark");let l,u="";switch(a.style){case"blur":l=i?"rgba(17,24,39,0.3)":"rgba(255,255,255,0.3)",u="backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);";break;case"fade":l=i?"rgba(17,24,39,0.8)":"rgba(255,255,255,0.8)";break;case"skeleton":l=i?"rgb(17,24,39)":"rgb(249,250,251)";break;default:l=i?"rgb(17,24,39)":"rgb(249,250,251)";break}if(a.target){const d=`#${CSS.escape(a.target)}`,f=document.createElement("style");f.id="g7-transition-overlay",f.textContent=`${d}{position:relative;z-index:0;}${d}::after{content:'';position:absolute;inset:0;background:${l};${u}z-index:2147483647;pointer-events:none;}`,document.head.appendChild(f),this.transitionOverlayEl=f}else{const d=document.createElement("div");d.id="g7-transition-overlay",d.setAttribute("aria-hidden","true"),d.style.position="fixed",d.style.inset="0",d.style.zIndex="9999",d.style.pointerEvents="none",d.style.background=l;for(const f of u.split(";")){const g=f.indexOf(":");g!==-1&&d.style.setProperty(f.slice(0,g).trim(),f.slice(g+1).trim())}document.body.appendChild(d),this.transitionOverlayEl=d}}hideTransitionOverlay(){this.transitionOverlayEl&&(this.transitionOverlayEl.remove(),this.transitionOverlayEl=null),this.hideSkeletonOverlay()}renderSkeletonOverlay(e,n,a,i){const u=hr.getInstance().getComponent(n.component);if(!u){P.log(`Skeleton component "${n.component}" not found in registry, falling back to opaque overlay`),this.showTransitionOverlay({enabled:!0,style:"opaque",target:e});return}let d=document.getElementById(e),f="target";if(!d&&i&&(d=document.getElementById(i),f="fallback"),d||(d=document.getElementById("app"),f="fullpage"),!d){P.log(`Skeleton overlay: no target found (target="#${e}", fallback="${i||"none"}"), falling back to opaque overlay`),this.showTransitionOverlay({enabled:!0,style:"opaque",target:e});return}this.hideSkeletonOverlay();const m=document.documentElement.classList.contains("dark")?"rgb(17,24,39)":"rgb(249,250,251)",b=f==="fullpage"?"app":f==="fallback"?i:e,v=`#${CSS.escape(b)}`,S=document.createElement("style");S.id="g7-skeleton-overlay-style",S.textContent=`${v}{position:relative;z-index:0;}${v}::after{content:'';position:absolute;inset:0;background:${m};z-index:2147483646;pointer-events:none;}`,document.head.appendChild(S),this.transitionOverlayEl=S;const _=document.createElement("div");if(_.id="g7-skeleton-overlay",_.setAttribute("role","status"),_.setAttribute("aria-busy","true"),_.setAttribute("aria-label","Loading..."),f==="fullpage")_.style.cssText=["position:fixed","inset:0","z-index:20","overflow:hidden","pointer-events:none",`background:${m}`].join(";")+";";else{const x=d.getBoundingClientRect(),D=window.scrollX||document.documentElement.scrollLeft,U=window.scrollY||document.documentElement.scrollTop;_.style.cssText=["position:absolute",`top:${x.top+U}px`,`left:${x.left+D}px`,`width:${x.width}px`,`height:${x.height}px`,"z-index:20","overflow:hidden","pointer-events:none",`background:${m}`].join(";")+";"}document.body.appendChild(_),this.skeletonOverlayContainer=_;const A=a.components||[];let k;if(f==="fullpage")k=A;else{const x=f==="fallback"?i:e;k=this.findComponentChildrenById(A,x)}const O=nd.createRoot(_);this.skeletonOverlayRoot=O,Aa.flushSync(()=>{O.render(Xe.createElement(u,{components:k,options:{animation:n.animation||"pulse",iteration_count:n.iteration_count||5}}))});const M=f==="fullpage"?"fullpage (#app)":f==="fallback"?`fallback (#${i})`:`target (#${e})`;P.log(`Skeleton overlay rendered [${M}] with "${n.component}" (${k.length} components)`)}findComponentChildrenById(e,n){const a=i=>{for(const l of i){if(l.id===n)return l.children||[];if(l.children&&Array.isArray(l.children)){const u=a(l.children);if(u!==null)return u}}return null};return a(e)||e}renderSpinnerOverlay(e,n,a){let i=document.getElementById(e),l="target";if(!i&&a&&(i=document.getElementById(a),l="fallback"),i||(i=document.getElementById("app"),l="fullpage"),!i){P.log(`Spinner overlay: no target found (target="#${e}", fallback="${a||"none"}"), falling back to opaque overlay`),this.
Line truncated
|
||||
`)(n)}return!!e}catch(a){return B.warn(`Failed to evaluate script condition: ${e}`,a),!1}}showInitError(e){if(fd()){B.warn("Init failed while document is unloading — skipping error screen",e);return}const n=Ey(e);wf.renderFromError("app",n,"초기화 실패",this.config.debug,{templateId:this.config.templateId,locale:this.config.locale},void 0,e)}showTransitionOverlay(e,n){const a=typeof e=="boolean"?{enabled:e,style:"opaque",target:void 0,skeleton:void 0,spinner:void 0}:{enabled:e.enabled,style:e.style||"opaque",target:e.target,skeleton:e.skeleton,spinner:e.spinner,fallback_target:e.fallback_target};if(!a.enabled)return;if(this.hideTransitionOverlay(),a.style==="skeleton"&&a.skeleton?.component&&a.target&&n){this.renderSkeletonOverlay(a.target,a.skeleton,n,a.fallback_target);return}if(a.style==="spinner"&&a.target){this.renderSpinnerOverlay(a.target,a.spinner,a.fallback_target);return}const i=document.documentElement.classList.contains("dark");let l,c="";switch(a.style){case"blur":l=i?"rgba(17,24,39,0.3)":"rgba(255,255,255,0.3)",c="backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);";break;case"fade":l=i?"rgba(17,24,39,0.8)":"rgba(255,255,255,0.8)";break;case"skeleton":l=i?"rgb(17,24,39)":"rgb(249,250,251)";break;default:l=i?"rgb(17,24,39)":"rgb(249,250,251)";break}if(a.target){const d=`#${CSS.escape(a.target)}`,f=document.createElement("style");f.id="g7-transition-overlay",f.textContent=`${d}{position:relative;z-index:0;}${d}::after{content:'';position:absolute;inset:0;background:${l};${c}z-index:2147483647;pointer-events:none;}`,document.head.appendChild(f),this.transitionOverlayEl=f}else{const d=document.createElement("div");d.id="g7-transition-overlay",d.setAttribute("aria-hidden","true"),d.style.position="fixed",d.style.inset="0",d.style.zIndex="9999",d.style.pointerEvents="none",d.style.background=l;for(const f of c.split(";")){const g=f.indexOf(":");g!==-1&&d.style.setProperty(f.slice(0,g).trim(),f.slice(g+1).trim())}document.body.appendChild(d),this.transitionOverlayEl=d}}hideTransitionOverlay(){this.transitionOverlayEl&&(this.transitionOverlayEl.remove(),this.transitionOverlayEl=null),this.hideSkeletonOverlay()}renderSkeletonOverlay(e,n,a,i){const c=mr.getInstance().getComponent(n.component);if(!c){B.log(`Skeleton component "${n.component}" not found in registry, falling back to opaque overlay`),this.showTransitionOverlay({enabled:!0,style:"opaque",target:e});return}let d=document.getElementById(e),f="target";if(!d&&i&&(d=document.getElementById(i),f="fallback"),d||(d=document.getElementById("app"),f="fullpage"),!d){B.log(`Skeleton overlay: no target found (target="#${e}", fallback="${i||"none"}"), falling back to opaque overlay`),this.showTransitionOverlay({enabled:!0,style:"opaque",target:e});return}this.hideSkeletonOverlay();const m=document.documentElement.classList.contains("dark")?"rgb(17,24,39)":"rgb(249,250,251)",y=f==="fullpage"?"app":f==="fallback"?i:e,S=`#${CSS.escape(y)}`,v=document.createElement("style");v.id="g7-skeleton-overlay-style",v.textContent=`${S}{position:relative;z-index:0;}${S}::after{content:'';position:absolute;inset:0;background:${m};z-index:2147483646;pointer-events:none;}`,document.head.appendChild(v),this.transitionOverlayEl=v;const _=document.createElement("div");if(_.id="g7-skeleton-overlay",_.setAttribute("role","status"),_.setAttribute("aria-busy","true"),_.setAttribute("aria-label","Loading..."),f==="fullpage")_.style.cssText=["position:fixed","inset:0","z-index:20","overflow:hidden","pointer-events:none",`background:${m}`].join(";")+";";else{const T=d.getBoundingClientRect(),D=window.scrollX||document.documentElement.scrollLeft,z=window.scrollY||document.documentElement.scrollTop;_.style.cssText=["position:absolute",`top:${T.top+z}px`,`left:${T.left+D}px`,`width:${T.width}px`,`height:${T.height}px`,"z-index:20","overflow:hidden","pointer-events:none",`background:${m}`].join(";")+";"}document.body.appendChild(_),this.skeletonOverlayContainer=_;const A=a.components||[];let x;if(f==="fullpage")x=A;else{const T=f==="fallback"?i:e;x=this.findComponentChildrenById(A,T)}const O=od.createRoot(_);this.skeletonOverlayRoot=O,ka.flushSync(()=>{O.render(Xe.createElement(c,{components:x,options:{animation:n.animation||"pulse",iteration_count:n.iteration_count||5}}))});const M=f==="fullpage"?"fullpage (#app)":f==="fallback"?`fallback (#${i})`:`target (#${e})`;B.log(`Skeleton overlay rendered [${M}] with "${n.component}" (${x.length} components)`)}findComponentChildrenById(e,n){const a=i=>{for(const l of i){if(l.id===n)return l.children||[];if(l.children&&Array.isArray(l.children)){const c=a(l.children);if(c!==null)return c}}return null};return a(e)||e}renderSpinnerOverlay(e,n,a){let i=document.getElementById(e),l="target";if(!i&&a&&(i=document.getElementById(a),l="fallback"),i||(i=document.getElementById("app"),l="fullpage"),!i){B.log(`Spinner overlay: no target found (target="#${e}", fallback="${a||"none"}"), falling back to opaque overlay`),this.
Line truncated
|
||||
with(ctx) {
|
||||
try {
|
||||
return ${e};
|
||||
@@ -240,5 +240,5 @@ Error generating stack: `+c.message+`
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
`)(n)}catch(a){P.warn(`Expression evaluation failed: ${e}`,a);return}}async executeInitActions(e,n={}){const{getActionDispatcher:a}=await Promise.resolve().then(()=>Ur),i=a();if(!i){P.warn("ActionDispatcher not available for init_actions");return}P.log("Executing init_actions:",e.map(d=>d.handler)),P.log("Init actions dataContext:",n),P.log("Init actions dataContext._global:",n._global);const l=e.map(d=>d.handler).filter(d=>d.includes("."));l.length>0&&await this.waitForHandlers(i,l);let u={...n};for(const d of e)try{const f={type:"click",handler:d.handler,target:d.target,params:d.params,resultTo:d.resultTo,onSuccess:d.onSuccess,onError:d.onError,if:d.if,conditions:d.conditions,auth_mode:d.auth_mode,auth_required:d.auth_required},g=i.createHandler(f,u),m=new Event("init");await g(m),P.log(`Init action executed: ${d.handler}`);const b=this.globalState;u={...u,_global:{...b},_local:b._local||u._local||{}},P.log(`Data context refreshed after ${d.handler}:`,{cartKey:u._global?.cartKey})}catch(f){P.error(`Failed to execute init action: ${d.handler}`,f)}}async waitForHandlers(e,n,a=5e3){const i=Date.now(),l=50,u=()=>n.every(f=>e.customHandlers?.has(f));if(u()){P.log("All module handlers already registered");return}const d=mf().getFailedJsAssets();if(d.length>0){const f=d.some(b=>b==="module"||b==="plugin"),g=n.filter(b=>!e.customHandlers?.has(b)),m=g.length>0&&g.every(b=>d.some(v=>b.startsWith(`${v}.`)));if(f||m){P.warn("Extension asset load failed — not waiting for handlers that will never register:",{pending:g,failedAssets:d});return}}return P.log("Waiting for module handlers:",n),new Promise(f=>{const g=()=>{if(u()){P.log("All module handlers now registered"),f();return}if(Date.now()-i>=a){const m=n.filter(b=>!e.customHandlers?.has(b));P.warn("Timeout waiting for handlers:",m),f();return}setTimeout(g,l)};g()})}};$(_r,"LOCALE_STORAGE_KEY","g7_locale"),$(_r,"CACHE_VERSION_STORAGE_KEY","g7_cache_version");let _o=_r;function bf(o){const e=new _o(o);return o.websocket&&(yc.configure(o.websocket),P.log("WebSocket 설정 완료")),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{e.init(),window.__templateApp=e}):(e.init(),window.__templateApp=e),e}typeof window<"u"&&(window.G7Core=window.G7Core||{},window.G7Core.initTemplateApp=bf,window.G7Core.dataSource={refetch:async(o,e)=>{const n=window.__templateApp;if(!n){P.warn("TemplateApp not initialized (G7Core.dataSource.refetch)");return}return n.refetchDataSource(o,e)},get:o=>{const e=window.__templateApp;if(!e){P.warn("TemplateApp not initialized (G7Core.dataSource.get)");return}return e.getDataSource(o)}});function dA(o={}){const{checked:e,value:n,name:a,type:i="checkbox"}=o,u=n!==void 0?n:i==="checkbox"||i==="radio"?e??!1:String(e??""),d={checked:e??!1,value:u,name:a??"",type:i};return{target:d,currentTarget:d,preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new Event("change"),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:"change",isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{}}}function fA(o={}){const{button:e=0,clientX:n=0,clientY:a=0}=o;return{button:e,clientX:n,clientY:a,preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new MouseEvent("click"),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:"click",isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{},target:document.createElement("div"),currentTarget:document.createElement("div")}}function hA(){return{preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new Event("submit"),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:"submit",isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{},target:document.createElement("form"),currentTarget:document.createElement("form")}}function gA(o,e="keydown"){return{key:o,code:o.length===1?`Key${o.toUpperCase()}`:o,preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new KeyboardEvent(e,{key:o}),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:e,isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{},target:document.createElement("div"),currentTarget:document.createElement("div"),altKey:!1,ctrlKey:!1,metaKey:!1,shiftKey:!1,repeat:!1}}const pA={justify:/^justify-(start|end|center|between|around|evenly)$/,items:/^items-(start|end|center|baseline|stretch)$/,content:/^content-(start|end|center|between|around|evenly|stretch)$/,self:/^self-(auto|start|end|center|stretch|baseline)$/,flex:/^flex-(row|row-reverse|col|col-reverse|wrap|wrap-reverse|nowrap|1|auto|initial|none)$/,grow:/^(grow|grow-0)$/,shrink:/^(shrink|shrink-0)$/,basis:/^basis-/,order:/^(order-|-)order-/,gap:/^gap(-x|-y)?-/,gridCols:/^grid-cols-/,gridRows:/^grid-rows-/,colSpan:/^col-(span-|start-|end-)/,rowSpan:/^row-(span-|start-|end-)/,displa
Line truncated
|
||||
`)(n)}catch(a){B.warn(`Expression evaluation failed: ${e}`,a);return}}async executeInitActions(e,n={}){const{getActionDispatcher:a}=await Promise.resolve().then(()=>Pr),i=a();if(!i){B.warn("ActionDispatcher not available for init_actions");return}B.log("Executing init_actions:",e.map(d=>d.handler)),B.log("Init actions dataContext:",n),B.log("Init actions dataContext._global:",n._global);const l=e.map(d=>d.handler).filter(d=>d.includes("."));l.length>0&&await this.waitForHandlers(i,l);let c={...n};for(const d of e)try{const f={type:"click",handler:d.handler,target:d.target,params:d.params,resultTo:d.resultTo,onSuccess:d.onSuccess,onError:d.onError,if:d.if,conditions:d.conditions,auth_mode:d.auth_mode,auth_required:d.auth_required},g=i.createHandler(f,c),m=new Event("init");await g(m),B.log(`Init action executed: ${d.handler}`);const y=this.globalState;c={...c,_global:{...y},_local:y._local||c._local||{}},B.log(`Data context refreshed after ${d.handler}:`,{cartKey:c._global?.cartKey})}catch(f){B.error(`Failed to execute init action: ${d.handler}`,f)}}async waitForHandlers(e,n,a=5e3){const i=Date.now(),l=50,c=()=>n.every(f=>e.customHandlers?.has(f));if(c()){B.log("All module handlers already registered");return}const d=Ef().getFailedJsAssets();if(d.length>0){const f=d.some(y=>y==="module"||y==="plugin"),g=n.filter(y=>!e.customHandlers?.has(y)),m=g.length>0&&g.every(y=>d.some(S=>y.startsWith(`${S}.`)));if(f||m){B.warn("Extension asset load failed — not waiting for handlers that will never register:",{pending:g,failedAssets:d});return}}return B.log("Waiting for module handlers:",n),new Promise(f=>{const g=()=>{if(c()){B.log("All module handlers now registered"),f();return}if(Date.now()-i>=a){const m=n.filter(y=>!e.customHandlers?.has(y));B.warn("Timeout waiting for handlers:",m),f();return}setTimeout(g,l)};g()})}};$(xr,"LOCALE_STORAGE_KEY","g7_locale"),$(xr,"CACHE_VERSION_STORAGE_KEY","g7_cache_version");let Do=xr;function Af(o){const e=new Do(o);return o.websocket&&(Cc.configure(o.websocket),B.log("WebSocket 설정 완료")),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{e.init(),window.__templateApp=e}):(e.init(),window.__templateApp=e),e}typeof window<"u"&&(window.G7Core=window.G7Core||{},window.G7Core.initTemplateApp=Af,window.G7Core.dataSource={refetch:async(o,e)=>{const n=window.__templateApp;if(!n){B.warn("TemplateApp not initialized (G7Core.dataSource.refetch)");return}return n.refetchDataSource(o,e)},get:o=>{const e=window.__templateApp;if(!e){B.warn("TemplateApp not initialized (G7Core.dataSource.get)");return}return e.getDataSource(o)}});function bA(o={}){const{checked:e,value:n,name:a,type:i="checkbox"}=o,c=n!==void 0?n:i==="checkbox"||i==="radio"?e??!1:String(e??""),d={checked:e??!1,value:c,name:a??"",type:i};return{target:d,currentTarget:d,preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new Event("change"),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:"change",isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{}}}function vA(o={}){const{button:e=0,clientX:n=0,clientY:a=0}=o;return{button:e,clientX:n,clientY:a,preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new MouseEvent("click"),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:"click",isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{},target:document.createElement("div"),currentTarget:document.createElement("div")}}function SA(){return{preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new Event("submit"),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:"submit",isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{},target:document.createElement("form"),currentTarget:document.createElement("form")}}function wA(o,e="keydown"){return{key:o,code:o.length===1?`Key${o.toUpperCase()}`:o,preventDefault:()=>{},stopPropagation:()=>{},nativeEvent:new KeyboardEvent(e,{key:o}),bubbles:!0,cancelable:!0,defaultPrevented:!1,eventPhase:Event.AT_TARGET,isTrusted:!1,timeStamp:Date.now(),type:e,isDefaultPrevented:()=>!1,isPropagationStopped:()=>!1,persist:()=>{},target:document.createElement("div"),currentTarget:document.createElement("div"),altKey:!1,ctrlKey:!1,metaKey:!1,shiftKey:!1,repeat:!1}}const CA={justify:/^justify-(start|end|center|between|around|evenly)$/,items:/^items-(start|end|center|baseline|stretch)$/,content:/^content-(start|end|center|between|around|evenly|stretch)$/,self:/^self-(auto|start|end|center|stretch|baseline)$/,flex:/^flex-(row|row-reverse|col|col-reverse|wrap|wrap-reverse|nowrap|1|auto|initial|none)$/,grow:/^(grow|grow-0)$/,shrink:/^(shrink|shrink-0)$/,basis:/^basis-/,order:/^(order-|-)order-/,gap:/^gap(-x|-y)?-/,gridCols:/^grid-cols-/,gridRows:/^grid-rows-/,colSpan:/^col-(span-|start-|end-)/,rowSpan:/^row-(span-|start-|end-)/,displa
Line truncated
|
||||
//# sourceMappingURL=template-engine.min.js.map
|
||||
@@ -11,6 +11,8 @@ import type { Route } from './routing/Router';
|
||||
import { LayoutLoader, LayoutLoaderError } from './template-engine/LayoutLoader';
|
||||
import type { InitActionDefinition, LayoutScript, ComputedSwitchDefinition } from './template-engine/LayoutLoader';
|
||||
import { DataBindingEngine } from './template-engine/DataBindingEngine';
|
||||
import { extractSingleBinding } from './template-engine/BindingShape';
|
||||
import { hasPipes } from './template-engine/PipeRegistry';
|
||||
import { evaluateRenderCondition } from './template-engine/helpers/RenderHelpers';
|
||||
import { ComponentRegistry } from './template-engine/ComponentRegistry';
|
||||
import { DataSourceManager } from './template-engine/DataSourceManager';
|
||||
@@ -1963,12 +1965,13 @@ export class TemplateApp {
|
||||
* extractValueByPathOrExpression(data, "{{data.items.map(i => i.id)}}", "cart")
|
||||
*/
|
||||
private extractValueByPathOrExpression(obj: any, pathOrExpression: string, sourceId: string): any {
|
||||
// 표현식 패턴 확인: {{...}}
|
||||
const expressionMatch = pathOrExpression.match(/^\{\{(.+)\}\}$/);
|
||||
// 단일 바인딩 판정은 BindingShape 정본을 쓴다. 종전 greedy 정규식
|
||||
// `^\{\{(.+)\}\}$` 은 `"{{a}}-{{b}}"` 같은 보간 문자열까지 단일 바인딩으로 오판해
|
||||
// `a}}-{{b` 를 식으로 평가하려 했고, 그 결과는 조용한 undefined 였다.
|
||||
// @since engine-v1.55.0
|
||||
const expression = extractSingleBinding(pathOrExpression);
|
||||
|
||||
if (expressionMatch) {
|
||||
// 표현식으로 평가
|
||||
const expression = expressionMatch[1].trim();
|
||||
if (expression !== null) {
|
||||
const bindingEngine = new DataBindingEngine();
|
||||
|
||||
// 컨텍스트 구성: data 변수로 API 응답 접근 가능
|
||||
@@ -1980,7 +1983,11 @@ export class TemplateApp {
|
||||
};
|
||||
|
||||
try {
|
||||
const result = bindingEngine.evaluateExpression(expression, context);
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — evaluateExpression 은
|
||||
// `|` 를 JS 비트 OR 로 본다. @since engine-v1.55.0
|
||||
const result = hasPipes(expression)
|
||||
? bindingEngine.evaluatePipeExpression(expression, context, { skipCache: true })
|
||||
: bindingEngine.evaluateExpression(expression, context);
|
||||
logger.log(`initLocal/initGlobal expression evaluated: ${pathOrExpression} -> `, result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
|
||||
import { DataBindingEngine } from './DataBindingEngine';
|
||||
import { extractSingleBinding } from './BindingShape';
|
||||
import { hasPipes } from './PipeRegistry';
|
||||
import { resolveExpressionString } from './helpers/RenderHelpers';
|
||||
import { TranslationEngine, TranslationContext } from './TranslationEngine';
|
||||
import { AuthManager, AuthType } from '../auth/AuthManager';
|
||||
@@ -5392,11 +5394,21 @@ export class ActionDispatcher {
|
||||
if (trimmed.startsWith('{{') && trimmed.endsWith('}}')) {
|
||||
try {
|
||||
const innerExpr = trimmed.slice(2, -2).trim();
|
||||
newComputed[key] = this.bindingEngine.evaluateExpression(
|
||||
innerExpr,
|
||||
computedContext,
|
||||
{ skipCache: true }
|
||||
);
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — 렌더 경로의
|
||||
// computed 재계산(DynamicRenderer)과 같은 규칙이다. 한쪽만 고치면
|
||||
// 같은 computed 가 렌더 직후와 액션 직후에 다른 값이 된다.
|
||||
// @since engine-v1.54.10
|
||||
newComputed[key] = hasPipes(innerExpr)
|
||||
? this.bindingEngine.evaluatePipeExpression(
|
||||
innerExpr,
|
||||
computedContext,
|
||||
{ skipCache: true }
|
||||
)
|
||||
: this.bindingEngine.evaluateExpression(
|
||||
innerExpr,
|
||||
computedContext,
|
||||
{ skipCache: true }
|
||||
);
|
||||
} catch (e) {
|
||||
// 평가 실패 시 기존 값 유지
|
||||
newComputed[key] = currentComputed[key];
|
||||
@@ -5442,11 +5454,19 @@ export class ActionDispatcher {
|
||||
if (trimmed.startsWith('{{') && trimmed.endsWith('}}')) {
|
||||
try {
|
||||
const innerExpr = trimmed.slice(2, -2).trim();
|
||||
newComputed[key] = this.bindingEngine.evaluateExpression(
|
||||
innerExpr,
|
||||
computedContext,
|
||||
{ skipCache: true }
|
||||
);
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — 위 setState
|
||||
// 직후 재계산 블록과 같은 규칙이다. @since engine-v1.54.10
|
||||
newComputed[key] = hasPipes(innerExpr)
|
||||
? this.bindingEngine.evaluatePipeExpression(
|
||||
innerExpr,
|
||||
computedContext,
|
||||
{ skipCache: true }
|
||||
)
|
||||
: this.bindingEngine.evaluateExpression(
|
||||
innerExpr,
|
||||
computedContext,
|
||||
{ skipCache: true }
|
||||
);
|
||||
} catch (e) {
|
||||
// 평가 실패 시 기존 값 유지
|
||||
newComputed[key] = currentComputed[key];
|
||||
@@ -6426,14 +6446,14 @@ export class ActionDispatcher {
|
||||
private evaluateExpression(expr: string, dataContext?: any): any {
|
||||
if (!dataContext) return expr;
|
||||
|
||||
// {{expression}} 패턴 매칭 — 단일 {{...}} 표현식만 매칭
|
||||
const match = expr.match(/^\{\{(.+)\}\}$/);
|
||||
// 복합 표현식 감지: 캡처 그룹 내에 }} 또는 {{가 포함되면
|
||||
// 실제로는 {{A}}/text/{{B}} 형태의 복합 표현식임
|
||||
// (greedy .+가 첫 번째 {{부터 마지막 }}까지 모두 캡처하기 때문)
|
||||
const isSingleExpression = match && !match[1].includes('}}') && !match[1].includes('{{');
|
||||
// 단일 `{{...}}` 판정은 BindingShape 정본을 쓴다. 종전에는 greedy 정규식
|
||||
// (`^\{\{(.+)\}\}$`)이 `{{A}}/text/{{B}}` 까지 잡아, 캡처 안에 `{{`/`}}` 가 있는지
|
||||
// 확인하는 가드를 덧대어 걸러냈다. 정본은 따옴표·중괄호 균형을 추적하므로
|
||||
// 그 가드 없이도 같은 판정을 하고, 식 안의 객체 리터럴(`?? {}`)도 지킨다.
|
||||
// @since engine-v1.55.0
|
||||
const singleExpression = extractSingleBinding(expr);
|
||||
|
||||
if (!isSingleExpression) {
|
||||
if (singleExpression === null) {
|
||||
// {{}} 패턴이 아니거나 복합 표현식({{A}}/text/{{B}})인 경우
|
||||
// resolveBindings가 각 {{...}} 블록을 개별 처리
|
||||
// 복합 표현식에서도 최신 _global/_computed 상태 주입 (Stale Closure 방지)
|
||||
@@ -6459,7 +6479,7 @@ export class ActionDispatcher {
|
||||
return this.bindingEngine.resolveBindings(expr, effectiveContext, { skipCache: true });
|
||||
}
|
||||
|
||||
let expression = match![1].trim();
|
||||
let expression = singleExpression;
|
||||
|
||||
// $args.숫자 형태를 $args[숫자]로 변환 (예: $args.1 → $args[1])
|
||||
expression = expression.replace(/\$args\.(\d+)/g, '$args[$1]');
|
||||
@@ -6502,8 +6522,14 @@ export class ActionDispatcher {
|
||||
}
|
||||
|
||||
try {
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — evaluateExpression 은
|
||||
// `|` 를 JS 비트 OR 로 보므로 인자 있는 파이프는 예외로 아래 catch 에 걸려
|
||||
// 원본 `{{...}}` 문자열이 그대로 서버로 전송되고, 인자 없는 파이프는
|
||||
// 날짜 문자열이 `0` 이 되는 식의 조용한 오답이 된다. @since engine-v1.54.10
|
||||
// DataBindingEngine.evaluateExpression을 사용하여 $t: 토큰 등을 올바르게 처리
|
||||
const result = this.bindingEngine.evaluateExpression(expression, effectiveDataContext);
|
||||
const result = hasPipes(expression)
|
||||
? this.bindingEngine.evaluatePipeExpression(expression, effectiveDataContext, { skipCache: true })
|
||||
: this.bindingEngine.evaluateExpression(expression, effectiveDataContext);
|
||||
|
||||
// 디버그 로그 (init_actions 바인딩 문제 진단용)
|
||||
if (expression.includes('_global.modules')) {
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/**
|
||||
* 바인딩 식의 **형태 판정** 단일 출처
|
||||
*
|
||||
* "이 문자열이 단일 바인딩인가", "이 식이 경로인가 표현식인가 파이프인가" 를 판정하는
|
||||
* 로직은 엔진 곳곳에 복제되어 있었고, 복사본마다 인식 문자 집합이 달라 **같은 식이
|
||||
* 어느 렌더 경로를 타느냐에 따라 다른 결과**를 냈다. 판정은 이 모듈 하나로 모은다.
|
||||
*
|
||||
* 통일하는 것은 **판정뿐**이다. 캐시 정책·예외 처리·DevTools 추적·후처리(번역, raw 마커)는
|
||||
* 지점마다 정당한 이유로 다르므로 각 호출 지점이 그대로 유지한다.
|
||||
*
|
||||
* 순수 함수만 두고 런타임 의존은 `PipeRegistry`(파이프 스캐너) 하나로 제한한다 —
|
||||
* `DataBindingEngine` 은 `import type` 으로만 참조해 순환 import 를 만들지 않는다.
|
||||
*
|
||||
* @since engine-v1.55.0
|
||||
*/
|
||||
|
||||
import { hasPipes } from './PipeRegistry';
|
||||
import type { BindingContext, BindingOptions, DataBindingEngine } from './DataBindingEngine';
|
||||
|
||||
/**
|
||||
* JavaScript 리터럴 값 매핑
|
||||
*
|
||||
* 경로 탐색(`resolve`)이 `true`/`null` 같은 리터럴을 컨텍스트 키로 오인하는 것을 막는다.
|
||||
* `undefined` 는 `Map.get()` 의 기본 반환값과 구분되지 않으므로 `has()` 로 판정한다.
|
||||
*/
|
||||
export const LITERALS = new Map<string, unknown>([
|
||||
['true', true],
|
||||
['false', false],
|
||||
['null', null],
|
||||
['undefined', undefined],
|
||||
]);
|
||||
|
||||
/**
|
||||
* 바인딩 식의 형태
|
||||
*
|
||||
* - `empty`: `{{}}` — 식이 없음 (평가 대상 아님)
|
||||
* - `literal`: `true` / `false` / `null` / `undefined`
|
||||
* - `pipe`: 파이프(`|`)가 포함된 식
|
||||
* - `expression`: 연산자·괄호·리터럴 등이 포함된 JS 표현식
|
||||
* - `path`: 점/대괄호 경로 (`user.profile.name`, `items[0]`)
|
||||
*/
|
||||
export type ExpressionShape = 'empty' | 'literal' | 'pipe' | 'expression' | 'path';
|
||||
|
||||
/**
|
||||
* 문자열 전체가 단일 `{{ }}` 바인딩인지 판정하고 내부 식을 반환
|
||||
*
|
||||
* 판정 기준(정본):
|
||||
* - 따옴표(`'`, `"`, `` ` ``) 안의 중괄호는 세지 않는다 — `{{a['}']}}` 같은 식이 잘리지 않는다.
|
||||
* - 따옴표 안의 백슬래시 이스케이프(`'\''`)는 다음 한 글자를 소비한다.
|
||||
* - 중괄호 균형을 추적해, 외곽 바인딩이 중간에 닫히면(`"{{a}} {{b}}"`) 단일 바인딩이 아니다.
|
||||
* - 균형이 맞지 않으면(`{{a} }}`) 단일 바인딩으로 보지 않는다.
|
||||
* - 결과는 trim 한다.
|
||||
*
|
||||
* `{{}}` 는 빈 문자열을 반환한다 — "바인딩이긴 하나 식이 없음"이며, 호출 지점은
|
||||
* `classifyExpression` 이 돌려주는 `'empty'` 로 이 상태를 구분해 처리한다.
|
||||
*
|
||||
* @param value 검사할 문자열
|
||||
* @returns 단일 바인딩이면 내부 식(trim), 아니면 null
|
||||
*/
|
||||
export function extractSingleBinding(value: string): string | null {
|
||||
if (typeof value !== 'string') return null;
|
||||
if (!value.startsWith('{{') || !value.endsWith('}}') || value.length < 4) return null;
|
||||
|
||||
const inner = value.slice(2, -2);
|
||||
let quote: string | null = null;
|
||||
let depth = 0;
|
||||
|
||||
for (let i = 0; i < inner.length; i++) {
|
||||
const char = inner[i];
|
||||
|
||||
if (quote !== null) {
|
||||
if (char === '\\') {
|
||||
i++; // 이스케이프된 문자 1개 소비
|
||||
continue;
|
||||
}
|
||||
if (char === quote) quote = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '"' || char === "'" || char === '`') {
|
||||
quote = char;
|
||||
continue;
|
||||
}
|
||||
if (char === '{') {
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
if (char === '}') {
|
||||
if (depth > 0) {
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
// 깊이 0 에서의 `}` → 외곽 바인딩이 중간에 닫혔거나 균형이 맞지 않는다.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return depth === 0 ? inner.trim() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 문자열에서 찾은 `{{ }}` 바인딩 한 건
|
||||
*/
|
||||
export interface ScannedBinding {
|
||||
/** 원본 문자열에서 `{{` 가 시작하는 인덱스 */
|
||||
start: number;
|
||||
/** 원본 문자열에서 `}}` 다음 인덱스 (slice 종료 지점) */
|
||||
end: number;
|
||||
/** `{{ }}` 를 벗긴 내부 식 (trim 하지 않은 원본) */
|
||||
expr: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 문자열 안의 모든 `{{ }}` 바인딩 위치를 스캔
|
||||
*
|
||||
* 정규식 `\{\{([^}]+)\}\}` 은 식 안에 `}` 가 들어가면(`{{(row.meta ?? {}) | json}}`)
|
||||
* 매칭에 실패한다. `String.replace` 는 매칭이 없으면 입력을 그대로 돌려주므로,
|
||||
* 그 결과는 예외도 경고도 없이 **원본 `{{...}}` 문자열이 화면에 노출**되는 것이었다.
|
||||
*
|
||||
* 이 스캐너는 따옴표와 중괄호 깊이를 추적해 바인딩의 끝을 정확히 찾는다.
|
||||
* 닫히지 않은 `{{` 는 바인딩으로 보지 않고 리터럴로 남긴다(종전 정규식과 같은 결과).
|
||||
*
|
||||
* @param template 검사할 문자열
|
||||
* @returns 발견된 바인딩 목록 (등장 순서)
|
||||
*
|
||||
* @since engine-v1.55.1
|
||||
*/
|
||||
export function scanBindings(template: string): ScannedBinding[] {
|
||||
const found: ScannedBinding[] = [];
|
||||
if (typeof template !== 'string') return found;
|
||||
|
||||
for (let i = 0; i < template.length - 1; i++) {
|
||||
if (template[i] !== '{' || template[i + 1] !== '{') continue;
|
||||
|
||||
let quote: string | null = null;
|
||||
let depth = 0;
|
||||
let closed = -1;
|
||||
|
||||
for (let j = i + 2; j < template.length; j++) {
|
||||
const char = template[j];
|
||||
|
||||
if (quote !== null) {
|
||||
if (char === '\\') {
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
if (char === quote) quote = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '"' || char === "'" || char === '`') {
|
||||
quote = char;
|
||||
continue;
|
||||
}
|
||||
if (char === '{') {
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
if (char === '}') {
|
||||
if (depth > 0) {
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
if (template[j + 1] === '}') {
|
||||
closed = j;
|
||||
break;
|
||||
}
|
||||
// 깊이 0 에서 짝이 맞지 않는 `}` — 바인딩이 아니다.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (closed === -1) continue; // 닫히지 않음 → 리터럴로 취급
|
||||
|
||||
found.push({ start: i, end: closed + 2, expr: template.slice(i + 2, closed) });
|
||||
i = closed + 1; // 루프의 i++ 와 합쳐 바인딩 다음으로 이동
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* 복잡한 표현식 여부 판정
|
||||
*
|
||||
* true 면 `evaluateExpression`(JS 평가)으로, false 면 `resolve`(경로 탐색)로 보낸다.
|
||||
*
|
||||
* 인식 문자 집합은 종전 6개 방언 중 **최대집합**을 정본으로 삼는다. 좁은 방언은
|
||||
* 배열/객체 리터럴(`['a','b']`)이나 따옴표 키 인덱싱(`query['sales_status[]']`)을
|
||||
* "단순 경로" 로 오판해 경로 탐색으로 보냈고, 그 결과는 조용한 `undefined` 였다.
|
||||
*
|
||||
* `|`(파이프)도 집합에 포함되지만, 분류 순서상 파이프가 먼저 걸러지므로
|
||||
* 파이프 식이 이 판정에 도달하지 않는다(`classifyExpression` 참조).
|
||||
*
|
||||
* @param expr 판정할 식 (`{{ }}` 제거된 상태)
|
||||
* @returns 복잡한 표현식 여부
|
||||
*/
|
||||
export function isComplexExpression(expr: string): boolean {
|
||||
return /[?:|&!+\-*/<>=()[\]{}]/.test(expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 바인딩 식의 형태 분류
|
||||
*
|
||||
* 분류 순서가 곧 라우팅 우선순위다: 빈 식 → 리터럴 → 파이프 → 표현식 → 경로.
|
||||
* 리터럴을 파이프보다 먼저 보는 이유는 `true`/`null` 이 경로로 해석되면 안 되기 때문이고,
|
||||
* 리터럴 문자열 자체에는 파이프가 들어갈 수 없어 순서 충돌이 없다.
|
||||
*
|
||||
* @param expr 분류할 식 (`{{ }}` 제거된 상태)
|
||||
* @returns 식의 형태
|
||||
*/
|
||||
export function classifyExpression(expr: string): ExpressionShape {
|
||||
const trimmed = typeof expr === 'string' ? expr.trim() : '';
|
||||
if (trimmed === '') return 'empty';
|
||||
if (LITERALS.has(trimmed)) return 'literal';
|
||||
if (hasPipes(trimmed)) return 'pipe';
|
||||
if (isComplexExpression(trimmed)) return 'expression';
|
||||
return 'path';
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 바인딩 값 해석 옵션
|
||||
*/
|
||||
export interface ResolveSingleBindingOptions {
|
||||
/** 캐시 사용 안 함 (반복 렌더 등) */
|
||||
skipCache?: boolean;
|
||||
|
||||
/** DevTools 표현식 추적 정보 */
|
||||
trackingInfo?: { componentId?: string; componentName?: string; propName?: string };
|
||||
|
||||
/**
|
||||
* 평가 실패 시 호출된다. 반환값이 해석 결과가 된다.
|
||||
* 미지정이면 예외를 그대로 전파한다 — 실패 정책은 호출 지점의 몫이다.
|
||||
*/
|
||||
onError?: (error: unknown, expr: string) => unknown;
|
||||
|
||||
/**
|
||||
* 빈 바인딩(`{{}}`)을 만났을 때 호출된다. 미지정이면 조용히 `undefined` 를 반환한다.
|
||||
*/
|
||||
onEmpty?: (expr: string) => void;
|
||||
|
||||
// `format` 옵션은 두지 않는다.
|
||||
//
|
||||
// 이 함수는 **판정과 라우팅**만 담당하고 결과를 원본 타입으로 돌려준다. 서식은
|
||||
// 호출 지점마다 규칙이 다르다 — `text` 노드는 문자열이 필요하고, prop 은 배열/객체/
|
||||
// boolean 을 그대로 넘겨야 한다(원본 타입 소실이 engine-v1.54.9 에서 고친 결함이다).
|
||||
// 여기에 서식을 얹으면 그 결함을 옵션 형태로 되살리는 셈이라, 서식은 호출 지점에 남긴다.
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 바인딩 식을 형태에 따라 알맞은 평가기로 보내 값을 해석
|
||||
*
|
||||
* 형태 판정(`classifyExpression`)과 라우팅만 담당하고, 실행은 전달받은 엔진에 위임한다.
|
||||
* 결과는 **원본 타입**이다 — 문자열 서식이 필요한 호출 지점이 스스로 적용한다.
|
||||
*
|
||||
* @param expr 단일 바인딩 내부 식 (`{{ }}` 와 `raw:` 접두사가 제거된 상태)
|
||||
* @param context 데이터 컨텍스트
|
||||
* @param engine DataBindingEngine 인스턴스
|
||||
* @param options 해석 옵션
|
||||
* @returns 해석된 값 (빈 바인딩이면 undefined)
|
||||
*/
|
||||
export function resolveSingleBindingValue(
|
||||
expr: string,
|
||||
context: BindingContext,
|
||||
engine: DataBindingEngine,
|
||||
options?: ResolveSingleBindingOptions,
|
||||
): unknown {
|
||||
const trimmed = typeof expr === 'string' ? expr.trim() : '';
|
||||
const shape = classifyExpression(trimmed);
|
||||
const bindingOptions: BindingOptions | undefined = options?.skipCache
|
||||
? { skipCache: true }
|
||||
: undefined;
|
||||
|
||||
if (shape === 'empty') {
|
||||
options?.onEmpty?.(expr);
|
||||
return undefined;
|
||||
}
|
||||
if (shape === 'literal') {
|
||||
return LITERALS.get(trimmed);
|
||||
}
|
||||
|
||||
try {
|
||||
if (shape === 'pipe') {
|
||||
return engine.evaluatePipeExpression(trimmed, context, bindingOptions, options?.trackingInfo);
|
||||
}
|
||||
if (shape === 'expression') {
|
||||
return engine.evaluateExpression(trimmed, context, options?.trackingInfo);
|
||||
}
|
||||
return engine.resolve(trimmed, context, bindingOptions, options?.trackingInfo);
|
||||
} catch (error) {
|
||||
if (options?.onError) return options.onError(error, trimmed);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,125 @@
|
||||
>
|
||||
> 형식: [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)
|
||||
|
||||
## [engine-v1.56.3] - 2026-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- 반복 렌더 경로(`renderItemChildren`)가 `raw:` 바인딩의 **번역 면제 마커를 벗기지 않고** React 로 넘겨, 화면에 Unicode Noncharacter 두 글자(`U+FDD0`/`U+FDD1`)가 그대로 실렸다. 마커는 번역 패스가 raw 값을 건너뛰게 하려는 내부 표식이므로 값이 화면으로 나가기 전에 제거돼야 한다. 단발 렌더 경로는 `resolveTranslationsDeep` 안에서 벗기지만 반복 경로에는 그 패스가 없었다.
|
||||
- `rawMarkers.stripRawDeep()` 을 추가하고 반복 경로의 React 경계(props / children / 컴포넌트 id) 전부에 적용했다. 마커가 없으면 원본 참조를 그대로 돌려주므로 반복 렌더에서 추가 할당이 생기지 않는다. 배열·객체 리프까지 재귀하되 이미 렌더된 React 엘리먼트는 순회하지 않는다.
|
||||
|
||||
## [engine-v1.56.2] - 2026-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- `DataBindingEngine.evaluateExpression` — 컨텍스트에 함수 파라미터 이름이 될 수 없는 키(`sales_status[]`, `data-id`, 예약어 등)가 **하나라도** 있으면 평가 함수 생성 자체가 SyntaxError 로 실패해, 그 키를 쓰지 않는 식까지 **같은 컨텍스트에서는 전부** 평가되지 못했다. 이제 그런 키를 파라미터 목록에서 제외한다. 제외해도 잃는 것은 없다 — 식 안에서 맨이름으로 참조할 수 없는 키였고, 실제 작성은 `query['sales_status[]']` 처럼 상위 객체를 거치므로 그대로 동작한다.
|
||||
- 증상은 예외나 화면 오류가 아니라 **값이 비어 보이는 것**이었다(평가 실패 → 폴백). 판정 통일(engine-v1.55.0) 이후 이 경로로 들어오는 식이 늘면서 노출 범위가 넓어졌다.
|
||||
|
||||
## [engine-v1.56.1] - 2026-08-02
|
||||
|
||||
### Added
|
||||
|
||||
- `G7Core.evaluateCondition(condition, extraContext?)` — 템플릿 컴포넌트가 조건 문자열을 엔진과 같은 규칙으로 평가할 수 있게 노출한다. 전역/로컬/계산 상태를 컨텍스트에 병합해 넘기므로, `{{_local.expanded}}` 같은 조건도 컴포넌트에서 그대로 쓸 수 있다.
|
||||
|
||||
### Changed
|
||||
|
||||
- `TranslationEngine` 의 `$t:` 파라미터 값 평가가 `new Function` 대신 `DataBindingEngine.evaluateExpression` 을 쓴다. 종전 자체 평가기는 `$localized(...)`·`$t(...)`·`$uuid()` 헬퍼와 optional chaining 전처리, 표현식 함수 캐시를 쓰지 못했고 실패 시 조용히 빈 문자열을 돌려줬다 — 번역 문구에서 값만 사라지므로 원인 파악이 어려웠다.
|
||||
- `$t:key|name=값` 의 `|` 는 파라미터 구분자이므로 이 자리에서는 파이프를 지원하지 않는다(별개 문법 영역). 파이프 분기 스캔의 면제 목록에 사유와 함께 등재했다.
|
||||
|
||||
## [engine-v1.56.0] - 2026-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
#### `iteration` 과 `if` 를 함께 쓴 목록이 통째로 렌더되지 않던 문제
|
||||
|
||||
- `DynamicRenderer` — `if` 가 항목 변수(`{{user.is_active}}` 등)를 참조하면, 조건 평가가 iteration 분기보다 먼저 걸리는데 그 시점 컨텍스트에는 항목 변수가 없어 조건이 항상 false 가 됐다. 결과는 **목록 전체 미렌더**이며 예외도 경고도 남지 않는다. 이제 iteration 이 있으면 부모 시점 조건으로 끊지 않고, 항목별 컨텍스트에서 자식이 같은 `if` 를 다시 평가한다. 같은 작성이 반복 렌더 경로(`cellChildren` 등)에서는 정상 동작했기 때문에 재현 위치를 특정하기 어려운 결함이었다.
|
||||
- 외곽 변수만 참조하는 `if` 는 결과가 종전과 같다(항목 수만큼 재평가되지만 렌더 사이클 캐시가 흡수한다).
|
||||
|
||||
#### 반복 렌더 경로에서 문자열 중간의 `$t:` 가 번역되지 않던 문제
|
||||
|
||||
- `RenderHelpers.renderItemChildren` — 종전에는 `$t:` 로 **시작**하는 문자열만 번역해서 `"27$t:admin.count_suffix"` 같은 중간 토큰이 원본 키 그대로 화면에 노출됐다(반복 서브트리 내 실사용 2건). 일반 컴포넌트 경로는 이미 처리하던 형태다. JSON 구조 문자열(`{...}`/`[...]`) 안의 `$t:` 는 데이터의 일부이므로 번역하지 않는 가드도 함께 이식했다.
|
||||
- 이 처리는 `$t:defer:` 분기보다 **뒤에** 둔다 — `$t:defer:` 는 이 경로가 항목 컨텍스트와 함께 처리하도록 남겨 둔 것이고, 반복 서브트리 내 실사용 23건이 그 순서에 의존한다.
|
||||
- 표현식 평가 결과가 배열·객체인 경우에도 리프 문자열의 `$t:` 까지 번역한다. 종전에는 문자열 결과만 검사해 배열 안의 키가 번역되지 않았다.
|
||||
|
||||
### Added
|
||||
|
||||
- 반복 렌더 경로가 `$switch`/`$cases` 객체를 값으로 해석한다. 종전에는 객체가 그대로 컴포넌트에 전달되어 조용히 아무 일도 일어나지 않았다(일반 경로는 지원). 평가는 `DataBindingEngine.resolveSwitch` 에 위임한다.
|
||||
- 반복 렌더 경로도 액션 정의 객체를 선평가하지 않는다. 콜백 prop 에 담긴 액션 정의를 렌더 시점에 해소하면 내부 `{{_local.xxx}}` 가 그 시점 값으로 고정되어, 실행 시점의 최신 상태를 보지 못한다.
|
||||
- `DynamicRenderer` 의 iteration 에 `{item_var}_index` 자동 변수를 주입한다(반복 렌더 경로에는 이미 있던 것).
|
||||
|
||||
두 `Added` 항목과 `_index` 주입은 저장소 실사용 0건으로, 같은 작성이 경로에 따라 다르게 동작하는 것을 없애기 위한 정렬이다.
|
||||
|
||||
## [engine-v1.55.1] - 2026-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
#### 보간 문자열의 바인딩 탐색을 정규식에서 스캐너로 교체
|
||||
|
||||
- `DataBindingEngine.resolveBindings` — 바인딩 위치를 `BindingShape.scanBindings()` 로 찾는다. 종전 정규식(`\{\{([^}]+)\}\}`)은 식 안에 `}` 가 들어가면 매칭하지 못했고, `String.replace` 는 매칭이 없을 때 입력을 그대로 돌려주므로 `"a {{x ?? {}}} b"` 같은 문자열이 **원본 `{{...}}` 그대로 화면에 노출**됐다. 스캐너는 따옴표와 중괄호 깊이를 추적하며, 닫히지 않은 `{{` 는 종전처럼 리터럴로 남긴다.
|
||||
|
||||
#### 표현식 하나의 실패가 컴포넌트의 props 해석 전체를 중단시키던 문제
|
||||
|
||||
- `DataBindingEngine.resolveObject` — key 단위로 실패를 격리한다. 이 메서드에는 상위 catch 가 없어서, 표현식 하나가 던지면 그 컴포넌트의 props 가 통째로 해석되지 않았다(중첩 prop 표현식은 특히 렌더 도중 throw 로 이어졌다). 이제 실패한 key 만 `undefined` 가 되고 경고가 남으며, 나머지 key 와 형제 컴포넌트는 정상 렌더된다. 중첩 객체는 재귀 호출이 같은 규칙으로 처리하므로 실패가 가장 안쪽 key 에서 멈춘다.
|
||||
|
||||
### Changed
|
||||
|
||||
- `DynamicRenderer` 의 중첩 객체 해석에도 컴포넌트별 `skipBindingKeys` 를 전달한다. 종전에는 전달하지 않아 코어 기본 4개만 적용됐고, 컴포넌트가 "내가 직접 반복 렌더한다" 고 선언한 키가 중첩 위치에서는 선평가되어 항목 컨텍스트 없이 stale 값으로 고정될 수 있었다. 저장소 전수 스캔 결과 해당 위치는 **0건**이라 예방 목적의 변경이다(선언된 키: `DataGrid`·`CardGrid`·`RichSelect`·`DropdownButton`).
|
||||
|
||||
## [engine-v1.55.0] - 2026-08-02
|
||||
|
||||
### Changed
|
||||
|
||||
#### 바인딩 식의 형태 판정을 단일 모듈로 통일
|
||||
|
||||
같은 작성 문법이 **어느 렌더 경로를 타느냐에 따라 다른 결과**를 내던 원인은 판정 로직이 복제되면서 복사본마다 인식 문자 집합이 갈라졌기 때문이다. "이 문자열이 단일 바인딩인가" 를 판정하는 구현이 4종 + 정규식 4종, "이 식이 표현식인가 경로인가" 를 판정하는 정규식이 6종 있었다. `template-engine/BindingShape.ts` 로 판정을 모으고 14개 지점이 이를 쓰도록 바꿨다.
|
||||
|
||||
통일한 것은 **판정뿐**이다. 캐시 정책·예외 처리·DevTools 추적·후처리(번역, raw 마커)는 지점마다 정당한 이유로 다르므로 그대로 두었다.
|
||||
|
||||
- **단일 바인딩 판정**: 따옴표(`'` `"` `` ` ``) 안의 중괄호를 세지 않고, 따옴표 안의 백슬래시 이스케이프를 소비하며, 중괄호 균형을 추적한다. 종전 greedy 정규식(`^\{\{(.+)\}\}$`)은 `"{{a}}-{{b}}"` 같은 보간 문자열까지 단일 바인딩으로 오판했다 — 액션 경로는 덧댄 가드로 막고 있었지만 데이터소스 params 와 `initLocal`/`initGlobal` 값 추출에는 그 가드가 없었다.
|
||||
- **표현식/경로 판정**: 종전 6개 방언 중 **최대집합**을 정본으로 삼았다. 좁은 방언은 `query['sales_status[]']` 같은 따옴표 키 인덱싱과 배열/객체 리터럴을 "단순 경로" 로 오판해 경로 탐색으로 보냈고, 결과는 조용한 `undefined` 였다. 저장소 전수 스캔 기준 **63개 식**의 라우팅이 바뀐다(따옴표 대괄호 22, 숫자 인덱싱 39, 기타 2). 숫자 인덱싱은 두 경로의 결과가 동치임을 4개 컨텍스트(존재/범위밖/null/중첩 미정의)로 확인했고, 따옴표 대괄호 22건은 **지금까지 `undefined` 였던 값이 실제 값으로 나타난다**.
|
||||
- **빈 바인딩(`{{}}`) 봉인**: `undefined` + 경고로 처리한다. 종전에는 빈 경로가 경로 탐색으로 들어가 **컨텍스트 객체 전체**가 값으로 반환됐고, 그것이 문자열로 서식되면 전역 상태가 통째로 화면이나 요청에 실릴 수 있었다. 안전망으로 `resolvePath` 진입 시에도 빈 경로를 차단한다. 레이아웃 실사용은 0건이며 런타임에 조립되는 문자열에서 발생할 수 있는 경로다.
|
||||
- 리터럴(`true`/`false`/`null`/`undefined`) 처리도 한 곳으로 모았다. 종전에는 조건 두 지점만 리터럴을 알아 `{{true}}` 가 prop 자리에서는 경로로 탐색돼 `undefined` 가 됐다. 저장소의 리터럴 단일 바인딩 60건은 전부 `if`/`condition` 자리라 실제 표시에는 변화가 없다.
|
||||
|
||||
- 선행 하네스: `__tests__/BindingShape.routingParity.test.ts` — 저장소의 모든 레이아웃 JSON 에서 단일 바인딩을 수집해 **구 방언들과 신 정본의 판정을 대조**하고, 판정이 바뀌는 식 전체를 소속 파일과 함께 스냅샷으로 고정한다. 새 레이아웃이 라우팅 변경 대상 식을 도입하면 이 테스트가 깨진다.
|
||||
|
||||
## [engine-v1.54.10] - 2026-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
#### 파이프(`|`)를 해석하지 않고 JS 비트 OR 로 평가하던 지점 8곳
|
||||
|
||||
`{{식 | 파이프}}` 를 `evaluateExpression` 으로 보내면 `|` 가 JS 비트 OR 로 평가된다. 인자 있는 파이프(`date('YYYY-MM-DD')`)는 함수가 아니라며 예외를 던지고, 인자 없는 파이프(`| number`)는 날짜 문자열이 `0` 이 되는 식의 조용한 오답이 된다. engine-v1.54.3 이 렌더 경로를 정리했으나 아래 지점들은 남아 있었다. 모두 `hasPipes()` 선분기 + `evaluatePipeExpression` 으로 정렬했다.
|
||||
|
||||
- **액션 params** (`ActionDispatcher`) — 예외가 catch 에 걸리면 catch 가 **원본 `{{...}}` 문자열을 그대로 반환**했고, 그 값이 요청 본문·쿼리스트링에 실려 서버로 전송됐다. 화면에는 아무 표시도 나지 않는다.
|
||||
- **데이터소스 params** (`DataSourceManager`) — 동일. 쿼리스트링에 `%7B%7B` 로 인코딩된 리터럴이 실렸다.
|
||||
- **`iteration` 의 `source`** (`RenderHelpers.resolveIterationSource`) — 결과가 배열이 아니게 되어 반복 대상이 사라진다. **목록 전체가 렌더되지 않으며** 예외도 경고도 남지 않는다. DynamicRenderer 와 `renderItemChildren` 이 공유하는 함수라 한 곳 수정으로 양 경로가 함께 해소된다.
|
||||
- **`expandContext`** (`G7CoreGlobals.renderExpandContent`) — 확장 행에 넘기는 값에 파이프가 적용되지 않았다.
|
||||
- **computed 정의식** — 렌더 경로(`DynamicRenderer`)와 액션 경로(`ActionDispatcher` 의 setState 직후·커스텀 핸들러 직후 재계산 2곳), 그리고 레이아웃 편집기 미리보기(`computedRecipeEngine`)까지 네 곳이 같은 결함을 복제하고 있었다. 셋 다 실패 시 "이전 값 유지" 라 computed 가 영원히 갱신되지 않는다. 한 곳만 고치면 같은 computed 가 렌더 직후와 액션 직후에 다른 값이 되므로 함께 정렬했다.
|
||||
- **`slot` 표현식** (`DynamicRenderer`) — 평가 결과가 문자열이 아니게 되어 `typeof result === 'string'` 가드에 걸려 슬롯 미등록으로 조용히 흡수됐다(그 컴포넌트가 화면에서 사라진다).
|
||||
|
||||
`blur_until_loaded` 는 종전대로 파이프를 지원하지 않는다 — truthiness 게이트라 파이프 결과(대개 비어있지 않은 문자열)가 항상 참이 되어 블러가 영구히 켜진다.
|
||||
|
||||
- 회귀 방지선: 표현식 평가 지점을 **모집단으로 스캔**하는 테스트를 추가했다(`__tests__/pipeBranchParity.test.ts`). 엔진 소스에서 `evaluateExpression` 호출 지점을 전부 찾아 파이프 분기 가드 유무를 검사하고, 의도적 미지원 지점만 사유와 함께 면제 목록에 둔다. 면제 항목이 코드에서 사라지면 그것도 실패한다. 실제로 이 스캔이 중복 블록 하나(액션 커스텀 핸들러 직후 재계산)를 잡아냈다.
|
||||
- 기존 동작 무영향: 레이아웃 전수 스캔 기준 이 8지점의 파이프 실사용은 0건이다. 논리 OR(`||`)과 따옴표 안의 `|`(다국어 파라미터 구분자)는 종전대로 파이프로 인식하지 않는다.
|
||||
|
||||
## [engine-v1.54.9] - 2026-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
#### 파이프 평가를 문자열 보간에 위임하면서 생긴 비대칭 3건
|
||||
|
||||
engine-v1.54.3 이 파이프 분기를 `resolveBindings(\`{{식}}\`)` 위임으로 처리하면서, 값이 필요한 지점까지 "문자열을 조립해 보간기에 넣고 문자열을 돌려받는" 경로를 타게 됐다. 평가 자체를 `DataBindingEngine.evaluatePipeExpression()` 으로 분리하고, 값이 필요한 6개 호출 지점이 이 메서드를 직접 호출하도록 바꿨다. `resolveBindings` 는 그 결과에 `formatValue` 를 적용해 종전과 같은 문자열을 돌려준다 — 보간의 계약은 그대로다.
|
||||
|
||||
- **식 안에 중괄호가 있으면 원본 `{{...}}` 문자열이 화면에 노출됐다.** 위임 대상인 `BINDING_PATTERN`(`/\{\{([^}]+)\}\}/g`)은 `}` 를 포함한 식을 매칭하지 못하고, `String.replace` 는 매칭이 없으면 입력을 그대로 돌려준다. `{{(row.meta ?? {}) | json}}` 같은 식이 평가되지 않은 채 그대로 렌더됐다. 예외도 경고도 남지 않는다. 이제 문자열 조립을 거치지 않으므로 정규식의 한계와 무관하다(`BINDING_PATTERN` 자체의 교정은 별건이다 — 보간 경로에는 아직 남아 있다).
|
||||
- **`_computed` 파이프가 컴포넌트 간 stale 값을 전파했다.** `DynamicRenderer` 의 props 해석에서 파이프 분기만 `getComputedAwareOptions` 를 거치지 않아, 다른 분기가 `skipCache` 로 회피하던 영구 캐시(30초)에 `_computed.x` 가 저장됐다. `_computed` 는 컴포넌트마다 `_local` 기반으로 재계산되므로, 먼저 렌더된 컴포넌트의 값이 뒤따르는 컴포넌트에 그대로 나타났다. 이제 파이프 분기도 같은 옵션을 쓴다(`$computed` alias 포함).
|
||||
- **파이프 결과가 항상 문자열로 서식됐다.** `resolveBindings` 는 보간이 목적이라 `formatValue` 를 적용한다. 그 결과 `{{row.tags | keys}}` 가 배열이 아니라 `"[\"a\",\"b\"]"` 로, `{{row.flags | first}}` 가 `false` 대신 `"false"`(truthy!) 로 prop·조건에 전달됐다. 값이 필요한 지점은 이제 원본 타입을 받는다 — 파이프가 없는 단일 바인딩(`resolve`)·복합식(`evaluateExpression`)과 같은 규칙이다.
|
||||
|
||||
전환한 호출 지점: `DataBindingEngine.resolveObject` / `DynamicRenderer` 의 props·`text` / `ConditionEvaluator.evaluateStringCondition` / `RenderHelpers` 의 `evaluateIfCondition`·`renderItemChildren`.
|
||||
|
||||
- 실패 정책도 지점별로 정렬했다. `evaluatePipeExpression` 은 평가 실패를 그대로 던지고, 각 호출 지점이 같은 자리의 복합식 분기와 동일하게 처리한다(경고 후 `undefined` / `''` / 조건 `false`). 종전에는 위임 대상이 예외를 삼키고 원본 `{{...}}` 문자열을 돌려주어, 조건 자리에서는 그 문자열이 truthy 로 평가돼 반대 분기를 탔다. `resolveObject` 는 상위 catch 가 없으므로 key 단위로 격리한다.
|
||||
- 조건 두 지점(`evaluateStringCondition`, `evaluateIfCondition`)은 파이프 식의 `raw:` 접두사를 제거한 뒤 평가한다. 위임 형태에서는 `resolveBindings` 가 접두사를 벗겨 줬으나 직접 호출에서는 그 단계가 없다. `rawMarkers.stripRawPrefix()` 로 공통화했다.
|
||||
- 기존 동작 무영향: 레이아웃 전수 스캔 기준 파이프 단일 바인딩은 32건(고유 29)이며 **전부 노드 `text`**, 사용 파이프는 `date`/`datetime`/`number` 로 모두 문자열을 반환한다. 반복 렌더 서브트리·액션 params·데이터소스 params 내 파이프와 중괄호를 포함한 파이프 식은 각각 0건이다.
|
||||
|
||||
## [engine-v1.54.8] - 2026-08-01
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -11,11 +11,30 @@
|
||||
import { createLogger } from '../utils/Logger';
|
||||
import { TranslationEngine } from './TranslationEngine';
|
||||
import { hasPipes, splitPipes, executePipeChain } from './PipeRegistry';
|
||||
import { classifyExpression, extractSingleBinding, isComplexExpression, LITERALS, scanBindings } from './BindingShape';
|
||||
import { RAW_PREFIX, wrapRaw, wrapRawDeep } from './rawMarkers';
|
||||
import type { G7DevToolsInterface } from './G7CoreGlobals';
|
||||
|
||||
const logger = createLogger('DataBindingEngine');
|
||||
|
||||
/**
|
||||
* 함수 파라미터 이름으로 쓸 수 있는 식별자 패턴.
|
||||
*
|
||||
* 표현식 평가는 컨텍스트 키를 `new Function` 의 파라미터로 넘긴다. 파라미터가 될 수 없는
|
||||
* 이름이 하나라도 섞이면 함수 생성이 통째로 실패하므로 미리 걸러낸다.
|
||||
*/
|
||||
const VALID_IDENTIFIER_PATTERN = /^[$A-Za-z_][$A-Za-z0-9_]*$/;
|
||||
|
||||
/** 파라미터 이름으로 쓸 수 없는 예약어 (엄격 모드 예약어 포함) */
|
||||
const RESERVED_WORDS = new Set([
|
||||
'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',
|
||||
'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for',
|
||||
'function', 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let',
|
||||
'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'static',
|
||||
'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void',
|
||||
'while', 'with', 'yield', 'arguments', 'eval',
|
||||
]);
|
||||
|
||||
/**
|
||||
* G7Core.devTools 인터페이스 가져오기
|
||||
*
|
||||
@@ -79,6 +98,39 @@ export interface BindingOptions {
|
||||
skipCache?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* DevTools 표현식 추적 정보
|
||||
*
|
||||
* 평가 지점(컴포넌트/prop)을 DevTools 로그에 남기기 위한 부가 정보.
|
||||
* 값 해석 결과에는 영향을 주지 않는다.
|
||||
*/
|
||||
export interface ExpressionTrackingInfo {
|
||||
/**
|
||||
* 평가를 요청한 컴포넌트 ID
|
||||
*/
|
||||
componentId?: string;
|
||||
|
||||
/**
|
||||
* 평가를 요청한 컴포넌트 이름
|
||||
*/
|
||||
componentName?: string;
|
||||
|
||||
/**
|
||||
* 평가 대상 prop 이름
|
||||
*/
|
||||
propName?: string;
|
||||
|
||||
/**
|
||||
* DevTools 로그에 기록할 평가 진입점 이름 (기본: 호출된 메서드명)
|
||||
*/
|
||||
method?: string;
|
||||
|
||||
/**
|
||||
* DevTools 로그에 표시할 원본 식 (기본: `{{expr}}`)
|
||||
*/
|
||||
displayExpression?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 캐시 엔트리 인터페이스
|
||||
*/
|
||||
@@ -114,13 +166,6 @@ export class DataBindingError extends Error {
|
||||
* {{variable}} 문법을 파싱하고 실제 데이터로 치환
|
||||
*/
|
||||
export class DataBindingEngine {
|
||||
/**
|
||||
* 바인딩 패턴 정규식
|
||||
*
|
||||
* {{variable}}, {{object.property}}, {{array[0]}} 등을 매칭
|
||||
*/
|
||||
private static readonly BINDING_PATTERN = /\{\{([^}]+)\}\}/g;
|
||||
|
||||
/**
|
||||
* 경로 파싱 정규식
|
||||
*
|
||||
@@ -285,7 +330,7 @@ export class DataBindingEngine {
|
||||
? { ...context, $computed: context._computed }
|
||||
: context;
|
||||
|
||||
return template.replace(DataBindingEngine.BINDING_PATTERN, (_match, path) => {
|
||||
const replaceBinding = (_match: string, path: string): string => {
|
||||
// 공백 제거
|
||||
const trimmedPath = path.trim();
|
||||
|
||||
@@ -299,82 +344,16 @@ export class DataBindingEngine {
|
||||
}
|
||||
|
||||
// 파이프 함수 처리 (예: {{post.created_at | date}})
|
||||
// 파이프가 있으면 먼저 분리하여 처리
|
||||
// 평가 자체는 evaluatePipeExpression 이 담당하고, 여기서는 보간의 목적에 맞게
|
||||
// 결과를 문자열로 서식한다. @since engine-v1.54.9
|
||||
if (hasPipes(effectivePath)) {
|
||||
const pipeStartTime = devTools?.isEnabled() ? performance.now() : 0;
|
||||
try {
|
||||
const [baseExpr, pipes] = splitPipes(effectivePath);
|
||||
|
||||
// 기본 표현식 평가 (파이프 적용 전)
|
||||
let value: any;
|
||||
let fromCache = false;
|
||||
const isBaseExpression = /[?:|&!+\-*/<>=()]/.test(baseExpr) || /\[['"]/.test(baseExpr);
|
||||
// 동적 경로 여부 판단
|
||||
const isDynamicPipeBase = baseExpr.startsWith('_global') ||
|
||||
baseExpr.startsWith('_local') ||
|
||||
baseExpr.startsWith('_isolated') ||
|
||||
baseExpr.startsWith('$parent');
|
||||
|
||||
if (opts.skipCache) {
|
||||
// skipCache: 모든 캐시 무시
|
||||
if (isBaseExpression) {
|
||||
value = this.evaluateExpression(baseExpr, extendedContext);
|
||||
} else {
|
||||
value = this.resolvePath(baseExpr, extendedContext, opts);
|
||||
}
|
||||
} else if (isBaseExpression) {
|
||||
// 복잡한 표현식: 렌더 사이클 캐시 사용
|
||||
const exprCached = this.getFromRenderCycleCache(`expr:${baseExpr}`);
|
||||
if (exprCached !== undefined) {
|
||||
value = exprCached;
|
||||
fromCache = true;
|
||||
} else {
|
||||
value = this.evaluateExpression(baseExpr, extendedContext);
|
||||
this.saveToRenderCycleCache(`expr:${baseExpr}`, value);
|
||||
}
|
||||
} else if (isDynamicPipeBase) {
|
||||
// 동적 경로: 렌더 사이클 캐시 사용
|
||||
const renderCycleCached = this.getFromRenderCycleCache(baseExpr);
|
||||
if (renderCycleCached !== undefined) {
|
||||
value = renderCycleCached;
|
||||
fromCache = true;
|
||||
} else {
|
||||
value = this.resolvePath(baseExpr, extendedContext, opts);
|
||||
this.saveToRenderCycleCache(baseExpr, value);
|
||||
}
|
||||
} else {
|
||||
// 정적 경로: 영구 캐시 사용
|
||||
const cached = this.getFromCache(baseExpr);
|
||||
if (cached !== undefined) {
|
||||
value = cached;
|
||||
fromCache = true;
|
||||
} else {
|
||||
value = this.resolvePath(baseExpr, extendedContext, opts);
|
||||
this.saveToCache(baseExpr, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 파이프 체인 실행
|
||||
const result = executePipeChain(value, pipes);
|
||||
|
||||
// DevTools: 파이프 표현식 평가 추적
|
||||
if (devTools?.isEnabled()) {
|
||||
const duration = performance.now() - pipeStartTime;
|
||||
devTools.trackExpressionEval({
|
||||
expression: `{{${trimmedPath}}}`,
|
||||
result: this.sanitizeResultForTracking(result),
|
||||
resultType: this.getResultType(result),
|
||||
componentId: trackingInfo?.componentId,
|
||||
componentName: trackingInfo?.componentName,
|
||||
propName: trackingInfo?.propName,
|
||||
fromCache,
|
||||
duration,
|
||||
method: 'resolveBindings',
|
||||
skipCache: opts.skipCache,
|
||||
});
|
||||
}
|
||||
|
||||
const formatted = this.formatValue(result);
|
||||
const result = this.evaluatePipeExpression(effectivePath, extendedContext, opts, {
|
||||
...trackingInfo,
|
||||
method: 'resolveBindings',
|
||||
displayExpression: `{{${trimmedPath}}}`,
|
||||
});
|
||||
const formatted = this.formatValue(result as ResolvedValue);
|
||||
return isRawBinding ? wrapRaw(formatted) : formatted;
|
||||
} catch (error) {
|
||||
logger.error('Pipe expression evaluation failed:', effectivePath, error);
|
||||
@@ -386,7 +365,10 @@ export class DataBindingEngine {
|
||||
// 대괄호 안에 따옴표가 있는 문자열 키 접근도 표현식으로 처리 (예: query['filters[0][field]'])
|
||||
// 함수 호출 패턴도 표현식으로 처리 (예: $localized(...))
|
||||
// 주의: 단일 | (파이프)는 위에서 먼저 처리되므로 여기서는 || 만 표현식으로 인식
|
||||
const isExpression = /[?:&!+\-*/<>=()]/.test(effectivePath) || /\|\|/.test(effectivePath) || /\[['"]/.test(effectivePath);
|
||||
// 판정은 BindingShape 정본을 쓴다 — 종전에는 이 자리의 문자 집합이 다른 렌더
|
||||
// 경로들과 달라, 같은 식이 여기서는 경로 탐색으로 가고 저기서는 표현식으로 가는
|
||||
// 비대칭이 있었다. 파이프는 위에서 이미 갈라졌다. @since engine-v1.55.0
|
||||
const isExpression = isComplexExpression(effectivePath);
|
||||
|
||||
if (isExpression) {
|
||||
// JavaScript 표현식으로 평가
|
||||
@@ -512,7 +494,133 @@ export class DataBindingEngine {
|
||||
|
||||
const formatted = this.formatValue(value);
|
||||
return isRawBinding ? wrapRaw(formatted) : formatted;
|
||||
});
|
||||
};
|
||||
|
||||
// 바인딩 위치는 수동 스캐너로 찾는다 — 정규식(`\{\{([^}]+)\}\}`)은 식 안에 `}` 가
|
||||
// 들어가면 매칭에 실패하고, `String.replace` 는 그때 입력을 그대로 돌려주어
|
||||
// 원본 `{{...}}` 문자열이 조용히 화면에 노출됐다. @since engine-v1.55.1
|
||||
const bindings = scanBindings(template);
|
||||
if (bindings.length === 0) return template;
|
||||
|
||||
let output = '';
|
||||
let cursor = 0;
|
||||
for (const binding of bindings) {
|
||||
output += template.slice(cursor, binding.start);
|
||||
output += replaceBinding(template.slice(binding.start, binding.end), binding.expr);
|
||||
cursor = binding.end;
|
||||
}
|
||||
return output + template.slice(cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 파이프(`|`)가 포함된 단일 바인딩 식을 평가해 **원본 타입 그대로** 반환
|
||||
*
|
||||
* `resolveBindings` 는 보간(문자열 산출)이 목적이라 결과에 `formatValue` 를 적용한다.
|
||||
* 반면 prop 값·`if` 조건·반복 렌더처럼 값 자체가 필요한 자리에서는 배열이
|
||||
* `"[\"a\",\"b\"]"` 로, `false` 가 `"false"`(truthy) 로 바뀌면 안 된다.
|
||||
* 그런 지점은 `resolveBindings(\`{{...}}\`)` 로 우회하지 말고 이 메서드를 직접 호출한다.
|
||||
*
|
||||
* 문자열 조립을 거치지 않으므로 `{{(row.meta ?? {}) | json}}` 처럼 식 안에 중괄호가
|
||||
* 있는 경우에도 정상 평가된다 — `BINDING_PATTERN` 은 `}` 를 포함한 식을 매칭하지 못해
|
||||
* 위임 방식에서는 입력 문자열이 그대로 화면에 노출됐다.
|
||||
*
|
||||
* @param expr `{{ }}` 와 `raw:` 접두사를 제거한 파이프 식 (예: `row.tags | slice(0,3)`)
|
||||
* @param context 데이터 컨텍스트
|
||||
* @param options 바인딩 옵션 (skipCache 등)
|
||||
* @param trackingInfo DevTools 표현식 추적 정보 (선택적)
|
||||
* @returns 파이프 체인 실행 결과 (원본 타입)
|
||||
* @throws 기본 식 평가 또는 파이프 실행 실패 시 그대로 전파 — 호출 지점이 자신의
|
||||
* 실패 정책(경고 후 undefined 등)을 적용한다
|
||||
*
|
||||
* @since engine-v1.54.9
|
||||
*/
|
||||
public evaluatePipeExpression(
|
||||
expr: string,
|
||||
context: BindingContext,
|
||||
options?: BindingOptions,
|
||||
trackingInfo?: ExpressionTrackingInfo,
|
||||
): unknown {
|
||||
const opts = { ...this.defaultOptions, ...options };
|
||||
const devTools = getDevTools();
|
||||
const pipeStartTime = devTools?.isEnabled() ? performance.now() : 0;
|
||||
|
||||
// $computed alias 추가 (resolveBindings 와 동일 규칙)
|
||||
const extendedContext: BindingContext = context._computed && !context.$computed
|
||||
? { ...context, $computed: context._computed }
|
||||
: context;
|
||||
|
||||
const [baseExpr, pipes] = splitPipes(expr);
|
||||
|
||||
// 기본 표현식 평가 (파이프 적용 전)
|
||||
let value: any;
|
||||
let fromCache = false;
|
||||
const isBaseExpression = isComplexExpression(baseExpr);
|
||||
// 동적 경로 여부 판단
|
||||
const isDynamicPipeBase = baseExpr.startsWith('_global') ||
|
||||
baseExpr.startsWith('_local') ||
|
||||
baseExpr.startsWith('_isolated') ||
|
||||
baseExpr.startsWith('$parent');
|
||||
|
||||
if (opts.skipCache) {
|
||||
// skipCache: 모든 캐시 무시
|
||||
if (isBaseExpression) {
|
||||
value = this.evaluateExpression(baseExpr, extendedContext);
|
||||
} else {
|
||||
value = this.resolvePath(baseExpr, extendedContext, opts);
|
||||
}
|
||||
} else if (isBaseExpression) {
|
||||
// 복잡한 표현식: 렌더 사이클 캐시 사용
|
||||
const exprCached = this.getFromRenderCycleCache(`expr:${baseExpr}`);
|
||||
if (exprCached !== undefined) {
|
||||
value = exprCached;
|
||||
fromCache = true;
|
||||
} else {
|
||||
value = this.evaluateExpression(baseExpr, extendedContext);
|
||||
this.saveToRenderCycleCache(`expr:${baseExpr}`, value);
|
||||
}
|
||||
} else if (isDynamicPipeBase) {
|
||||
// 동적 경로: 렌더 사이클 캐시 사용
|
||||
const renderCycleCached = this.getFromRenderCycleCache(baseExpr);
|
||||
if (renderCycleCached !== undefined) {
|
||||
value = renderCycleCached;
|
||||
fromCache = true;
|
||||
} else {
|
||||
value = this.resolvePath(baseExpr, extendedContext, opts);
|
||||
this.saveToRenderCycleCache(baseExpr, value);
|
||||
}
|
||||
} else {
|
||||
// 정적 경로: 영구 캐시 사용
|
||||
const cached = this.getFromCache(baseExpr);
|
||||
if (cached !== undefined) {
|
||||
value = cached;
|
||||
fromCache = true;
|
||||
} else {
|
||||
value = this.resolvePath(baseExpr, extendedContext, opts);
|
||||
this.saveToCache(baseExpr, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 파이프 체인 실행
|
||||
const result = executePipeChain(value, pipes);
|
||||
|
||||
// DevTools: 파이프 표현식 평가 추적
|
||||
if (devTools?.isEnabled()) {
|
||||
const duration = performance.now() - pipeStartTime;
|
||||
devTools.trackExpressionEval({
|
||||
expression: trackingInfo?.displayExpression ?? `{{${expr}}}`,
|
||||
result: this.sanitizeResultForTracking(result),
|
||||
resultType: this.getResultType(result),
|
||||
componentId: trackingInfo?.componentId,
|
||||
componentName: trackingInfo?.componentName,
|
||||
propName: trackingInfo?.propName,
|
||||
fromCache,
|
||||
duration,
|
||||
method: trackingInfo?.method ?? 'evaluatePipeExpression',
|
||||
skipCache: opts.skipCache,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -668,6 +776,14 @@ export class DataBindingEngine {
|
||||
depth: number = 0,
|
||||
visitedObjects: WeakSet<object> = new WeakSet(),
|
||||
): ResolvedValue {
|
||||
// 빈 경로 안전망 — 세그먼트가 하나도 없으면 아래 루프를 돌지 않아
|
||||
// 컨텍스트 객체 **전체**가 값으로 반환된다. 그 값이 문자열로 서식되면
|
||||
// 전역 상태가 통째로 화면·요청에 실릴 수 있다. @since engine-v1.55.0
|
||||
if (typeof path !== 'string' || path.trim() === '') {
|
||||
logger.warn('resolvePath: 빈 경로가 전달되었습니다 — undefined 로 해석합니다.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 최대 깊이 체크 (순환 참조 방지)
|
||||
if (options.maxDepth && depth > options.maxDepth) {
|
||||
if (options.detectCircular) {
|
||||
@@ -844,7 +960,7 @@ export class DataBindingEngine {
|
||||
* @param obj 검사할 객체
|
||||
* @returns 액션 정의이면 true
|
||||
*/
|
||||
private isActionDefinition(obj: Record<string, any>): boolean {
|
||||
public isActionDefinition(obj: Record<string, any>): boolean {
|
||||
if (typeof obj.handler !== 'string') return false;
|
||||
return (
|
||||
obj.params !== undefined ||
|
||||
@@ -855,43 +971,6 @@ export class DataBindingEngine {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 문자열 전체가 단일 `{{...}}` 바인딩인지 판정하고, 그렇다면 내부 식을 반환합니다.
|
||||
*
|
||||
* 종전 `^\{\{([^}]+)\}\}$` 정규식은 식 안에 `}` 가 든 경우(예: 빈 객체 fallback
|
||||
* `{{error.errors ?? {}}}`)를 단일 바인딩으로 인식하지 못해 원본 문자열이 그대로
|
||||
* 새어 나갔다. 본 메서드는 외곽 `{{ }}` 안의 중괄호 균형(depth)을 추적해, 내부에서
|
||||
* 바인딩이 조기 종료(`}}`)되지 않고 끝까지 단일 식으로 닫히는 경우에만 그 식을 반환한다.
|
||||
* `"{{a}} {{b}}"` 처럼 두 개 이상의 바인딩이면 null 을 반환해 문자열 보간 경로로 보낸다.
|
||||
*
|
||||
* @param value 검사할 문자열
|
||||
* @returns 단일 바인딩이면 내부 식 문자열, 아니면 null
|
||||
*/
|
||||
private extractSingleBinding(value: string): string | null {
|
||||
if (!value.startsWith('{{') || !value.endsWith('}}') || value.length < 5) {
|
||||
return null;
|
||||
}
|
||||
const inner = value.slice(2, -2);
|
||||
// inner 를 스캔하며 `}}` 가 깊이 0(외곽 바인딩 종료)에서 등장하면 단일 바인딩 아님.
|
||||
// `{` / `}` 로 깊이를 추적하되, 식 내부의 `{}`(객체 리터럴)는 균형이 맞으므로
|
||||
// 깊이가 음수로 떨어지지 않는 한 단일 바인딩으로 본다.
|
||||
let depth = 0;
|
||||
for (let i = 0; i < inner.length - 1; i++) {
|
||||
const c = inner[i];
|
||||
if (c === '{') {
|
||||
depth++;
|
||||
} else if (c === '}') {
|
||||
if (depth > 0) {
|
||||
depth--;
|
||||
} else if (inner[i + 1] === '}') {
|
||||
// 깊이 0 에서 `}}` 출현 → 외곽 바인딩이 중간에 닫힘 → 다중 바인딩/보간
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
|
||||
/**
|
||||
* 객체 내의 모든 바인딩 표현식을 데이터로 치환
|
||||
*
|
||||
@@ -957,12 +1036,48 @@ export class DataBindingEngine {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 해석 실패는 key 단위로 격리한다 — 이 메서드에는 상위 catch 가 없어서
|
||||
// 예외가 밖으로 나가면 그 컴포넌트의 props 해석 **전체**가 중단되고,
|
||||
// 표현식 하나의 실수가 화면 전체를 날린다. @since engine-v1.55.1
|
||||
try {
|
||||
this.resolveObjectEntry(result, key, value, context, options);
|
||||
} catch (error) {
|
||||
logger.warn(`resolveObject: 값 해석 실패 (key: ${key}):`, error);
|
||||
result[key] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* `resolveObject` 의 key 하나를 해석해 결과 객체에 기록
|
||||
*
|
||||
* 실패 격리 단위를 key 로 두기 위해 분리했다. 이 메서드는 예외를 잡지 않는다 —
|
||||
* 호출자(`resolveObject`)가 key 단위로 격리한다.
|
||||
*
|
||||
* @param result 결과 객체 (직접 기록)
|
||||
* @param key 대상 key
|
||||
* @param value 원본 값
|
||||
* @param context 데이터 컨텍스트
|
||||
* @param options 바인딩 옵션
|
||||
*
|
||||
* @since engine-v1.55.1
|
||||
*/
|
||||
private resolveObjectEntry(
|
||||
result: Record<string, any>,
|
||||
key: string,
|
||||
value: any,
|
||||
context: BindingContext,
|
||||
options?: BindingOptions & { skipBindingKeys?: string[] },
|
||||
): void {
|
||||
{
|
||||
if (typeof value === 'string') {
|
||||
// 전체가 {{variable}} 패턴인 경우 원본 값(배열/객체) 반환.
|
||||
// `[^}]+` 는 `{{error.errors ?? {}}}` 처럼 식 안에 `}` 가 든 경우(빈 객체 fallback 등)
|
||||
// 첫 `}` 에서 끊겨 매칭에 실패 → 원본 문자열이 그대로 새어 나오던 결함이 있었다.
|
||||
// extractSingleBinding 은 외곽 `{{ }}` 안의 중괄호 균형을 추적해 단일 바인딩 식을 정확히 추출한다.
|
||||
const singleBindingPath = this.extractSingleBinding(value);
|
||||
const singleBindingPath = extractSingleBinding(value);
|
||||
if (singleBindingPath !== null) {
|
||||
const path = singleBindingPath.trim();
|
||||
|
||||
@@ -974,18 +1089,30 @@ export class DataBindingEngine {
|
||||
effectivePath = path.slice(RAW_PREFIX.length);
|
||||
}
|
||||
|
||||
// 복잡 표현식(연산자 포함)은 evaluateExpression으로 라우팅
|
||||
// resolveBindings()와 동일한 isBaseExpression 판별 기준 사용
|
||||
// 단, 파이프 표현식은 resolveBindings 로 위임한다 — `|` 를 연산자로 보아
|
||||
// evaluateExpression 으로 보내면 `value | pipe` 가 JS 비트 OR 로 평가되어
|
||||
// 라우팅 판정은 BindingShape 정본을 쓴다 (파이프 → 표현식 → 경로).
|
||||
// 파이프를 evaluateExpression 으로 보내면 `value | pipe` 가 JS 비트 OR 로 평가되어
|
||||
// 인자 있는 파이프는 예외를 던지고(props 해석 전체가 중단), 인자 없는
|
||||
// 파이프는 포맷이 적용되지 않은 오답이 된다. @since engine-v1.54.3
|
||||
const isBaseExpression = /[?:|&!+\-*/<>=()]/.test(effectivePath) || /\[['"]/.test(effectivePath);
|
||||
const resolved = hasPipes(effectivePath)
|
||||
? this.resolveBindings(`{{${effectivePath}}}`, context, options)
|
||||
: isBaseExpression
|
||||
? this.evaluateExpression(effectivePath, context, options)
|
||||
: this.resolve(effectivePath, context, options);
|
||||
// 값이 필요한 자리이므로 서식(formatValue)을 적용하지 않는다. @since engine-v1.54.9
|
||||
// 실행(캐시·예외 격리)은 이 지점의 정책을 유지한다. @since engine-v1.55.0
|
||||
let resolved: any;
|
||||
switch (classifyExpression(effectivePath)) {
|
||||
case 'empty':
|
||||
logger.warn(`resolveObject: 빈 바인딩 \`{{}}\` (key: ${key}) — undefined 로 해석합니다.`);
|
||||
resolved = undefined;
|
||||
break;
|
||||
case 'literal':
|
||||
resolved = LITERALS.get(effectivePath);
|
||||
break;
|
||||
case 'pipe':
|
||||
resolved = this.evaluatePipeExpression(effectivePath, context, options);
|
||||
break;
|
||||
case 'expression':
|
||||
resolved = this.evaluateExpression(effectivePath, context, options);
|
||||
break;
|
||||
default:
|
||||
resolved = this.resolve(effectivePath, context, options);
|
||||
}
|
||||
result[key] = isRawBinding && resolved != null ? wrapRawDeep(resolved) : resolved;
|
||||
} else {
|
||||
// 문자열 보간
|
||||
@@ -1005,8 +1132,6 @@ export class DataBindingEngine {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1258,9 +1383,26 @@ export class DataBindingEngine {
|
||||
return current ?? fallback;
|
||||
};
|
||||
|
||||
// 확장된 컨텍스트의 모든 키를 변수로 사용할 수 있도록 준비
|
||||
const contextKeys = Object.keys(extendedContext);
|
||||
const contextValues = Object.values(extendedContext);
|
||||
// 확장된 컨텍스트의 키를 변수로 사용할 수 있도록 준비.
|
||||
//
|
||||
// 함수 파라미터가 될 수 없는 키(`sales_status[]`, `data-id`, 예약어 등)는 제외한다.
|
||||
// 하나라도 섞이면 `new Function` 생성 자체가 SyntaxError 로 실패해 **그 컨텍스트에서
|
||||
// 평가되는 모든 표현식**이 통째로 죽는다 — 식이 그 키를 쓰지 않아도 마찬가지다.
|
||||
// 예외도 화면 오류도 없이 값만 사라지므로(catch → 폴백) 원인 파악이 어렵다.
|
||||
//
|
||||
// 제외해도 잃는 것은 없다 — 그런 키는 애초에 식 안에서 맨이름으로 참조할 수 없었고
|
||||
// (`sales_status[]` 는 식별자가 아니다), 실제 작성은 `query['sales_status[]']` 처럼
|
||||
// 상위 객체를 거치므로 그대로 동작한다.
|
||||
// @since engine-v1.56.2
|
||||
const contextKeys: string[] = [];
|
||||
const contextValues: unknown[] = [];
|
||||
for (const [key, value] of Object.entries(extendedContext)) {
|
||||
if (!VALID_IDENTIFIER_PATTERN.test(key) || RESERVED_WORDS.has(key)) {
|
||||
continue;
|
||||
}
|
||||
contextKeys.push(key);
|
||||
contextValues.push(value);
|
||||
}
|
||||
|
||||
// 캐시 키 생성: 전처리된 표현식 + 컨텍스트 키 조합
|
||||
// 같은 표현식이라도 컨텍스트 키가 다르면 다른 함수가 필요
|
||||
|
||||
@@ -16,6 +16,8 @@ import { getApiClient } from '../api/ApiClient';
|
||||
import { AuthManager } from '../auth/AuthManager';
|
||||
import { resolveExpressionString, evaluateRenderCondition } from './helpers/RenderHelpers';
|
||||
import { DataBindingEngine } from './DataBindingEngine';
|
||||
import { extractSingleBinding } from './BindingShape';
|
||||
import { hasPipes } from './PipeRegistry';
|
||||
import { getErrorHandlingResolver } from '../error';
|
||||
import { getActionDispatcher } from './ActionDispatcher';
|
||||
import type { ErrorHandlingMap, OnErrorHandler, OnSuccessHandler, ErrorContext, SuccessContext, ErrorCondition } from '../types/ErrorHandling';
|
||||
@@ -44,13 +46,23 @@ function resolveParamValue(value: string, context: Record<string, any>): any {
|
||||
// 전체가 하나의 {{...}} 표현식인지 확인
|
||||
// 예: "{{query['sales_status[]']}}" → true
|
||||
// 예: "page={{query.page}}" → false
|
||||
const singleExpressionMatch = value.match(/^\{\{(.+)\}\}$/);
|
||||
//
|
||||
// 판정은 BindingShape 정본을 쓴다. 종전 greedy 정규식 `^\{\{(.+)\}\}$` 은
|
||||
// `"{{a}}-{{b}}"` 같은 보간 문자열도 단일 표현식으로 오판했고, 그때 잘라낸
|
||||
// `a}}-{{b` 를 평가하다 예외가 나면 **원본 문자열이 그대로 서버로 전송**됐다.
|
||||
// @since engine-v1.55.0
|
||||
const expression = extractSingleBinding(value);
|
||||
|
||||
if (singleExpressionMatch) {
|
||||
if (expression !== null) {
|
||||
// 단일 표현식인 경우 evaluateExpression으로 원본 타입 유지
|
||||
const expression = singleExpressionMatch[1].trim();
|
||||
try {
|
||||
return bindingEngine.evaluateExpression(expression, context);
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — evaluateExpression 은
|
||||
// `|` 를 JS 비트 OR 로 보므로 인자 있는 파이프는 예외로 아래 catch 에 걸려
|
||||
// 원본 `{{...}}` 리터럴이 그대로 쿼리스트링에 실려 서버로 전송된다.
|
||||
// @since engine-v1.54.10
|
||||
return hasPipes(expression)
|
||||
? bindingEngine.evaluatePipeExpression(expression, context, { skipCache: true })
|
||||
: bindingEngine.evaluateExpression(expression, context);
|
||||
} catch (error) {
|
||||
logger.error('Failed to evaluate param expression:', expression, error);
|
||||
return value; // 실패 시 원본 반환
|
||||
|
||||
@@ -26,6 +26,10 @@ import { TranslationEngine, TranslationContext } from './TranslationEngine';
|
||||
import { ActionDispatcher, ActionDefinition } from './ActionDispatcher';
|
||||
import { resolveIterationSource, evaluateIfCondition, evaluateRenderCondition, bindComponentActions, resolveClassMap } from './helpers/RenderHelpers';
|
||||
import { hasPipes } from './PipeRegistry';
|
||||
import {
|
||||
extractSingleBinding as canonicalExtractSingleBinding,
|
||||
isComplexExpression as canonicalIsComplexExpression,
|
||||
} from './BindingShape';
|
||||
import { RAW_PREFIX, RAW_MARKER_START, RAW_MARKER_END, RAW_PLACEHOLDER_MARKER, isRawWrapped, unwrapRaw, containsRawMarker, wrapRawDeep } from './rawMarkers';
|
||||
import type { ConditionsProperty } from './helpers/ConditionEvaluator';
|
||||
import { useTransitionState } from './TransitionContext';
|
||||
@@ -1723,7 +1727,13 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
if (expression.startsWith('{{') && expression.endsWith('}}')) {
|
||||
// {{...}} 형태의 표현식 평가
|
||||
const innerExpression = (expression as string).slice(2, -2).trim();
|
||||
const result = bindingEngine.evaluateExpression(innerExpression, computedContext, { skipCache: true });
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — evaluateExpression 은
|
||||
// `|` 를 JS 비트 OR 로 보므로 인자 있는 파이프는 예외로 아래 catch 에 걸려
|
||||
// computed 가 이전 값에 고정된다(값이 바뀌지 않는 조용한 실패).
|
||||
// @since engine-v1.54.10
|
||||
const result = hasPipes(innerExpression)
|
||||
? bindingEngine.evaluatePipeExpression(innerExpression, computedContext, { skipCache: true })
|
||||
: bindingEngine.evaluateExpression(innerExpression, computedContext, { skipCache: true });
|
||||
newComputed[key] = result;
|
||||
} else {
|
||||
// 순수 문자열은 그대로 사용
|
||||
@@ -2020,7 +2030,13 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
componentName: effectiveComponentDef.name,
|
||||
propName: 'slot',
|
||||
};
|
||||
const result = bindingEngine.evaluateExpression(expr, extendedDataContext, slotTrackingInfo);
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — evaluateExpression 은
|
||||
// `|` 를 JS 비트 OR 로 보므로 결과가 문자열이 아니게 되고, 아래 `typeof result === 'string'`
|
||||
// 가드에 걸려 슬롯 미등록으로 조용히 흡수된다(컴포넌트가 화면에서 사라짐).
|
||||
// @since engine-v1.54.10
|
||||
const result = hasPipes(expr)
|
||||
? bindingEngine.evaluatePipeExpression(expr, extendedDataContext, undefined, slotTrackingInfo)
|
||||
: bindingEngine.evaluateExpression(expr, extendedDataContext, slotTrackingInfo);
|
||||
logger.log(`[Slot] 표현식 평가: "${expr}" => "${result}" (컴포넌트: ${effectiveComponentDef.id})`);
|
||||
return typeof result === 'string' ? result : null;
|
||||
} catch (error) {
|
||||
@@ -2236,66 +2252,30 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
/**
|
||||
* 복잡한 표현식 여부 감지
|
||||
*
|
||||
* 삼항 연산자, 논리 연산자 등이 포함된 경우 true 반환
|
||||
* 판정은 BindingShape 정본에 위임한다 — 종전에는 이 자리의 문자 집합이 좁아
|
||||
* `query['sales_status[]']` 같은 따옴표 키 인덱싱과 배열/객체 리터럴이
|
||||
* "단순 경로" 로 오판되어 경로 탐색으로 갔고, 결과는 조용한 undefined 였다.
|
||||
* 같은 식이 `if`(RenderHelpers 방언)에서는 정상 동작해 재현 위치를 특정하기 어려웠다.
|
||||
*
|
||||
* @since engine-v1.55.0
|
||||
*/
|
||||
const isComplexExpression = useCallback((expr: string): boolean => {
|
||||
// 삼항 연산자나 복잡한 표현식 감지 (?, :, ||, &&, !, 산술 연산자, 함수 호출 등)
|
||||
// () 추가: $localized(menu.name) 같은 함수 호출 표현식도 복잡한 표현식으로 처리
|
||||
return /[?:|&!+\-*/<>=()]/.test(expr);
|
||||
}, []);
|
||||
const isComplexExpression = useCallback(
|
||||
(expr: string): boolean => canonicalIsComplexExpression(expr),
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* 단일 바인딩 표현식 매칭 정규식
|
||||
* 단일 바인딩 표현식 추출
|
||||
*
|
||||
* 전체가 {{...}}로 감싸진 표현식을 매칭합니다.
|
||||
* 내부에 문자열 리터럴('...' 또는 "...")이 포함된 복잡한 표현식도 지원합니다.
|
||||
* 전체가 `{{...}}` 로 감싸진 식이면 내부 식을, 아니면 null 을 반환한다.
|
||||
* 판정은 BindingShape 정본에 위임한다 (따옴표·이스케이프·중괄호 균형 추적).
|
||||
*
|
||||
* 예시:
|
||||
* - {{user.name}} -> 매칭
|
||||
* - {{row.status === 'active' ? 'yes' : 'no'}} -> 매칭
|
||||
* - Hello {{name}} -> 매칭 안됨 (문자열 보간)
|
||||
* @since engine-v1.55.0
|
||||
*/
|
||||
const extractSingleBinding = useCallback((value: string): string | null => {
|
||||
// 시작이 {{이고 끝이 }}인지 확인
|
||||
if (!value.startsWith('{{') || !value.endsWith('}}')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 중간 내용 추출
|
||||
const inner = value.slice(2, -2);
|
||||
|
||||
// 중간에 }}가 있으면 단일 바인딩이 아님 (예: {{a}} {{b}})
|
||||
// 단, 문자열 리터럴 안의 }는 제외해야 함
|
||||
let inString: string | null = null;
|
||||
let braceCount = 0;
|
||||
|
||||
for (let i = 0; i < inner.length; i++) {
|
||||
const char = inner[i];
|
||||
const prevChar = i > 0 ? inner[i - 1] : '';
|
||||
|
||||
// 이스케이프된 문자는 무시
|
||||
if (prevChar === '\\') continue;
|
||||
|
||||
// 문자열 시작/종료 감지
|
||||
if ((char === '"' || char === "'") && !inString) {
|
||||
inString = char;
|
||||
} else if (char === inString) {
|
||||
inString = null;
|
||||
}
|
||||
|
||||
// 문자열 밖에서 중괄호 카운트
|
||||
if (!inString) {
|
||||
if (char === '{') braceCount++;
|
||||
if (char === '}') braceCount--;
|
||||
|
||||
// 닫는 중괄호가 더 많으면 단일 바인딩이 아님
|
||||
if (braceCount < 0) return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 중괄호가 균형이 맞으면 단일 바인딩
|
||||
return braceCount === 0 ? inner.trim() : null;
|
||||
}, []);
|
||||
const extractSingleBinding = useCallback(
|
||||
(value: string): string | null => canonicalExtractSingleBinding(value),
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* Props 처리
|
||||
@@ -2322,6 +2302,13 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
// 같은 표현식이 다른 아이템 컨텍스트로 평가되어야 하므로 캐시 사용 시 잘못된 값 반환
|
||||
const bindingOptions = isInsideIteration ? { skipCache: true } : undefined;
|
||||
|
||||
// 중첩 객체 해석에도 컴포넌트별 skipBindingKeys 를 전달한다.
|
||||
// 종전에는 전달하지 않아 resolveObject 의 기본 4개만 적용됐고, 그래서 컴포넌트가
|
||||
// "내가 직접 반복 렌더한다" 고 선언한 키가 중첩 위치에서는 선평가됐다 —
|
||||
// 그 시점에는 항목 컨텍스트가 없으므로 표현식이 stale 값으로 고정된다.
|
||||
// 저장소 전수 스캔 기준 현재 해당 위치 0건(예방 목적). @since engine-v1.55.1
|
||||
const nestedBindingOptions = { ...(bindingOptions ?? {}), skipBindingKeys };
|
||||
|
||||
// _computed 참조 표현식의 캐시 스킵 여부를 판별하는 헬퍼
|
||||
// _computed는 컴포넌트마다 _local 기반으로 재계산되므로 컴포넌트 간 공유 캐시 사용 시
|
||||
// 먼저 렌더링된 컴포넌트의 stale 값이 이후 컴포넌트에 전파되는 버그 발생
|
||||
@@ -2361,11 +2348,27 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
|
||||
let resolvedValue;
|
||||
if (hasPipes(effectivePath)) {
|
||||
// 파이프 표현식은 resolveBindings 로 위임 (파이프 체인 실행 포함).
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다.
|
||||
// isComplexExpression 은 `|` 를 연산자로 보므로 그대로 두면
|
||||
// `value | pipe` 가 JS 비트 OR 로 평가된다 — text 처리와 동일 분기.
|
||||
// @since engine-v1.54.3
|
||||
resolvedValue = bindingEngine.resolveBindings(`{{${effectivePath}}}`, extendedDataContext, bindingOptions, trackingInfo);
|
||||
// prop 값은 원본 타입이 필요하므로 문자열 서식을 거치지 않으며,
|
||||
// 캐시 옵션도 다른 분기와 동일하게 _computed 인지를 반영한다.
|
||||
// @since engine-v1.54.9
|
||||
try {
|
||||
resolvedValue = bindingEngine.evaluatePipeExpression(
|
||||
effectivePath,
|
||||
extendedDataContext,
|
||||
getComputedAwareOptions(effectivePath),
|
||||
trackingInfo,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`파이프 표현식 평가 실패 (컴포넌트: ${effectiveComponentDef.id}, prop: ${key}):`,
|
||||
error
|
||||
);
|
||||
resolvedValue = undefined;
|
||||
}
|
||||
} else if (isComplexExpression(effectivePath)) {
|
||||
try {
|
||||
resolvedValue = bindingEngine.evaluateExpression(effectivePath, extendedDataContext, trackingInfo);
|
||||
@@ -2394,11 +2397,11 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
typeof item === 'string'
|
||||
? bindingEngine.resolveBindings(item, extendedDataContext, bindingOptions)
|
||||
: typeof item === 'object' && item !== null
|
||||
? bindingEngine.resolveObject(item, extendedDataContext, bindingOptions)
|
||||
? bindingEngine.resolveObject(item, extendedDataContext, nestedBindingOptions)
|
||||
: item
|
||||
);
|
||||
} else if (value && typeof value === 'object') {
|
||||
props[key] = bindingEngine.resolveObject(value, extendedDataContext, bindingOptions);
|
||||
props[key] = bindingEngine.resolveObject(value, extendedDataContext, nestedBindingOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2690,9 +2693,20 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
effectivePath = singleBindingPath.slice(RAW_PREFIX.length);
|
||||
}
|
||||
|
||||
// 파이프가 있는 경우 resolveBindings 사용 (파이프 처리 포함)
|
||||
// 파이프가 있는 경우 evaluatePipeExpression 사용 (파이프 체인 실행 포함).
|
||||
// 다른 두 분기(resolve/evaluateExpression)와 마찬가지로 원본 타입을 유지한다 —
|
||||
// 문자열 서식을 거치면 `first`/`filter` 같은 파이프의 결과 타입이 바뀐다.
|
||||
// @since engine-v1.54.9
|
||||
if (hasPipes(effectivePath)) {
|
||||
resolvedText = bindingEngine.resolveBindings(`{{${effectivePath}}}`, extendedDataContext, { skipCache: true }, textTrackingInfo);
|
||||
try {
|
||||
resolvedText = bindingEngine.evaluatePipeExpression(effectivePath, extendedDataContext, { skipCache: true }, textTrackingInfo);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`text 파이프 표현식 평가 실패 (컴포넌트: ${effectiveComponentDef.id}):`,
|
||||
error
|
||||
);
|
||||
resolvedText = '';
|
||||
}
|
||||
} else if (isComplexExpression(effectivePath)) {
|
||||
// 복잡한 표현식인 경우 evaluateExpression 사용
|
||||
try {
|
||||
@@ -2846,9 +2860,14 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
// 각 아이템에 대해 자식 컴포넌트 렌더링
|
||||
return dataArray.map((item, index) => {
|
||||
// 반복 컨텍스트 생성 (extendedDataContext 기반, _local 포함)
|
||||
//
|
||||
// `{item_var}_index` 자동 변수는 반복 렌더 경로(renderItemChildren)가 이미
|
||||
// 제공하던 것으로, 이 경로에만 없어서 같은 작성이 한쪽에서만 동작했다.
|
||||
// @since engine-v1.56.0
|
||||
const iterationContext: Record<string, any> = {
|
||||
...extendedDataContext,
|
||||
[effectiveComponentDef.iteration!.item_var]: item,
|
||||
[`${effectiveComponentDef.iteration!.item_var}_index`]: index,
|
||||
};
|
||||
|
||||
if (effectiveComponentDef.iteration!.index_var) {
|
||||
@@ -3741,8 +3760,19 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = memo(
|
||||
}
|
||||
|
||||
// 조건부 렌더링 체크
|
||||
//
|
||||
// iteration 이 있으면 여기서 끊지 않는다. `if` 가 항목 변수(`{{user.is_active}}` 등)를
|
||||
// 참조하는 경우 이 시점의 컨텍스트에는 그 변수가 없어 조건이 항상 false 가 되고,
|
||||
// **목록 전체가 렌더되지 않는다**(에러도 경고도 없음). 반복 렌더 경로
|
||||
// (renderItemChildren)는 항목별 컨텍스트에서 평가하므로 같은 작성이 정상 동작했다 —
|
||||
// 그 비대칭을 없앤다. 항목별 재평가는 자식 DynamicRenderer 가 수행한다
|
||||
// (iteration 만 제거한 정의를 넘기므로 `if` 는 그대로 남는다).
|
||||
// @since engine-v1.56.0
|
||||
if (!shouldRender) {
|
||||
return null;
|
||||
if (!effectiveComponentDef.iteration) {
|
||||
return null;
|
||||
}
|
||||
return renderIteration;
|
||||
}
|
||||
|
||||
// 정렬 가능 리스트 렌더링 체크 (sortable은 iteration보다 우선)
|
||||
|
||||
@@ -19,6 +19,9 @@ import { ComponentRegistry } from './ComponentRegistry';
|
||||
import { TranslationEngine, TranslationContext } from './TranslationEngine';
|
||||
import { ActionDispatcher } from './ActionDispatcher';
|
||||
import { DataBindingEngine, dataBindingEngine } from './DataBindingEngine';
|
||||
import { hasPipes } from './PipeRegistry';
|
||||
import { extractSingleBinding } from './BindingShape';
|
||||
import { evaluateStringCondition } from './helpers/ConditionEvaluator';
|
||||
import { DataSourceManager, dataSourceManager } from './DataSourceManager';
|
||||
import DynamicRenderer from './DynamicRenderer';
|
||||
import { useTransitionState } from './TransitionContext';
|
||||
@@ -978,6 +981,39 @@ function initHelperAPIs(G7Core: any, deps: G7CoreDependencies): void {
|
||||
return renderItemChildren(children, mergedContext, componentMap, keyPrefix, mergedOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* 조건 문자열 평가 노출 (템플릿 컴포넌트용)
|
||||
*
|
||||
* 템플릿 컴포넌트가 `new Function('row', ...)` 로 자체 평가기를 두면 엔진과 판정이
|
||||
* 갈린다 — 실제로 `_local`/`_global` 을 참조하는 조건에서 두 컴포넌트가 예외를 서로
|
||||
* 다르게 처리해 한쪽은 자식이 사라지고 다른 쪽은 항상 표시되는 **반대 방향** 결과가 났다.
|
||||
* 이 API 는 엔진의 `evaluateStringCondition` 을 그대로 쓰고, 전역/로컬/계산 상태를
|
||||
* 컨텍스트에 병합해 넘긴다.
|
||||
*
|
||||
* @param condition 조건 문자열 (예: `"{{row.charge_policy !== 'free'}}"`)
|
||||
* @param extraContext 추가 컨텍스트 (예: `{ row }`)
|
||||
* @returns 평가 결과. 조건이 비어 있으면 true
|
||||
*
|
||||
* @since engine-v1.56.1
|
||||
*/
|
||||
G7Core.evaluateCondition = (condition: string, extraContext?: Record<string, any>): boolean => {
|
||||
if (!condition) return true;
|
||||
|
||||
const state = deps.getState();
|
||||
const templateApp = (window as any).__templateApp;
|
||||
const globalStateContent = templateApp?.getGlobalState?.() || {};
|
||||
|
||||
const context = {
|
||||
_global: globalStateContent,
|
||||
_local: globalStateContent._local || {},
|
||||
_computed: globalStateContent._computed || {},
|
||||
...(extraContext || {}),
|
||||
};
|
||||
|
||||
const engine = state.bindingEngine ?? dataBindingEngine;
|
||||
return evaluateStringCondition(condition, context, engine);
|
||||
};
|
||||
|
||||
// getComponentMap 노출 (CardGrid 등에서 전체 컴포넌트 맵 접근용)
|
||||
G7Core.getComponentMap = () => {
|
||||
return ComponentRegistry.getInstance().getComponentMap();
|
||||
@@ -1145,14 +1181,20 @@ function initHelperAPIs(G7Core: any, deps: G7CoreDependencies): void {
|
||||
// resolveObject는 DEFAULT_SKIP_BINDING_KEYS 때문에 expandContext 내부 키를 스킵할 수 있음
|
||||
for (const [key, value] of Object.entries(expandContext)) {
|
||||
if (typeof value === 'string') {
|
||||
// 단일 Mustache 표현식인지 확인 ({{expr}} 형태)
|
||||
const singleBindingMatch = value.match(/^\{\{([^}]+)\}\}$/);
|
||||
if (singleBindingMatch) {
|
||||
// 단일 Mustache 표현식인지 확인 ({{expr}} 형태).
|
||||
// 판정은 BindingShape 정본 — 종전 `^\{\{([^}]+)\}\}$` 은 식 안에 `}` 가
|
||||
// 들어간 경우(`?? {}` 등)를 단일 바인딩으로 보지 못해 보간 경로로 흘렸다.
|
||||
// @since engine-v1.55.0
|
||||
const expr = extractSingleBinding(value);
|
||||
if (expr !== null) {
|
||||
// 단일 바인딩 표현식: evaluateExpression 사용하여 원본 타입 유지
|
||||
// resolveBindings는 문자열 보간용이라 배열/객체를 JSON 문자열로 변환함
|
||||
const expr = singleBindingMatch[1].trim();
|
||||
try {
|
||||
resolvedExpandContext[key] = bindingEngine.evaluateExpression(expr, evalContext, { skipCache: true });
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — evaluateExpression 은
|
||||
// `|` 를 JS 비트 OR 로 보므로 파이프가 적용되지 않는다. @since engine-v1.54.10
|
||||
resolvedExpandContext[key] = hasPipes(expr)
|
||||
? bindingEngine.evaluatePipeExpression(expr, evalContext, { skipCache: true })
|
||||
: bindingEngine.evaluateExpression(expr, evalContext, { skipCache: true });
|
||||
} catch (e) {
|
||||
// 평가 실패 시 undefined (폴백 처리는 표현식 자체에서 || 로 처리)
|
||||
resolvedExpandContext[key] = undefined;
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
import { createLogger } from '../utils/Logger';
|
||||
import { suffixed } from '../support/assetUrl';
|
||||
// 순환 import (DataBindingEngine → TranslationEngine → DataBindingEngine) 이지만
|
||||
// 양쪽 모두 모듈 평가 시점이 아니라 메서드 실행 시점에만 서로를 참조하므로
|
||||
// live binding 이 채워진 뒤에 사용된다.
|
||||
import { dataBindingEngine } from './DataBindingEngine';
|
||||
|
||||
const logger = createLogger('TranslationEngine');
|
||||
|
||||
@@ -497,10 +501,15 @@ export class TranslationEngine {
|
||||
const isComplexExpression = /[|&()[\]!?:+\-*/%<>=\s]/.test(expression);
|
||||
|
||||
if (isComplexExpression && dataContext) {
|
||||
// JavaScript 표현식으로 평가
|
||||
// 평가는 엔진에 위임한다. 종전에는 `new Function(...Object.keys(dataContext))` 로
|
||||
// 자체 평가해 `$localized(...)`/`$t(...)`/`$uuid()` 헬퍼와 optional chaining 전처리,
|
||||
// 표현식 함수 캐시를 쓰지 못했다. 실패 시 조용히 빈 문자열이 되어 번역 문구에서
|
||||
// 값만 사라졌다.
|
||||
// (컨텍스트 키가 식별자가 아닐 때의 실패는 엔진 쪽 문제였고 engine-v1.56.2 에서
|
||||
// DataBindingEngine 이 그런 키를 제외하도록 고쳤다.)
|
||||
// @since engine-v1.56.1
|
||||
try {
|
||||
const func = new Function(...Object.keys(dataContext), `return ${expression}`);
|
||||
const resolved = func(...Object.values(dataContext));
|
||||
const resolved = dataBindingEngine.evaluateExpression(expression, dataContext);
|
||||
return String(resolved ?? '');
|
||||
} catch (error) {
|
||||
logger.error('Expression evaluation failed:', expression, error);
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* ActionDispatcher 액션 params 파이프 회귀 테스트
|
||||
*
|
||||
* 액션 params 의 `{{...}}` 는 `evaluateExpression` 으로 평가되는데, 이 경로는
|
||||
* `|` 를 JS 비트 OR 로 본다. 그래서 파이프가 붙으면
|
||||
* - 인자 있는 파이프: 예외 → catch 가 **원본 `{{...}}` 문자열을 그대로 반환** → 서버로 전송
|
||||
* - 인자 없는 파이프: 날짜 문자열 | 0 → `0` 같은 조용한 오답
|
||||
* 이 되었다. 화면에는 아무 표시도 나지 않고 서버 요청 본문만 오염된다.
|
||||
*
|
||||
* @since engine-v1.54.10
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import { ActionDispatcher, ActionDefinition } from '../ActionDispatcher';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
|
||||
vi.mock('../../auth/AuthManager', () => ({
|
||||
AuthManager: {
|
||||
getInstance: vi.fn(() => ({
|
||||
login: vi.fn(),
|
||||
logout: vi.fn(),
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../api/ApiClient', () => ({
|
||||
getApiClient: vi.fn(() => ({
|
||||
getToken: vi.fn(() => null),
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('ActionDispatcher — 액션 params 의 파이프', () => {
|
||||
let dispatcher: ActionDispatcher;
|
||||
let mockFetch: ReturnType<typeof vi.fn>;
|
||||
let originalFetch: typeof fetch;
|
||||
|
||||
const dataContext = {
|
||||
row: {
|
||||
created_at: '2024-01-15T14:30:00',
|
||||
price: 1234567,
|
||||
tags: ['a', 'b', 'c'],
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
dispatcher = new ActionDispatcher({ navigate: vi.fn() });
|
||||
Logger.getInstance().setDebug(false);
|
||||
|
||||
originalFetch = globalThis.fetch;
|
||||
mockFetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ success: true, data: {} }),
|
||||
});
|
||||
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
||||
|
||||
Object.defineProperty(document, 'cookie', {
|
||||
value: 'XSRF-TOKEN=test-csrf-token',
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
Logger.getInstance().setDebug(false);
|
||||
});
|
||||
|
||||
const createMockEvent = () =>
|
||||
({ preventDefault: vi.fn(), type: 'click', target: null } as unknown as Event);
|
||||
|
||||
it('인자 있는 파이프가 서식된 값으로 전송된다 (원본 {{...}} 전송 금지)', async () => {
|
||||
const action: ActionDefinition = {
|
||||
type: 'click',
|
||||
handler: 'apiCall',
|
||||
target: '/api/test/pipe-body',
|
||||
params: {
|
||||
method: 'POST',
|
||||
auth_required: false,
|
||||
body: {
|
||||
date: "{{row.created_at | datetime('YYYY-MM-DD')}}",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await dispatcher.createHandler(action, dataContext)(createMockEvent());
|
||||
|
||||
const call = mockFetch.mock.calls.find((c: unknown[]) => c[0] === '/api/test/pipe-body');
|
||||
expect(call).toBeDefined();
|
||||
const body = String((call![1] as RequestInit).body);
|
||||
expect(body).not.toContain('{{');
|
||||
expect(JSON.parse(body)).toEqual({ date: '2024-01-15' });
|
||||
});
|
||||
|
||||
it('인자 없는 파이프가 비트 OR 오답으로 전송되지 않는다', async () => {
|
||||
const action: ActionDefinition = {
|
||||
type: 'click',
|
||||
handler: 'apiCall',
|
||||
target: '/api/test/pipe-body-noarg',
|
||||
params: {
|
||||
method: 'POST',
|
||||
auth_required: false,
|
||||
body: { count: '{{row.tags | length}}' },
|
||||
},
|
||||
};
|
||||
|
||||
await dispatcher.createHandler(action, dataContext)(createMockEvent());
|
||||
|
||||
const call = mockFetch.mock.calls.find(
|
||||
(c: unknown[]) => c[0] === '/api/test/pipe-body-noarg'
|
||||
);
|
||||
expect(call).toBeDefined();
|
||||
const body = JSON.parse(String((call![1] as RequestInit).body));
|
||||
expect(body.count).toBe(3);
|
||||
expect(body.count).not.toBe(0);
|
||||
});
|
||||
|
||||
it('GET 쿼리스트링에 {{ 리터럴이 실리지 않는다', async () => {
|
||||
const action: ActionDefinition = {
|
||||
type: 'click',
|
||||
handler: 'apiCall',
|
||||
target: '/api/test/pipe-query',
|
||||
params: {
|
||||
method: 'GET',
|
||||
auth_required: false,
|
||||
query: { date: "{{row.created_at | date('YYYY-MM-DD')}}" },
|
||||
},
|
||||
};
|
||||
|
||||
await dispatcher.createHandler(action, dataContext)(createMockEvent());
|
||||
|
||||
const call = mockFetch.mock.calls.find((c: unknown[]) =>
|
||||
String(c[0]).startsWith('/api/test/pipe-query')
|
||||
);
|
||||
expect(call).toBeDefined();
|
||||
const url = String(call![0]);
|
||||
expect(url).not.toContain('%7B%7B');
|
||||
expect(url).toContain('date=2024-01-15');
|
||||
});
|
||||
|
||||
it('파이프 없는 params 는 종전 경로 그대로 (회귀 보호)', async () => {
|
||||
const action: ActionDefinition = {
|
||||
type: 'click',
|
||||
handler: 'apiCall',
|
||||
target: '/api/test/no-pipe',
|
||||
params: {
|
||||
method: 'POST',
|
||||
auth_required: false,
|
||||
body: { tags: '{{row.tags}}', total: '{{row.price}}' },
|
||||
},
|
||||
};
|
||||
|
||||
await dispatcher.createHandler(action, dataContext)(createMockEvent());
|
||||
|
||||
const call = mockFetch.mock.calls.find((c: unknown[]) => c[0] === '/api/test/no-pipe');
|
||||
const body = JSON.parse(String((call![1] as RequestInit).body));
|
||||
expect(body.tags).toEqual(['a', 'b', 'c']);
|
||||
expect(body.total).toBe(1234567);
|
||||
});
|
||||
|
||||
it('논리 OR(||)는 파이프로 오인되지 않는다 (회귀 보호)', async () => {
|
||||
const action: ActionDefinition = {
|
||||
type: 'click',
|
||||
handler: 'apiCall',
|
||||
target: '/api/test/logical-or',
|
||||
params: {
|
||||
method: 'POST',
|
||||
auth_required: false,
|
||||
body: { name: "{{row.missing || 'fallback'}}" },
|
||||
},
|
||||
};
|
||||
|
||||
await dispatcher.createHandler(action, dataContext)(createMockEvent());
|
||||
|
||||
const call = mockFetch.mock.calls.find((c: unknown[]) => c[0] === '/api/test/logical-or');
|
||||
expect(JSON.parse(String((call![1] as RequestInit).body)).name).toBe('fallback');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* 판정기 통일 라우팅 diff 하네스
|
||||
*
|
||||
* 종전 엔진에는 "이 문자열이 단일 바인딩인가" 를 판정하는 구현이 4종, "이 식이 복잡한
|
||||
* 표현식인가" 를 판정하는 정규식이 6종 있었고 서로 인식 문자 집합이 달랐다. 그래서
|
||||
* **같은 식이 prop 에서는 undefined 인데 `if` 에서는 정상** 같은 비대칭이 생겼다.
|
||||
*
|
||||
* `BindingShape` 로 판정을 통일하면 일부 식의 라우팅이 바뀐다. 이 테스트는 저장소의
|
||||
* 모든 레이아웃 JSON 에서 단일 바인딩을 수집해 **구 방언들과 신 정본의 판정을 대조**하고,
|
||||
* 판정이 바뀌는 식 전체를 소속 파일과 함께 스냅샷으로 고정한다. 목록에 항목이
|
||||
* 늘어나면(=새 레이아웃이 라우팅 변경 대상 식을 도입하면) 이 테스트가 깨진다.
|
||||
*
|
||||
* @since engine-v1.55.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { hasPipes } from '../PipeRegistry';
|
||||
import {
|
||||
classifyExpression,
|
||||
extractSingleBinding as canonicalExtract,
|
||||
isComplexExpression as canonicalIsComplex,
|
||||
LITERALS,
|
||||
} from '../BindingShape';
|
||||
|
||||
const REPO_ROOT = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..', '..', '..', '..', '..',
|
||||
);
|
||||
|
||||
const LAYOUT_ROOTS = [
|
||||
'resources/layouts',
|
||||
'templates/_bundled',
|
||||
'modules/_bundled',
|
||||
'plugins/_bundled',
|
||||
];
|
||||
|
||||
// ── 구 방언 (교체 대상) ────────────────────────────────────────────────
|
||||
|
||||
/** DataBindingEngine.resolveBindings 의 isExpression — 단일 `|` 제외, `||` 만 인식 */
|
||||
const oldComplexResolveBindings = (e: string) =>
|
||||
/[?:&!+\-*/<>=()]/.test(e) || /\|\|/.test(e) || /\[['"]/.test(e);
|
||||
|
||||
/** DataBindingEngine 의 isBaseExpression (파이프 base / resolveObject) */
|
||||
const oldComplexDataBindingEngine = (e: string) =>
|
||||
/[?:|&!+\-*/<>=()]/.test(e) || /\[['"]/.test(e);
|
||||
|
||||
/** DynamicRenderer / ConditionEvaluator — 가장 좁은 방언 */
|
||||
const oldComplexNarrow = (e: string) => /[?:|&!+\-*/<>=()]/.test(e);
|
||||
|
||||
/** RenderHelpers — 최대집합 (신 정본의 출처) */
|
||||
const oldComplexRenderHelpers = (e: string) => /[?:|&!+\-*/<>=()[\]{}]/.test(e);
|
||||
|
||||
const OLD_COMPLEX_DIALECTS: Record<string, (e: string) => boolean> = {
|
||||
'DataBindingEngine.resolveBindings': oldComplexResolveBindings,
|
||||
'DataBindingEngine.isBaseExpression': oldComplexDataBindingEngine,
|
||||
'DynamicRenderer/ConditionEvaluator': oldComplexNarrow,
|
||||
'RenderHelpers': oldComplexRenderHelpers,
|
||||
};
|
||||
|
||||
/** 액션/데이터소스/TemplateApp 의 greedy 단일 바인딩 정규식 */
|
||||
const oldExtractGreedy = (v: string): string | null => {
|
||||
const m = v.match(/^\{\{(.+)\}\}$/);
|
||||
return m ? m[1].trim() : null;
|
||||
};
|
||||
|
||||
/** G7CoreGlobals 의 `}` 불허 정규식 */
|
||||
const oldExtractNoBrace = (v: string): string | null => {
|
||||
const m = v.match(/^\{\{([^}]+)\}\}$/);
|
||||
return m ? m[1].trim() : null;
|
||||
};
|
||||
|
||||
// ── 레이아웃 수집 ──────────────────────────────────────────────────────
|
||||
|
||||
interface Occurrence {
|
||||
file: string;
|
||||
jsonPath: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function walkFiles(dir: string, acc: string[] = []): string[] {
|
||||
let entries: fs.Dirent[];
|
||||
try {
|
||||
entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
} catch {
|
||||
return acc;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
if (entry.name === 'node_modules' || entry.name === 'vendor') continue;
|
||||
walkFiles(full, acc);
|
||||
continue;
|
||||
}
|
||||
if (entry.name.endsWith('.json') && full.replace(/\\/g, '/').includes('/layouts/')) {
|
||||
acc.push(full);
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
function collectStrings(node: unknown, segs: (string | number)[], file: string, out: Occurrence[]): void {
|
||||
if (typeof node === 'string') {
|
||||
if (node.includes('{{')) {
|
||||
out.push({ file, jsonPath: segs.join('.'), value: node });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(node)) {
|
||||
node.forEach((v, i) => collectStrings(v, segs.concat(i), file, out));
|
||||
return;
|
||||
}
|
||||
if (node && typeof node === 'object') {
|
||||
for (const [k, v] of Object.entries(node)) collectStrings(v, segs.concat(k), file, out);
|
||||
}
|
||||
}
|
||||
|
||||
function collectOccurrences(): Occurrence[] {
|
||||
const out: Occurrence[] = [];
|
||||
for (const root of LAYOUT_ROOTS) {
|
||||
for (const file of walkFiles(path.join(REPO_ROOT, root))) {
|
||||
let json: unknown;
|
||||
try {
|
||||
json = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
const relative = path.relative(REPO_ROOT, file).replace(/\\/g, '/');
|
||||
collectStrings(json, [], relative, out);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const occurrences = collectOccurrences();
|
||||
|
||||
describe('BindingShape 라우팅 diff 하네스', () => {
|
||||
it('레이아웃 모집단을 실제로 수집한다 (하네스 자체 회귀 방지)', () => {
|
||||
const files = new Set(occurrences.map((o) => o.file));
|
||||
expect(files.size).toBeGreaterThan(100);
|
||||
expect(occurrences.length).toBeGreaterThan(1000);
|
||||
});
|
||||
|
||||
it('단일 바인딩 판정: 구 정규식과 신 정본이 갈리는 총량', () => {
|
||||
const values = new Set<string>();
|
||||
for (const occ of occurrences) {
|
||||
const canonical = canonicalExtract(occ.value);
|
||||
if (canonical === oldExtractGreedy(occ.value) && canonical === oldExtractNoBrace(occ.value)) continue;
|
||||
values.add(occ.value);
|
||||
}
|
||||
// 대부분은 greedy 정규식(`^\{\{(.+)\}\}$`)이 `"{{a}}-{{b}}"` 같은 보간 문자열을
|
||||
// 단일 바인딩으로 오판하던 경우다. 총량만 고정하고, 실제 피해가 나는 지점
|
||||
// (액션 params / 데이터소스 params)은 아래에서 따로 목록화한다.
|
||||
expect(values.size).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('단일 바인딩 판정: greedy 정규식이 실제로 쓰이는 지점(액션/데이터소스 params)의 갈리는 값', () => {
|
||||
const isGreedySite = (jsonPath: string) =>
|
||||
/(^|\.)data_sources\./.test(jsonPath) ||
|
||||
(/\.params(\.|$)/.test(jsonPath) &&
|
||||
(/(^|\.)actions?\./.test(jsonPath) || /(^|\.)init_actions\./.test(jsonPath) || /\.on[A-Z]/.test(jsonPath)));
|
||||
|
||||
const diffs = new Map<string, { files: Set<string>; greedy: string | null; canonical: string | null }>();
|
||||
for (const occ of occurrences) {
|
||||
if (!isGreedySite(occ.jsonPath)) continue;
|
||||
const canonical = canonicalExtract(occ.value);
|
||||
const greedy = oldExtractGreedy(occ.value);
|
||||
if (canonical === greedy) continue;
|
||||
|
||||
const entry = diffs.get(occ.value) ?? { files: new Set<string>(), greedy, canonical };
|
||||
entry.files.add(`${occ.file} :: ${occ.jsonPath}`);
|
||||
diffs.set(occ.value, entry);
|
||||
}
|
||||
|
||||
const report = [...diffs.entries()]
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([value, e]) => ({
|
||||
value,
|
||||
greedy: e.greedy,
|
||||
canonical: e.canonical,
|
||||
sites: [...e.files].sort(),
|
||||
}));
|
||||
|
||||
expect(report).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('표현식/경로 판정: 구 방언과 신 정본이 갈리는 식 목록', () => {
|
||||
const diffs = new Map<string, { files: Set<string>; old: Record<string, boolean>; canonical: boolean }>();
|
||||
|
||||
for (const occ of occurrences) {
|
||||
const inner = canonicalExtract(occ.value);
|
||||
if (inner === null) continue;
|
||||
// 파이프·리터럴·빈 식은 복잡식 판정에 도달하지 않는다.
|
||||
const shape = classifyExpression(inner);
|
||||
if (shape === 'pipe' || shape === 'literal' || shape === 'empty') continue;
|
||||
|
||||
const canonical = canonicalIsComplex(inner);
|
||||
const old: Record<string, boolean> = {};
|
||||
let diverges = false;
|
||||
for (const [name, fn] of Object.entries(OLD_COMPLEX_DIALECTS)) {
|
||||
old[name] = fn(inner);
|
||||
if (old[name] !== canonical) diverges = true;
|
||||
}
|
||||
if (!diverges) continue;
|
||||
|
||||
const entry = diffs.get(inner) ?? { files: new Set<string>(), old, canonical };
|
||||
entry.files.add(occ.file);
|
||||
diffs.set(inner, entry);
|
||||
}
|
||||
|
||||
const report = [...diffs.entries()]
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([expr, e]) => ({
|
||||
expr,
|
||||
canonical: e.canonical,
|
||||
old: e.old,
|
||||
files: [...e.files].sort(),
|
||||
}));
|
||||
|
||||
expect(report).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('판정이 갈리는 식의 분류별 건수 요약', () => {
|
||||
let quotedBracket = 0;
|
||||
let numericIndex = 0;
|
||||
let other = 0;
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const occ of occurrences) {
|
||||
const inner = canonicalExtract(occ.value);
|
||||
if (inner === null || seen.has(inner)) continue;
|
||||
const shape = classifyExpression(inner);
|
||||
if (shape === 'pipe' || shape === 'literal' || shape === 'empty') continue;
|
||||
const canonical = canonicalIsComplex(inner);
|
||||
const diverges = Object.values(OLD_COMPLEX_DIALECTS).some((fn) => fn(inner) !== canonical);
|
||||
if (!diverges) continue;
|
||||
seen.add(inner);
|
||||
|
||||
if (/\[\s*['"]/.test(inner)) quotedBracket++;
|
||||
else if (/\[\s*\d+\s*\]/.test(inner)) numericIndex++;
|
||||
else other++;
|
||||
}
|
||||
|
||||
expect({ quotedBracket, numericIndex, other, total: seen.size }).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('빈 바인딩(`{{}}`)은 저장소에 존재하지 않는다', () => {
|
||||
const empties = occurrences.filter((o) => {
|
||||
const inner = canonicalExtract(o.value);
|
||||
return inner !== null && classifyExpression(inner) === 'empty';
|
||||
});
|
||||
expect(empties.map((o) => `${o.file} :: ${o.jsonPath}`)).toEqual([]);
|
||||
});
|
||||
|
||||
it('파이프 식은 파이프로 분류된다 (분류 순서 회귀 방지)', () => {
|
||||
const pipeExprs = occurrences
|
||||
.map((o) => canonicalExtract(o.value))
|
||||
.filter((e): e is string => e !== null)
|
||||
.filter((e) => hasPipes(e));
|
||||
expect(pipeExprs.length).toBeGreaterThan(0);
|
||||
for (const e of pipeExprs) {
|
||||
expect(classifyExpression(e)).toBe('pipe');
|
||||
}
|
||||
});
|
||||
|
||||
it('리터럴 식은 경로로 해석되지 않는다', () => {
|
||||
for (const literal of LITERALS.keys()) {
|
||||
expect(classifyExpression(literal)).toBe('literal');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* 판정 통일 회귀 테스트 (engine-v1.55.0)
|
||||
*
|
||||
* 같은 식이 렌더 경로마다 다른 결과를 내던 비대칭을 고정한다.
|
||||
* - 따옴표 키 인덱싱(`query['sales_status[]']`)이 prop·text·if·conditions 4경로에서 동일
|
||||
* - 숫자 인덱싱(`items[0]`)은 경로 이동(resolve → evaluateExpression) 후에도 결과 동치
|
||||
* - 빈 바인딩(`{{}}`)은 컨텍스트 객체 전체가 아니라 undefined
|
||||
* - `"{{a}}-{{b}}"` 는 단일 바인딩이 아니다 (greedy 정규식 오판 해소)
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import DynamicRenderer, { ComponentDefinition } from '../DynamicRenderer';
|
||||
import { ComponentRegistry } from '../ComponentRegistry';
|
||||
import { DataBindingEngine } from '../DataBindingEngine';
|
||||
import { TranslationEngine, TranslationContext } from '../TranslationEngine';
|
||||
import { ActionDispatcher } from '../ActionDispatcher';
|
||||
import { evaluateStringCondition } from '../helpers/ConditionEvaluator';
|
||||
import { evaluateIfCondition } from '../helpers/RenderHelpers';
|
||||
import {
|
||||
classifyExpression,
|
||||
extractSingleBinding,
|
||||
isComplexExpression,
|
||||
resolveSingleBindingValue,
|
||||
} from '../BindingShape';
|
||||
|
||||
const Probe: React.FC<{ value?: any; children?: React.ReactNode }> = ({ value, children }) => (
|
||||
<div data-testid="probe" data-json={JSON.stringify(value ?? null)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
describe('BindingShape — 판정 통일', () => {
|
||||
describe('extractSingleBinding 정본', () => {
|
||||
it('식 안의 객체 리터럴을 단일 바인딩으로 인식한다', () => {
|
||||
expect(extractSingleBinding('{{error.errors ?? {}}}')).toBe('error.errors ?? {}');
|
||||
});
|
||||
|
||||
it('따옴표 안의 중괄호는 균형 계산에서 제외한다', () => {
|
||||
expect(extractSingleBinding("{{query['a}b']}}")).toBe("query['a}b']");
|
||||
});
|
||||
|
||||
it('여러 바인딩이 이어진 문자열은 단일 바인딩이 아니다 (greedy 오판 해소)', () => {
|
||||
expect(extractSingleBinding('{{a}}-{{b}}')).toBeNull();
|
||||
expect(extractSingleBinding('{{_global.shopBase}}/category/{{cat?.slug}}')).toBeNull();
|
||||
});
|
||||
|
||||
it('앞뒤 공백은 제거된다', () => {
|
||||
expect(extractSingleBinding('{{ user.name }}')).toBe('user.name');
|
||||
});
|
||||
|
||||
it('빈 바인딩은 빈 문자열로 인식된다 (null 아님)', () => {
|
||||
expect(extractSingleBinding('{{}}')).toBe('');
|
||||
expect(classifyExpression('')).toBe('empty');
|
||||
});
|
||||
|
||||
it('바인딩이 아닌 문자열은 null', () => {
|
||||
expect(extractSingleBinding('hello {{name}}')).toBeNull();
|
||||
expect(extractSingleBinding('plain')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isComplexExpression 정본 (최대집합)', () => {
|
||||
it('따옴표 키 인덱싱을 표현식으로 본다', () => {
|
||||
expect(isComplexExpression("query['sales_status[]']")).toBe(true);
|
||||
});
|
||||
|
||||
it('배열/객체 리터럴을 표현식으로 본다', () => {
|
||||
expect(isComplexExpression("['a','b']")).toBe(true);
|
||||
expect(isComplexExpression('[{ id: 1 }]')).toBe(true);
|
||||
});
|
||||
|
||||
it('순수 점 경로는 경로로 본다', () => {
|
||||
expect(isComplexExpression('user.profile.name')).toBe(false);
|
||||
expect(classifyExpression('user.profile.name')).toBe('path');
|
||||
});
|
||||
});
|
||||
|
||||
describe('빈 바인딩 봉인', () => {
|
||||
let engine: DataBindingEngine;
|
||||
beforeEach(() => {
|
||||
engine = new DataBindingEngine();
|
||||
});
|
||||
|
||||
it('resolveObject 의 빈 바인딩은 undefined (컨텍스트 객체 미반환)', () => {
|
||||
const context = { secret: 'do-not-leak', _global: { token: 'x' } };
|
||||
const out: any = engine.resolveObject({ v: '{{}}' }, context);
|
||||
expect(out.v).toBeUndefined();
|
||||
});
|
||||
|
||||
it('빈 경로가 resolve 로 들어가도 컨텍스트 전체를 반환하지 않는다', () => {
|
||||
const context = { secret: 'do-not-leak' };
|
||||
expect(engine.resolve('', context, { skipCache: true })).toBeUndefined();
|
||||
expect(engine.resolve(' ', context, { skipCache: true })).toBeUndefined();
|
||||
});
|
||||
|
||||
it('조건에서의 빈 바인딩은 false 로 판정된다', () => {
|
||||
const context = { secret: 'do-not-leak' };
|
||||
expect(evaluateStringCondition('{{}}', context, engine)).toBe(false);
|
||||
expect(evaluateIfCondition('{{}}', context, engine, 'test')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('숫자 인덱싱 경로 이동 동치성 (4 컨텍스트)', () => {
|
||||
let engine: DataBindingEngine;
|
||||
beforeEach(() => {
|
||||
engine = new DataBindingEngine();
|
||||
});
|
||||
|
||||
const cases: { name: string; context: any; expr: string }[] = [
|
||||
{ name: '존재', context: { rows: [{ id: 'a' }, { id: 'b' }] }, expr: 'rows[1]' },
|
||||
{ name: '미존재(범위 밖)', context: { rows: [{ id: 'a' }] }, expr: 'rows[3]' },
|
||||
{ name: 'null', context: { rows: null }, expr: 'rows[0]' },
|
||||
{ name: '중첩 미정의', context: {}, expr: 'missing.deep[0]' },
|
||||
];
|
||||
|
||||
for (const c of cases) {
|
||||
it(`${c.name}: resolve 와 evaluateExpression 결과가 같다`, () => {
|
||||
const viaResolve = engine.resolve(c.expr, c.context, { skipCache: true });
|
||||
let viaEval: unknown;
|
||||
try {
|
||||
viaEval = engine.evaluateExpression(c.expr, c.context);
|
||||
} catch {
|
||||
viaEval = undefined;
|
||||
}
|
||||
expect(viaEval).toEqual(viaResolve);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('따옴표 키 인덱싱 — prop / text / if / conditions 4경로 동일값', () => {
|
||||
let registry: ComponentRegistry;
|
||||
let bindingEngine: DataBindingEngine;
|
||||
let translationEngine: TranslationEngine;
|
||||
let actionDispatcher: ActionDispatcher;
|
||||
let translationContext: TranslationContext;
|
||||
|
||||
const context = { query: { 'sales_status[]': ['selling', 'soldout'] } };
|
||||
const EXPR = "query['sales_status[]']";
|
||||
|
||||
beforeEach(() => {
|
||||
registry = ComponentRegistry.getInstance();
|
||||
(registry as any).registry = {
|
||||
Probe: { component: Probe, metadata: { name: 'Probe', type: 'basic' } },
|
||||
};
|
||||
bindingEngine = new DataBindingEngine();
|
||||
translationEngine = new TranslationEngine();
|
||||
actionDispatcher = new ActionDispatcher({ navigate: vi.fn() });
|
||||
translationContext = { templateId: 'test-template', locale: 'ko' };
|
||||
});
|
||||
|
||||
const renderDef = (componentDef: ComponentDefinition) =>
|
||||
render(
|
||||
<DynamicRenderer
|
||||
componentDef={componentDef}
|
||||
dataContext={context}
|
||||
translationContext={translationContext}
|
||||
registry={registry}
|
||||
bindingEngine={bindingEngine}
|
||||
translationEngine={translationEngine}
|
||||
actionDispatcher={actionDispatcher}
|
||||
/>
|
||||
);
|
||||
|
||||
it('prop 경로에서 배열이 전달된다', () => {
|
||||
const view = renderDef({
|
||||
id: 'prop', type: 'basic', name: 'Probe', props: { value: `{{${EXPR}}}` },
|
||||
});
|
||||
expect(JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!)).toEqual([
|
||||
'selling',
|
||||
'soldout',
|
||||
]);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('text 경로에서 값이 비지 않는다', () => {
|
||||
const view = renderDef({
|
||||
id: 'text', type: 'basic', name: 'Probe', props: {}, text: `{{${EXPR}}}`,
|
||||
});
|
||||
expect(screen.getByTestId('probe').textContent).toContain('selling');
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('if 경로에서 truthy 로 판정된다', () => {
|
||||
expect(evaluateIfCondition(`{{${EXPR}}}`, context, bindingEngine, 'test')).toBe(true);
|
||||
});
|
||||
|
||||
it('conditions 경로에서 truthy 로 판정된다', () => {
|
||||
expect(evaluateStringCondition(`{{${EXPR}}}`, context, bindingEngine)).toBe(true);
|
||||
});
|
||||
|
||||
it('네 경로가 모두 같은 값을 본다 (엔진 직접 해석과 동치)', () => {
|
||||
const direct = resolveSingleBindingValue(EXPR, context, bindingEngine, { skipCache: true });
|
||||
expect(direct).toEqual(['selling', 'soldout']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -50,4 +50,36 @@ describe('DataBindingEngine — {}}} 닫기 모호성 (단일 바인딩 내부
|
||||
const out: any = eng.resolveObject({ s: '{{a}} {{b}}' }, ctx, {});
|
||||
expect(out.s).toBe('X Y');
|
||||
});
|
||||
|
||||
/**
|
||||
* 파이프 식 안에 중괄호가 든 경우.
|
||||
*
|
||||
* 종전에는 파이프 분기가 `resolveBindings(`{{...}}`)` 로 위임했는데, 그 안의
|
||||
* `BINDING_PATTERN`(`/\{\{([^}]+)\}\}/g`)이 `}` 를 포함한 식을 매칭하지 못해
|
||||
* `String.replace` 가 입력을 그대로 돌려주었다. 결과적으로 원본 `{{...}}` 문자열이
|
||||
* 화면에 노출됐다. evaluatePipeExpression 직접 호출로 문자열 조립 자체를 없앴다.
|
||||
* @since engine-v1.54.9
|
||||
*/
|
||||
describe('파이프 식 내부 중괄호', () => {
|
||||
it('{{(row.meta ?? {}) | json}} 가 원본 문자열로 새지 않는다', () => {
|
||||
const eng = new DataBindingEngine();
|
||||
const ctx: any = { row: {} }; // row.meta === undefined → ?? {} fallback
|
||||
const out: any = eng.resolveObject({ meta: '{{(row.meta ?? {}) | json}}' }, ctx, {});
|
||||
expect(out.meta).toBe('{}');
|
||||
expect(out.meta).not.toContain('{{');
|
||||
});
|
||||
|
||||
it('중괄호를 포함한 파이프 식이 값이 있을 때도 정상 평가된다', () => {
|
||||
const eng = new DataBindingEngine();
|
||||
const ctx: any = { row: { meta: { a: 1 } } };
|
||||
const out: any = eng.resolveObject({ meta: '{{(row.meta ?? {}) | json}}' }, ctx, {});
|
||||
expect(out.meta).toBe('{"a":1}');
|
||||
});
|
||||
|
||||
it('evaluatePipeExpression 직접 호출도 동일하게 평가된다', () => {
|
||||
const eng = new DataBindingEngine();
|
||||
const ctx: any = { row: { meta: { a: 1 } } };
|
||||
expect(eng.evaluatePipeExpression('(row.meta ?? {}) | json', ctx)).toBe('{"a":1}');
|
||||
});
|
||||
});
|
||||
});
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* 파서 견고화 회귀 테스트 (engine-v1.55.1)
|
||||
*
|
||||
* - 보간 문자열 안의 바인딩을 정규식이 아니라 스캐너로 찾는다 (식 안의 `}` 허용)
|
||||
* - `resolveObject` 는 key 단위로 실패를 격리한다 (한 표현식이 컴포넌트 전체를 죽이지 않음)
|
||||
* - 중첩 객체 해석에도 컴포넌트별 skipBindingKeys 가 적용된다
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { DataBindingEngine } from '../DataBindingEngine';
|
||||
import { scanBindings } from '../BindingShape';
|
||||
|
||||
describe('DataBindingEngine — 파서 견고화', () => {
|
||||
let engine: DataBindingEngine;
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new DataBindingEngine();
|
||||
});
|
||||
|
||||
describe('scanBindings', () => {
|
||||
it('식 안의 중괄호를 포함한 바인딩을 끝까지 찾는다', () => {
|
||||
const found = scanBindings('a {{x ?? {}}} b');
|
||||
expect(found).toHaveLength(1);
|
||||
expect(found[0].expr).toBe('x ?? {}');
|
||||
});
|
||||
|
||||
it('연속된 바인딩을 각각 찾는다', () => {
|
||||
const found = scanBindings('{{a}}-{{b}}');
|
||||
expect(found.map((f) => f.expr)).toEqual(['a', 'b']);
|
||||
});
|
||||
|
||||
it('따옴표 안의 중괄호는 바인딩 종료로 보지 않는다', () => {
|
||||
const found = scanBindings("{{q['a}b']}}");
|
||||
expect(found).toHaveLength(1);
|
||||
expect(found[0].expr).toBe("q['a}b']");
|
||||
});
|
||||
|
||||
it('닫히지 않은 `{{` 는 바인딩이 아니다', () => {
|
||||
expect(scanBindings('{{unclosed')).toEqual([]);
|
||||
expect(scanBindings('plain text')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('보간 문자열 안의 중괄호 식', () => {
|
||||
it('"a {{x ?? {}}} b" 가 원본 문자열로 남지 않는다', () => {
|
||||
const out = engine.resolveBindings('a {{x ?? {}}} b', {});
|
||||
expect(out).not.toContain('{{');
|
||||
expect(out).toBe('a {} b');
|
||||
});
|
||||
|
||||
it('중괄호 식과 일반 바인딩이 섞여도 각각 치환된다', () => {
|
||||
const out = engine.resolveBindings('{{name}} / {{meta ?? {}}}', { name: 'g7' });
|
||||
expect(out).toBe('g7 / {}');
|
||||
});
|
||||
|
||||
it('바인딩이 없는 문자열은 그대로 반환한다 (회귀 보호)', () => {
|
||||
expect(engine.resolveBindings('plain', {})).toBe('plain');
|
||||
});
|
||||
|
||||
it('닫히지 않은 `{{` 는 리터럴로 남는다 (종전 정규식과 동일)', () => {
|
||||
expect(engine.resolveBindings('{{unclosed', {})).toBe('{{unclosed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveObject 의 key 단위 예외 격리', () => {
|
||||
it('한 key 의 표현식이 던져도 나머지 key 는 정상 해석된다', () => {
|
||||
const context = {
|
||||
ok: 'fine',
|
||||
get boom(): string {
|
||||
throw new Error('evaluation failed');
|
||||
},
|
||||
};
|
||||
|
||||
const out: any = engine.resolveObject(
|
||||
{ a: '{{ok}}', b: '{{(boom) + 1}}', c: '{{ok}}' },
|
||||
context,
|
||||
{ skipCache: true },
|
||||
);
|
||||
|
||||
expect(out.a).toBe('fine');
|
||||
expect(out.b).toBeUndefined();
|
||||
expect(out.c).toBe('fine');
|
||||
});
|
||||
|
||||
it('중첩 객체 안의 실패는 그 안쪽 key 로 격리된다 (재귀 호출도 같은 규칙)', () => {
|
||||
const context = {
|
||||
get boom(): string {
|
||||
throw new Error('evaluation failed');
|
||||
},
|
||||
ok: 1,
|
||||
};
|
||||
|
||||
const out: any = engine.resolveObject(
|
||||
{ style: { color: '{{(boom) + 1}}' }, id: '{{ok}}' },
|
||||
context,
|
||||
{ skipCache: true },
|
||||
);
|
||||
|
||||
expect(out.id).toBe(1);
|
||||
// 중첩 객체는 재귀 resolveObject 가 처리하므로 실패가 그 안쪽 key 에서 멈춘다.
|
||||
expect(out.style).toEqual({ color: undefined });
|
||||
});
|
||||
});
|
||||
|
||||
describe('skipBindingKeys', () => {
|
||||
it('전달된 키는 중첩 깊이와 무관하게 원본 그대로 남는다', () => {
|
||||
const context = { row: { id: 7 } };
|
||||
const out: any = engine.resolveObject(
|
||||
{
|
||||
wrapper: {
|
||||
children: [{ text: '{{row.id}}' }],
|
||||
label: '{{row.id}}',
|
||||
},
|
||||
},
|
||||
context,
|
||||
{ skipCache: true, skipBindingKeys: ['children'] },
|
||||
);
|
||||
|
||||
expect(out.wrapper.children[0].text).toBe('{{row.id}}');
|
||||
expect(out.wrapper.label).toBe(7);
|
||||
});
|
||||
|
||||
it('기본 스킵 키(cellChildren)는 옵션 없이도 유지된다 (회귀 보호)', () => {
|
||||
const context = { row: { id: 7 } };
|
||||
const out: any = engine.resolveObject(
|
||||
{ columns: [{ cellChildren: [{ text: '{{row.id}}' }] }] },
|
||||
context,
|
||||
{ skipCache: true },
|
||||
);
|
||||
expect(out.columns[0].cellChildren[0].text).toBe('{{row.id}}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('식별자가 아닌 컨텍스트 키 (engine-v1.56.2)', () => {
|
||||
// 표현식 평가는 컨텍스트 키를 `new Function` 의 파라미터로 넘긴다. 파라미터가 될 수
|
||||
// 없는 키가 하나라도 있으면 함수 생성 자체가 SyntaxError 로 실패해, 그 키를 쓰지도
|
||||
// 않는 식까지 **그 컨텍스트에서는 전부** 평가되지 못했다.
|
||||
it('대괄호가 든 키가 있어도 다른 식이 정상 평가된다', () => {
|
||||
const context: any = { 'sales_status[]': ['selling'], user: { name: '홍길동' } };
|
||||
|
||||
expect(engine.evaluateExpression("user.name ?? ''", context)).toBe('홍길동');
|
||||
});
|
||||
|
||||
it('하이픈이 든 키가 있어도 다른 식이 정상 평가된다', () => {
|
||||
const context: any = { 'data-id': 3, count: 5 };
|
||||
|
||||
expect(engine.evaluateExpression('count * 2', context)).toBe(10);
|
||||
});
|
||||
|
||||
it('예약어 키가 있어도 다른 식이 정상 평가된다', () => {
|
||||
const context: any = { class: 'x', new: 1, total: 4 };
|
||||
|
||||
expect(engine.evaluateExpression('total + 1', context)).toBe(5);
|
||||
});
|
||||
|
||||
it('상위 객체를 거치는 실제 작성 형태는 그대로 동작한다', () => {
|
||||
// 레이아웃의 실사용은 최상위 키가 아니라 `query['sales_status[]']` 형태다.
|
||||
const context: any = { query: { 'sales_status[]': ['selling'] } };
|
||||
|
||||
expect(engine.evaluateExpression("query['sales_status[]']", context)).toEqual(['selling']);
|
||||
});
|
||||
|
||||
it('같은 식이라도 컨텍스트 키 구성이 다르면 캐시가 섞이지 않는다', () => {
|
||||
expect(engine.evaluateExpression('a + 1', { a: 1 } as any)).toBe(2);
|
||||
expect(engine.evaluateExpression('a + 1', { a: 10, 'bad-key': 1 } as any)).toBe(11);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -223,6 +223,70 @@ describe('DataBindingEngine - Pipe Integration', () => {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 파이프 결과의 타입 보존.
|
||||
*
|
||||
* `resolveBindings` 는 보간(문자열 산출)이 목적이라 결과에 formatValue 를 적용한다.
|
||||
* 종전에는 값이 필요한 지점(prop·조건·반복 렌더)도 이 메서드에 위임했기 때문에
|
||||
* 배열이 `"[\"a\",\"b\"]"` 로, `false` 가 `"false"`(truthy!) 로 바뀌어 나갔다.
|
||||
* @since engine-v1.54.9
|
||||
*/
|
||||
describe('evaluatePipeExpression — 원본 타입 보존', () => {
|
||||
it('배열을 반환하는 파이프는 배열 그대로 반환한다', () => {
|
||||
const context = { row: { meta: { a: 1, b: 2 } } };
|
||||
expect(engine.evaluatePipeExpression('row.meta | keys', context)).toEqual(['a', 'b']);
|
||||
expect(engine.evaluatePipeExpression('row.meta | values', context)).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('boolean 을 반환하는 파이프는 문자열로 바뀌지 않는다', () => {
|
||||
const context = { row: { flags: [false, true] } };
|
||||
expect(engine.evaluatePipeExpression('row.flags | first', context)).toBe(false);
|
||||
expect(engine.evaluatePipeExpression('row.flags | last', context)).toBe(true);
|
||||
});
|
||||
|
||||
it('숫자를 반환하는 파이프는 number 타입을 유지한다', () => {
|
||||
const context = { row: { tags: ['a', 'b', 'c'] } };
|
||||
expect(engine.evaluatePipeExpression('row.tags | length', context)).toBe(3);
|
||||
});
|
||||
|
||||
it('resolveBindings 는 종전대로 문자열로 서식한다 (보간 계약 유지)', () => {
|
||||
const context = { row: { tags: ['a', 'b', 'c'] } };
|
||||
expect(engine.resolveBindings('{{row.tags | length}}', context)).toBe('3');
|
||||
expect(typeof engine.resolveBindings('{{row.flags | first}}', { row: { flags: [false] } })).toBe('string');
|
||||
});
|
||||
|
||||
it('resolveObject 의 단일 바인딩은 파이프 결과 타입을 유지한다', () => {
|
||||
const context = { row: { meta: { a: 1 }, flags: [false] } };
|
||||
const out: any = engine.resolveObject(
|
||||
{ keys: '{{row.meta | keys}}', flag: '{{row.flags | first}}' },
|
||||
context,
|
||||
);
|
||||
expect(out.keys).toEqual(['a']);
|
||||
expect(out.flag).toBe(false);
|
||||
});
|
||||
|
||||
it('평가 실패는 호출 지점으로 전파된다 (resolveObject 는 key 단위로 격리)', () => {
|
||||
const makeContext = () => ({
|
||||
row: { a: 1 },
|
||||
get boom(): string {
|
||||
throw new Error('base expression failed');
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => engine.evaluatePipeExpression('(boom) | uppercase', makeContext())).toThrow();
|
||||
|
||||
// resolveObject 에는 상위 catch 가 없으므로 실패한 key 만 undefined 로 떨어지고
|
||||
// 같은 객체의 다른 key 는 정상 해석되어야 한다.
|
||||
const out: any = engine.resolveObject(
|
||||
{ broken: '{{(boom) | uppercase}}', intact: '{{row}}' },
|
||||
makeContext(),
|
||||
{ skipCache: true },
|
||||
);
|
||||
expect(out.broken).toBeUndefined();
|
||||
expect(out.intact).toEqual({ a: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('skipCache와 파이프', () => {
|
||||
it('skipCache: true로 매번 새로 평가', () => {
|
||||
let counter = 0;
|
||||
|
||||
@@ -252,6 +252,70 @@ describe('DataSourceManager', () => {
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* 데이터소스 params 의 파이프.
|
||||
*
|
||||
* `resolveParamValue` 는 단일 `{{...}}` 를 evaluateExpression 으로 평가하는데,
|
||||
* 그 경로는 `|` 를 JS 비트 OR 로 본다. 인자 있는 파이프는 예외로 catch 에 걸려
|
||||
* **원본 `{{...}}` 문자열이 그대로 쿼리스트링에 실려 서버로 전송**됐다.
|
||||
* @since engine-v1.54.10
|
||||
*/
|
||||
it('params 의 인자 있는 파이프가 원본 {{...}} 로 전송되지 않아야 함', async () => {
|
||||
const mockResponse = { data: [] };
|
||||
const mockApiClient = getApiClient();
|
||||
(mockApiClient.get as any).mockResolvedValue(mockResponse);
|
||||
|
||||
const sources: DataSource[] = [
|
||||
{
|
||||
id: 'pipe-param-api',
|
||||
type: 'api',
|
||||
endpoint: '/api/admin/orders',
|
||||
method: 'GET',
|
||||
auth_required: true,
|
||||
auto_fetch: true,
|
||||
params: { from: "{{query.from | date('YYYY-MM-DD')}}" },
|
||||
},
|
||||
];
|
||||
|
||||
await manager.fetchDataSources(sources, {}, new URLSearchParams('from=2024-01-15T14:30:00'));
|
||||
|
||||
expect(mockApiClient.get).toHaveBeenCalledWith(
|
||||
'/admin/orders',
|
||||
expect.objectContaining({
|
||||
params: { from: '2024-01-15' },
|
||||
}),
|
||||
);
|
||||
const sentParams = (mockApiClient.get as any).mock.calls[0][1].params;
|
||||
expect(JSON.stringify(sentParams)).not.toContain('{{');
|
||||
});
|
||||
|
||||
it('params 의 인자 없는 파이프가 비트 OR 오답으로 전송되지 않아야 함', async () => {
|
||||
const mockResponse = { data: [] };
|
||||
const mockApiClient = getApiClient();
|
||||
(mockApiClient.get as any).mockResolvedValue(mockResponse);
|
||||
|
||||
const sources: DataSource[] = [
|
||||
{
|
||||
id: 'pipe-param-noarg',
|
||||
type: 'api',
|
||||
endpoint: '/api/admin/orders',
|
||||
method: 'GET',
|
||||
auth_required: true,
|
||||
auto_fetch: true,
|
||||
params: { code: '{{query.code | uppercase}}' },
|
||||
},
|
||||
];
|
||||
|
||||
await manager.fetchDataSources(sources, {}, new URLSearchParams('code=abc'));
|
||||
|
||||
expect(mockApiClient.get).toHaveBeenCalledWith(
|
||||
'/admin/orders',
|
||||
expect.objectContaining({
|
||||
params: { code: 'ABC' },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('쿼리 파라미터를 치환할 수 있어야 함 (점 표기법)', async () => {
|
||||
const mockResponse = { data: [] };
|
||||
const mockApiClient = getApiClient();
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* iteration + `if` 항목별 평가 회귀 테스트 (engine-v1.56.0)
|
||||
*
|
||||
* `if` 가 항목 변수를 참조하면 부모 시점 컨텍스트에는 그 변수가 없어 조건이 항상 false 가
|
||||
* 되고, 조건 평가가 iteration 분기보다 먼저 걸려 **목록 전체가 렌더되지 않았다**.
|
||||
* 반복 렌더 경로(renderItemChildren)는 항목별로 평가하므로 같은 작성이 거기서는 정상
|
||||
* 동작했고, 그 비대칭 때문에 재현 위치를 특정하기 어려웠다.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import DynamicRenderer, { ComponentDefinition } from '../DynamicRenderer';
|
||||
import { ComponentRegistry } from '../ComponentRegistry';
|
||||
import { DataBindingEngine } from '../DataBindingEngine';
|
||||
import { TranslationEngine, TranslationContext } from '../TranslationEngine';
|
||||
import { ActionDispatcher } from '../ActionDispatcher';
|
||||
|
||||
const Row: React.FC<{ label?: string; children?: React.ReactNode }> = ({ label, children }) => (
|
||||
<div data-testid="row">{label ?? children}</div>
|
||||
);
|
||||
|
||||
describe('DynamicRenderer — iteration + if', () => {
|
||||
let registry: ComponentRegistry;
|
||||
let bindingEngine: DataBindingEngine;
|
||||
let translationEngine: TranslationEngine;
|
||||
let actionDispatcher: ActionDispatcher;
|
||||
let translationContext: TranslationContext;
|
||||
|
||||
beforeEach(() => {
|
||||
registry = ComponentRegistry.getInstance();
|
||||
(registry as any).registry = {
|
||||
Row: { component: Row, metadata: { name: 'Row', type: 'basic' } },
|
||||
};
|
||||
bindingEngine = new DataBindingEngine();
|
||||
translationEngine = new TranslationEngine();
|
||||
actionDispatcher = new ActionDispatcher({ navigate: vi.fn() });
|
||||
translationContext = { templateId: 'test-template', locale: 'ko' };
|
||||
});
|
||||
|
||||
const renderDef = (componentDef: ComponentDefinition, dataContext: any) =>
|
||||
render(
|
||||
<DynamicRenderer
|
||||
componentDef={componentDef}
|
||||
dataContext={dataContext}
|
||||
translationContext={translationContext}
|
||||
registry={registry}
|
||||
bindingEngine={bindingEngine}
|
||||
translationEngine={translationEngine}
|
||||
actionDispatcher={actionDispatcher}
|
||||
/>
|
||||
);
|
||||
|
||||
const context = {
|
||||
users: [
|
||||
{ name: '가', is_active: true },
|
||||
{ name: '나', is_active: false },
|
||||
{ name: '다', is_active: true },
|
||||
],
|
||||
};
|
||||
|
||||
it('항목 변수를 참조하는 if 가 항목별로 평가된다 (종전에는 목록 전체 미렌더)', () => {
|
||||
renderDef(
|
||||
{
|
||||
id: 'list',
|
||||
type: 'basic',
|
||||
name: 'Row',
|
||||
props: { label: '{{user.name}}' },
|
||||
if: '{{user.is_active}}',
|
||||
iteration: { source: '{{users}}', item_var: 'user' },
|
||||
} as ComponentDefinition,
|
||||
context
|
||||
);
|
||||
|
||||
const rows = screen.getAllByTestId('row');
|
||||
expect(rows.map((r) => r.textContent)).toEqual(['가', '다']);
|
||||
});
|
||||
|
||||
it('외곽 변수만 참조하는 if 는 결과가 종전과 같다 (false → 전체 미렌더)', () => {
|
||||
const view = renderDef(
|
||||
{
|
||||
id: 'list-off',
|
||||
type: 'basic',
|
||||
name: 'Row',
|
||||
props: { label: '{{user.name}}' },
|
||||
if: '{{flags.visible}}',
|
||||
iteration: { source: '{{users}}', item_var: 'user' },
|
||||
} as ComponentDefinition,
|
||||
{ ...context, flags: { visible: false } }
|
||||
);
|
||||
|
||||
expect(screen.queryAllByTestId('row')).toHaveLength(0);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('외곽 변수 if 가 true 면 전 항목이 렌더된다 (회귀 보호)', () => {
|
||||
const view = renderDef(
|
||||
{
|
||||
id: 'list-on',
|
||||
type: 'basic',
|
||||
name: 'Row',
|
||||
props: { label: '{{user.name}}' },
|
||||
if: '{{flags.visible}}',
|
||||
iteration: { source: '{{users}}', item_var: 'user' },
|
||||
} as ComponentDefinition,
|
||||
{ ...context, flags: { visible: true } }
|
||||
);
|
||||
|
||||
expect(screen.getAllByTestId('row')).toHaveLength(3);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('iteration 없는 컴포넌트의 if 는 종전대로 즉시 차단한다 (회귀 보호)', () => {
|
||||
const view = renderDef(
|
||||
{
|
||||
id: 'single',
|
||||
type: 'basic',
|
||||
name: 'Row',
|
||||
props: { label: 'x' },
|
||||
if: '{{flags.visible}}',
|
||||
} as ComponentDefinition,
|
||||
{ flags: { visible: false } }
|
||||
);
|
||||
|
||||
expect(screen.queryAllByTestId('row')).toHaveLength(0);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('{item_var}_index 자동 변수가 주입된다 (반복 렌더 경로와 동일)', () => {
|
||||
const view = renderDef(
|
||||
{
|
||||
id: 'idx',
|
||||
type: 'basic',
|
||||
name: 'Row',
|
||||
props: { label: '{{user_index}}' },
|
||||
iteration: { source: '{{users}}', item_var: 'user' },
|
||||
} as ComponentDefinition,
|
||||
context
|
||||
);
|
||||
|
||||
expect(screen.getAllByTestId('row').map((r) => r.textContent)).toEqual(['0', '1', '2']);
|
||||
view.unmount();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* DynamicRenderer 파이프 렌더 경로 회귀 테스트
|
||||
*
|
||||
* #87 수정(engine-v1.54.3)이 파이프 평가를 `resolveBindings(`{{...}}`)` 위임으로 처리하면서
|
||||
* 남긴 비대칭 3건을 렌더 경로에서 고정한다. 셋 다 엔진 단위 테스트로는 잡히지 않는다 —
|
||||
* 결함이 "어떤 옵션으로 어떤 메서드를 호출하는가" 라는 호출 지점에 있기 때문이다.
|
||||
*
|
||||
* - D1: `}` 가 든 파이프 식이 원본 `{{...}}` 문자열로 화면에 노출
|
||||
* - D2: `_computed` 파이프가 영구 캐시를 타 컴포넌트 간 stale 값 전파
|
||||
* - D3: 파이프 결과가 항상 문자열로 서식되어 배열/boolean 이 소실
|
||||
*
|
||||
* @since engine-v1.54.9
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import DynamicRenderer, { ComponentDefinition } from '../DynamicRenderer';
|
||||
import { ComponentRegistry } from '../ComponentRegistry';
|
||||
import { DataBindingEngine } from '../DataBindingEngine';
|
||||
import { TranslationEngine, TranslationContext } from '../TranslationEngine';
|
||||
import { ActionDispatcher } from '../ActionDispatcher';
|
||||
|
||||
/** prop 으로 전달된 값의 타입을 그대로 드러내는 프로브 컴포넌트 */
|
||||
const TypeProbe: React.FC<{ value?: any; children?: React.ReactNode }> = ({ value }) => (
|
||||
<div
|
||||
data-testid="probe"
|
||||
data-kind={Array.isArray(value) ? 'array' : typeof value}
|
||||
data-json={JSON.stringify(value ?? null)}
|
||||
/>
|
||||
);
|
||||
|
||||
const Plain: React.FC<{ children?: React.ReactNode }> = ({ children }) => (
|
||||
<div data-testid="plain">{children}</div>
|
||||
);
|
||||
|
||||
describe('DynamicRenderer — 파이프 렌더 경로 비대칭 (engine-v1.54.9)', () => {
|
||||
let registry: ComponentRegistry;
|
||||
let bindingEngine: DataBindingEngine;
|
||||
let translationEngine: TranslationEngine;
|
||||
let actionDispatcher: ActionDispatcher;
|
||||
let translationContext: TranslationContext;
|
||||
|
||||
beforeEach(() => {
|
||||
registry = ComponentRegistry.getInstance();
|
||||
(registry as any).registry = {
|
||||
TypeProbe: { component: TypeProbe, metadata: { name: 'TypeProbe', type: 'basic' } },
|
||||
Plain: { component: Plain, metadata: { name: 'Plain', type: 'basic' } },
|
||||
};
|
||||
|
||||
bindingEngine = new DataBindingEngine();
|
||||
translationEngine = new TranslationEngine();
|
||||
actionDispatcher = new ActionDispatcher({ navigate: vi.fn() });
|
||||
translationContext = { templateId: 'test-template', locale: 'ko' };
|
||||
});
|
||||
|
||||
const renderDef = (componentDef: ComponentDefinition, dataContext: any) =>
|
||||
render(
|
||||
<DynamicRenderer
|
||||
componentDef={componentDef}
|
||||
dataContext={dataContext}
|
||||
translationContext={translationContext}
|
||||
registry={registry}
|
||||
bindingEngine={bindingEngine}
|
||||
translationEngine={translationEngine}
|
||||
actionDispatcher={actionDispatcher}
|
||||
/>
|
||||
);
|
||||
|
||||
describe('D1 — 파이프 식 내부 중괄호', () => {
|
||||
it('text 의 {{(row.meta ?? {}) | json}} 가 원본 문자열로 노출되지 않는다', () => {
|
||||
const def: ComponentDefinition = {
|
||||
id: 'd1-text',
|
||||
type: 'basic',
|
||||
name: 'Plain',
|
||||
props: {},
|
||||
text: '{{(row.meta ?? {}) | json}}',
|
||||
};
|
||||
|
||||
renderDef(def, { row: { meta: { a: 1 } } });
|
||||
|
||||
const el = screen.getByTestId('plain');
|
||||
expect(el).toHaveTextContent('{"a":1}');
|
||||
expect(el.textContent).not.toContain('{{');
|
||||
});
|
||||
|
||||
it('prop 의 파이프 식 내부 중괄호도 동일하게 평가된다', () => {
|
||||
const def: ComponentDefinition = {
|
||||
id: 'd1-prop',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{(row.meta ?? {}) | json}}' },
|
||||
};
|
||||
|
||||
renderDef(def, { row: {} });
|
||||
|
||||
const el = screen.getByTestId('probe');
|
||||
expect(el.getAttribute('data-kind')).toBe('string');
|
||||
expect(JSON.parse(el.getAttribute('data-json')!)).toBe('{}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('D2 — _computed 파이프의 컴포넌트 간 stale 전파', () => {
|
||||
it('같은 엔진으로 렌더된 다른 컴포넌트가 이전 _computed 값을 재사용하지 않는다', () => {
|
||||
const first = renderDef(
|
||||
{
|
||||
id: 'd2-a',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{_computed.total | number}}' },
|
||||
},
|
||||
{ _computed: { total: 1000 } }
|
||||
);
|
||||
expect(screen.getByTestId('probe').getAttribute('data-json')).toBe('"1,000"');
|
||||
first.unmount();
|
||||
|
||||
// _computed 는 컴포넌트마다 _local 기반으로 재계산된다. 파이프 분기가
|
||||
// getComputedAwareOptions 를 우회하면 baseExpr(`_computed.total`)이 영구 캐시(30초)에
|
||||
// 저장되어 다음 컴포넌트가 앞 컴포넌트의 값을 그대로 받는다.
|
||||
renderDef(
|
||||
{
|
||||
id: 'd2-b',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{_computed.total | number}}' },
|
||||
},
|
||||
{ _computed: { total: 2000 } }
|
||||
);
|
||||
expect(screen.getByTestId('probe').getAttribute('data-json')).toBe('"2,000"');
|
||||
});
|
||||
|
||||
it('$computed alias 도 동일하게 캐시를 건너뛴다', () => {
|
||||
const first = renderDef(
|
||||
{
|
||||
id: 'd2-alias-a',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{$computed.total | number}}' },
|
||||
},
|
||||
{ _computed: { total: 10 } }
|
||||
);
|
||||
expect(screen.getByTestId('probe').getAttribute('data-json')).toBe('"10"');
|
||||
first.unmount();
|
||||
|
||||
renderDef(
|
||||
{
|
||||
id: 'd2-alias-b',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{$computed.total | number}}' },
|
||||
},
|
||||
{ _computed: { total: 20 } }
|
||||
);
|
||||
expect(screen.getByTestId('probe').getAttribute('data-json')).toBe('"20"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('D3 — 파이프 결과 타입 보존', () => {
|
||||
it('배열을 반환하는 파이프가 prop 에 배열로 전달된다', () => {
|
||||
const def: ComponentDefinition = {
|
||||
id: 'd3-array',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{row.meta | keys}}' },
|
||||
};
|
||||
|
||||
renderDef(def, { row: { meta: { a: 1, b: 2 } } });
|
||||
|
||||
const el = screen.getByTestId('probe');
|
||||
expect(el.getAttribute('data-kind')).toBe('array');
|
||||
expect(JSON.parse(el.getAttribute('data-json')!)).toEqual(['a', 'b']);
|
||||
});
|
||||
|
||||
it('boolean 을 반환하는 파이프가 문자열 "false" 로 바뀌지 않는다', () => {
|
||||
const def: ComponentDefinition = {
|
||||
id: 'd3-boolean',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{row.flags | first}}' },
|
||||
};
|
||||
|
||||
renderDef(def, { row: { flags: [false, true] } });
|
||||
|
||||
const el = screen.getByTestId('probe');
|
||||
expect(el.getAttribute('data-kind')).toBe('boolean');
|
||||
expect(JSON.parse(el.getAttribute('data-json')!)).toBe(false);
|
||||
});
|
||||
|
||||
it('숫자를 반환하는 파이프가 number 타입으로 전달된다', () => {
|
||||
const def: ComponentDefinition = {
|
||||
id: 'd3-number',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{row.tags | length}}' },
|
||||
};
|
||||
|
||||
renderDef(def, { row: { tags: ['a', 'b', 'c'] } });
|
||||
|
||||
const el = screen.getByTestId('probe');
|
||||
expect(el.getAttribute('data-kind')).toBe('number');
|
||||
expect(JSON.parse(el.getAttribute('data-json')!)).toBe(3);
|
||||
});
|
||||
|
||||
it('computed 정의식의 파이프가 적용된다 (종전에는 예외 → 이전 값 유지)', () => {
|
||||
// _computedDefinitions 는 렌더마다 최신 _local 로 재계산된다. 그 평가가
|
||||
// evaluateExpression 이라 파이프가 붙으면 예외 → catch 에서 이전 값 유지가 되어
|
||||
// computed 가 영원히 갱신되지 않았다. @since engine-v1.54.10
|
||||
const def: ComponentDefinition = {
|
||||
id: 'p1-computed',
|
||||
type: 'basic',
|
||||
name: 'TypeProbe',
|
||||
props: { value: '{{_computed.formatted}}' },
|
||||
};
|
||||
|
||||
renderDef(def, {
|
||||
_local: { price: 1234567 },
|
||||
_computedDefinitions: { formatted: '{{_local.price | number}}' },
|
||||
});
|
||||
|
||||
const el = screen.getByTestId('probe');
|
||||
expect(JSON.parse(el.getAttribute('data-json')!)).toBe('1,234,567');
|
||||
});
|
||||
|
||||
it('문자열을 반환하는 기존 파이프(datetime/number)의 결과는 종전과 동일하다', () => {
|
||||
const def: ComponentDefinition = {
|
||||
id: 'd3-string',
|
||||
type: 'basic',
|
||||
name: 'Plain',
|
||||
props: {},
|
||||
text: "{{row.created_at | datetime('YYYY-MM-DD HH:mm')}}",
|
||||
};
|
||||
|
||||
renderDef(def, { row: { created_at: '2024-01-15T14:30:00' } });
|
||||
|
||||
expect(screen.getByTestId('plain')).toHaveTextContent('2024-01-15 14:30');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -284,6 +284,42 @@ describe('TranslationEngine', () => {
|
||||
);
|
||||
expect(result).toBe('prefix: 안녕하세요, 홍길동님');
|
||||
});
|
||||
|
||||
/**
|
||||
* 회귀: 파라미터 값의 표현식 평가를 엔진에 위임한다 (engine-v1.56.1).
|
||||
*
|
||||
* 종전에는 이 지점이 `new Function(...Object.keys(dataContext))` 로 자체 평가해
|
||||
* 엔진 확장 컨텍스트($localized/$t/$uuid)를 알지 못했고, 컨텍스트 키가 유효한
|
||||
* 식별자가 아니면 함수 생성 자체가 SyntaxError 로 실패했다. 두 경우 모두 예외를
|
||||
* 삼키고 **빈 문자열**을 돌려줬기 때문에, 번역 문구에서 값만 조용히 사라졌다.
|
||||
*/
|
||||
it('파라미터 표현식에서 $localized 헬퍼를 쓸 수 있다', () => {
|
||||
const result = engine.resolveTranslations(
|
||||
"$t:messages.greeting|name={{$localized('이순신')}}",
|
||||
context,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(result).toBe('안녕하세요, 이순신님');
|
||||
});
|
||||
|
||||
it('파라미터 표현식이 식별자가 아닌 컨텍스트 키와 공존해도 값이 사라지지 않는다', () => {
|
||||
// `sales_status[]` 는 함수 파라미터 이름이 될 수 없다 — 종전 구현은 여기서 실패했다.
|
||||
const dataContext = {
|
||||
'sales_status[]': ['selling'],
|
||||
user: { name: '홍길동' },
|
||||
};
|
||||
|
||||
// 단순 경로(`{{user.name}}`)는 함수를 만들지 않는 분기라 이 결함을 밟지 않는다.
|
||||
// 연산자가 들어간 식이어야 종전 구현이 `new Function` 을 시도했다.
|
||||
const result = engine.resolveTranslations(
|
||||
"$t:messages.greeting|name={{user.name ?? ''}}",
|
||||
context,
|
||||
dataContext
|
||||
);
|
||||
|
||||
expect(result).toBe('안녕하세요, 홍길동님');
|
||||
});
|
||||
});
|
||||
|
||||
describe('캐싱', () => {
|
||||
|
||||
+1049
File diff suppressed because it is too large.
Load diff
@@ -187,6 +187,42 @@ describe('iteration source / evaluateExpression - 리터럴 및 내장객체 회
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 파이프가 붙은 iteration source.
|
||||
*
|
||||
* `isComplexExpression` 은 `|` 를 연산자로 보아 evaluateExpression 으로 보내고,
|
||||
* 거기서 `배열 | pipe` 가 JS 비트 OR 로 평가되면 결과가 배열이 아니게 된다.
|
||||
* 반복 대상이 없어져 **목록 전체가 렌더되지 않는다** — 예외도 경고도 없다.
|
||||
* @since engine-v1.54.10
|
||||
*/
|
||||
describe('resolveIterationSource + 파이프', () => {
|
||||
it('인자 있는 파이프가 예외로 소실되지 않는다 (종전 undefined → 목록 전체 미렌더)', () => {
|
||||
const context = { row: { tags: ['a', 'b', 'c'] } };
|
||||
// evaluateExpression 경로에서는 `배열 | join('/')` 이 예외를 던져 undefined 가 됐다.
|
||||
// (인자 문자열의 앞뒤 공백은 파이프 인자 파서가 제거한다 — 종전과 동일한 기존 동작)
|
||||
const result = resolveIterationSource("{{row.tags | join('/')}}", context, engine);
|
||||
expect(result).toBe('a/b/c');
|
||||
expect(result).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('인자 없는 파이프가 배열 타입을 유지한다', () => {
|
||||
const context = { row: { meta: { a: 1, b: 2 } } };
|
||||
const result = resolveIterationSource('{{row.meta | values}}', context, engine);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('중괄호 없는 형태(source 축약형)에서도 동일하게 동작한다', () => {
|
||||
const context = { row: { meta: { a: 1 } } };
|
||||
expect(resolveIterationSource('row.meta | keys', context, engine)).toEqual(['a']);
|
||||
});
|
||||
|
||||
it('논리 OR(||)는 파이프로 오인되지 않는다 (회귀 보호)', () => {
|
||||
const context = { a: null, b: [1, 2] };
|
||||
expect(resolveIterationSource('{{a || b}}', context, engine)).toEqual([1, 2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveIterationSource + Set 결합 (채널 필터 실제 경로)', () => {
|
||||
it('Set 기반 채널 후보 배열을 iteration source 로 반환해야 함', () => {
|
||||
const context = {
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 파이프 분기 누락 검출 (구조 스캔)
|
||||
*
|
||||
* 사용자가 작성한 `{{...}}` 식을 `evaluateExpression` 으로 평가하는 지점은 반드시
|
||||
* 그 앞에서 `hasPipes()` 로 파이프 여부를 갈라야 한다. 갈라지 않으면 `|` 가 JS 비트 OR 로
|
||||
* 평가되어, 인자 있는 파이프는 예외(값 소실·원본 `{{...}}` 누출), 인자 없는 파이프는
|
||||
* 조용한 오답이 된다. #87 이 그 사례였고, 같은 결함이 엔진 곳곳에 복제되어 있었다.
|
||||
*
|
||||
* 손으로 열거한 케이스 목록은 "지금 아는 지점" 만 지킨다. 이 테스트는 반대로
|
||||
* **평가 지점 자체를 모집단으로 삼아** 새로 추가되는 지점까지 강제한다.
|
||||
* 의도적으로 파이프를 지원하지 않는 지점은 아래 EXEMPT 에 사유와 함께 등재한다.
|
||||
*
|
||||
* @since engine-v1.54.10
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
/**
|
||||
* 스캔 모집단은 `resources/js/core` 전체다.
|
||||
*
|
||||
* template-engine 디렉토리로 좁히면 `TemplateApp.ts` 처럼 밖에 있는 평가 지점이
|
||||
* 조용히 빠진다 — 모집단을 좁히는 순간 이 테스트는 통과하면서 아무것도 지키지 않는다.
|
||||
*/
|
||||
const ENGINE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||||
|
||||
/** 엔진 인스턴스를 통한 표현식 평가 호출 */
|
||||
const EVAL_CALL = /\b(?:this\.)?(?:bindingEngine|dataBindingEngine|engine)\.evaluateExpression\s*\(/;
|
||||
|
||||
/**
|
||||
* 파이프 분기 가드가 있어야 하는 범위(호출 지점 기준 위쪽 라인 수).
|
||||
*
|
||||
* 가드는 보통 같은 if/else 체인의 첫 분기에 있고, 그 사이에 배경 설명 주석이 들어간다.
|
||||
* 좁게 잡으면 정상 지점이 위반으로 잡히므로 넉넉히 둔다 — 이 테스트의 목적은
|
||||
* "가드가 아예 없는 지점" 을 잡는 것이다.
|
||||
*/
|
||||
const GUARD_LOOKBEHIND = 30;
|
||||
|
||||
/**
|
||||
* 파이프를 의도적으로 지원하지 않는 지점.
|
||||
*
|
||||
* key: `<엔진 루트 기준 상대경로>::<해당 라인에 포함된 고유 문자열>`
|
||||
*/
|
||||
const EXEMPT: { file: string; marker: string; reason: string }[] = [
|
||||
{
|
||||
file: 'template-engine/DynamicRenderer.tsx',
|
||||
marker: 'blurTrackingInfo',
|
||||
reason:
|
||||
'blur_until_loaded 는 truthiness 게이트다. 파이프 결과는 대부분 비어있지 않은 문자열이라 ' +
|
||||
'분기를 추가하면 블러가 영구히 켜진다. 레이아웃 사용은 정적 검사로 차단한다.',
|
||||
},
|
||||
{
|
||||
file: 'template-engine/G7CoreGlobals.ts',
|
||||
marker: 'devToolsCore.getState()',
|
||||
reason: 'DevTools 콘솔의 임의 식 평가 — 레이아웃 작성 문법이 아니라 개발자 입력이다.',
|
||||
},
|
||||
{
|
||||
file: 'template-engine/TranslationEngine.ts',
|
||||
marker: 'dataBindingEngine.evaluateExpression(expression, dataContext)',
|
||||
reason:
|
||||
'`$t:key|name=값` 의 `|` 는 파라미터 구분자다. 이 자리에서 `|` 를 파이프로 해석하면 ' +
|
||||
'구분자와 충돌하므로 파이프를 지원하지 않는다(별개 문법 영역).',
|
||||
},
|
||||
];
|
||||
|
||||
function collectSourceFiles(dir: string, acc: string[] = []): string[] {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
if (entry.name === '__tests__' || entry.name === 'node_modules') continue;
|
||||
collectSourceFiles(full, acc);
|
||||
continue;
|
||||
}
|
||||
if (/\.(ts|tsx)$/.test(entry.name) && !/\.test\.tsx?$/.test(entry.name)) {
|
||||
acc.push(full);
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
interface CallSite {
|
||||
relative: string;
|
||||
line: number;
|
||||
text: string;
|
||||
guarded: boolean;
|
||||
}
|
||||
|
||||
function collectCallSites(): CallSite[] {
|
||||
const sites: CallSite[] = [];
|
||||
for (const file of collectSourceFiles(ENGINE_ROOT)) {
|
||||
const relative = path.relative(ENGINE_ROOT, file).replace(/\\/g, '/');
|
||||
// 평가기 자신과 라우터는 스캔 대상이 아니다 —
|
||||
// DataBindingEngine 은 evaluateExpression/evaluatePipeExpression 의 구현체이고,
|
||||
// BindingShape 는 classifyExpression 으로 파이프를 먼저 갈라내는 라우터다.
|
||||
if (relative === 'template-engine/DataBindingEngine.ts') continue;
|
||||
if (relative === 'template-engine/BindingShape.ts') continue;
|
||||
|
||||
const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
|
||||
lines.forEach((text, index) => {
|
||||
if (!EVAL_CALL.test(text)) return;
|
||||
const from = Math.max(0, index - GUARD_LOOKBEHIND);
|
||||
const guarded = lines.slice(from, index + 1).some((l) => l.includes('hasPipes('));
|
||||
sites.push({ relative, line: index + 1, text: text.trim(), guarded });
|
||||
});
|
||||
}
|
||||
return sites;
|
||||
}
|
||||
|
||||
describe('파이프 분기 누락 검출 — evaluateExpression 호출 지점 전수 스캔', () => {
|
||||
const sites = collectCallSites();
|
||||
|
||||
it('스캔 모집단이 비어 있지 않다 (스캐너 자체 회귀 방지)', () => {
|
||||
expect(sites.length).toBeGreaterThan(5);
|
||||
});
|
||||
|
||||
it('모든 표현식 평가 지점이 파이프 분기를 갖거나 사유와 함께 면제되어 있다', () => {
|
||||
const violations = sites
|
||||
.filter((s) => !s.guarded)
|
||||
.filter(
|
||||
(s) =>
|
||||
!EXEMPT.some((e) => e.file === s.relative && s.text.includes(e.marker))
|
||||
)
|
||||
.map((s) => `${s.relative}:${s.line} ${s.text}`);
|
||||
|
||||
expect(violations).toEqual([]);
|
||||
});
|
||||
|
||||
it('면제 목록의 각 항목이 실제 코드에 존재한다 (좀비 면제 방지)', () => {
|
||||
const stale = EXEMPT.filter(
|
||||
(e) => !sites.some((s) => s.relative === e.file && s.text.includes(e.marker))
|
||||
).map((e) => `${e.file} :: ${e.marker}`);
|
||||
|
||||
expect(stale).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -194,9 +194,15 @@ describe('renderItemChildren - 표현식 결과 $t: 번역 후처리', () => {
|
||||
});
|
||||
|
||||
const element = result[0] as React.ReactElement;
|
||||
// raw: 바인딩이므로 번역되지 않고 원본 유지 (raw 마커로 감싸져 반환)
|
||||
// DynamicRenderer 상위에서 마커 스트리핑 처리됨
|
||||
expect(element.props.children).toBe(`${RAW_MARKER_START}$t:common.save${RAW_MARKER_END}`);
|
||||
// raw: 바인딩이므로 번역되지 않고 원본 유지.
|
||||
//
|
||||
// 마커는 번역 패스를 건너뛰게 하려는 **내부 표식**이므로 화면으로 나가기 전에 벗긴다.
|
||||
// 종전에는 이 경로가 마커를 붙인 채 React 에 넘겨 Unicode Noncharacter 두 글자가
|
||||
// 그대로 DOM 에 실렸다(단발 렌더 경로는 resolveTranslationsDeep 안에서 벗기지만
|
||||
// 반복 렌더 경로에는 그 패스가 없다). @since engine-v1.56.3
|
||||
expect(element.props.children).toBe('$t:common.save');
|
||||
expect(element.props.children).not.toContain(RAW_MARKER_START);
|
||||
expect(element.props.children).not.toContain(RAW_MARKER_END);
|
||||
});
|
||||
|
||||
it('표현식 결과가 $t: 문자열이 아니면 번역 시도 안 함', () => {
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 회귀 테스트 — 표현식 렌더 경로 비대칭 (engine-v1.54.4 ~ v1.56.3)
|
||||
*
|
||||
* 같은 작성 문법이 **어느 렌더 경로를 타느냐에 따라 다른 결과**를 내던 결함군을 고정한다.
|
||||
* 표현식 해석 로직이 엔진 곳곳에 복제되면서 복사본마다 인식 문자 집합과 분기 구성이
|
||||
* 갈린 것이 공통 원인이며, 증상 대부분이 예외가 아니라 **조용한 오답 또는 값 소실**이라
|
||||
* 화면만 봐서는 "데이터가 없다"와 구분되지 않는다.
|
||||
*
|
||||
* 여기서 고정하는 7사례:
|
||||
* 1. 따옴표 대괄호 식이 화면 위치마다 다른 값 (판정 방언)
|
||||
* 2. 액션/데이터소스 params 에 `{{...}}` 원문 전송 (파이프 선분기 누락)
|
||||
* 3. iteration source 파이프 → 목록 전멸
|
||||
* 4. iteration + 항목 참조 `if` → 목록 전체 미렌더
|
||||
* 5. 빈 바인딩 `{{}}` → 컨텍스트 객체 전체 노출
|
||||
* 6. 비식별자 컨텍스트 키 → SyntaxError → 영역 전체 실종
|
||||
* 7. raw 마커(U+FDD0/U+FDD1) 화면 누출
|
||||
*
|
||||
* 이 파일의 역할은 **증상 ↔ 코드 단언의 앵커**다. 각 결함의 상세 회귀선은 전용 테스트가
|
||||
* 따로 갖고 있으므로 여기서는 증상을 그대로 재현하는 최소 단언만 둔다.
|
||||
*
|
||||
* - 판정 통일 상세: `BindingShape.routingUnification.test.tsx`
|
||||
* - 라우팅 diff 하네스: `BindingShape.routingParity.test.ts`
|
||||
* - 파이프 선분기 구조 검사: `pipeBranchParity.test.ts`
|
||||
* - 반복 경로 패리티: `helpers/__tests__/RenderHelpers.repeatParity.test.tsx`
|
||||
* - iteration + if: `DynamicRenderer.iterationCondition.test.tsx`
|
||||
* - 파서 견고화: `DataBindingEngine.parserHardening.test.ts`
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { DataBindingEngine } from '../DataBindingEngine';
|
||||
import { classifyExpression, extractSingleBinding } from '../BindingShape';
|
||||
import { hasPipes } from '../PipeRegistry';
|
||||
import { evaluateStringCondition } from '../helpers/ConditionEvaluator';
|
||||
|
||||
describe('트러블슈팅 회귀 — 표현식 렌더 경로 비대칭', () => {
|
||||
let engine: DataBindingEngine;
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new DataBindingEngine();
|
||||
engine.clearCache?.();
|
||||
});
|
||||
|
||||
describe('[TS-RENDERPATH-1] 같은 식이 화면 위치마다 다른 값 (따옴표 대괄호)', () => {
|
||||
const EXPR = "query['sales_status[]']";
|
||||
const context = { query: { 'sales_status[]': ['selling', 'sold_out'] } };
|
||||
|
||||
it('따옴표 대괄호 식은 경로가 아니라 표현식으로 분류된다', () => {
|
||||
// 종전 좁은 방언(/[?:|&!+\-*/<>=()]/)은 `['...']` 를 인식하지 못해
|
||||
// 식 전체를 경로 문자열로 보고 undefined 를 반환했다.
|
||||
expect(classifyExpression(EXPR)).toBe('expression');
|
||||
});
|
||||
|
||||
it('평가 결과가 undefined 가 아니라 실제 값이다', () => {
|
||||
const value = engine.evaluateExpression(EXPR, context);
|
||||
expect(value).toEqual(['selling', 'sold_out']);
|
||||
});
|
||||
|
||||
it('조건 평가 경로도 같은 값을 본다 (경로 간 동일값)', () => {
|
||||
const viaCondition = evaluateStringCondition(`{{${EXPR}.length > 0}}`, context, engine);
|
||||
expect(viaCondition).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[TS-RENDERPATH-2] 액션/데이터소스 params 에 `{{...}}` 원문 전송', () => {
|
||||
const context = { row: { created_at: '2026-08-03 10:20:30' } };
|
||||
|
||||
it('인자 있는 파이프가 원본 문자열로 새지 않는다', () => {
|
||||
const expr = "row.created_at | datetime('YYYY-MM-DD')";
|
||||
expect(hasPipes(expr)).toBe(true);
|
||||
|
||||
const result = engine.evaluatePipeExpression(expr, context);
|
||||
expect(String(result)).not.toContain('{{');
|
||||
expect(String(result)).toBe('2026-08-03');
|
||||
});
|
||||
|
||||
it('인자 없는 파이프가 비트 OR 오답(0)이 되지 않는다', () => {
|
||||
// 종전: `'2026-08-03 10:20:30' | uppercase` 를 파이프 선분기 없이 표현식으로 평가하면
|
||||
// `|` 가 JS 비트 OR 로 해석되어 숫자 0 이 됐다 (예외 없는 조용한 오답).
|
||||
const result = engine.evaluatePipeExpression('row.title | uppercase', {
|
||||
row: { title: 'order-2026' },
|
||||
});
|
||||
|
||||
expect(result).not.toBe(0);
|
||||
expect(result).toBe('ORDER-2026');
|
||||
});
|
||||
|
||||
it('파이프 식은 선분기 없이 evaluateExpression 으로 보내면 안 된다 (구조 단언)', () => {
|
||||
// 평가 지점은 반드시 hasPipes 로 갈라야 한다는 계약을 문서 사례와 함께 고정한다.
|
||||
expect(hasPipes("row.created_at | datetime('YYYY-MM-DD')")).toBe(true);
|
||||
expect(hasPipes('row.created_at')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[TS-RENDERPATH-3] iteration source 파이프 → 목록 전멸', () => {
|
||||
it('파이프가 걸린 source 도 배열을 돌려준다', () => {
|
||||
const context = { items: [{ n: 3 }, { n: 1 }, { n: 2 }] };
|
||||
const result = engine.evaluatePipeExpression('items | length', context);
|
||||
|
||||
// 배열이 아니면 반복 렌더가 조용히 빈 배열로 처리된다 — 타입 보존이 방어선이다.
|
||||
expect(result).toBe(3);
|
||||
expect(typeof result).not.toBe('string');
|
||||
});
|
||||
|
||||
it('파이프 결과가 배열이면 배열 그대로 유지된다 (문자열화 금지)', () => {
|
||||
const context = { row: { tags: ['a', 'b', 'c'] } };
|
||||
const result = engine.evaluatePipeExpression('row.tags | keys', context);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[TS-RENDERPATH-5] 빈 바인딩이 컨텍스트 객체 전체를 노출', () => {
|
||||
it('빈 식은 empty 로 분류된다', () => {
|
||||
expect(extractSingleBinding('{{}}')).toBe('');
|
||||
expect(classifyExpression('')).toBe('empty');
|
||||
});
|
||||
|
||||
it('빈 경로 해석이 컨텍스트 객체를 반환하지 않는다', () => {
|
||||
const context = { _global: { secret: 'x' }, _local: { a: 1 } };
|
||||
const resolved = engine.resolve('', context);
|
||||
|
||||
expect(resolved).toBeUndefined();
|
||||
expect(JSON.stringify(resolved ?? null)).not.toContain('secret');
|
||||
});
|
||||
});
|
||||
|
||||
describe('[TS-RENDERPATH-6] 비식별자 컨텍스트 키가 표현식 전체를 죽임', () => {
|
||||
it('식별자가 아닌 키가 컨텍스트에 있어도 표현식이 평가된다', () => {
|
||||
// 종전: 컨텍스트 키를 함수 파라미터명으로 넘겨 SyntaxError → 영역 전체 미렌더.
|
||||
const context = {
|
||||
'sales_status[]': ['selling'],
|
||||
'data-id': 7,
|
||||
query: { page: 2 },
|
||||
user: { name: '홍길동' },
|
||||
};
|
||||
|
||||
expect(() => engine.evaluateExpression('user.name', context)).not.toThrow();
|
||||
expect(engine.evaluateExpression('user.name', context)).toBe('홍길동');
|
||||
expect(engine.evaluateExpression('query.page + 1', context)).toBe(3);
|
||||
});
|
||||
|
||||
it('비식별자 키는 대괄호 인덱싱으로 접근한다', () => {
|
||||
const context = { 'sales_status[]': ['selling'] };
|
||||
expect(engine.evaluateExpression("this['sales_status[]']?.length ?? 0", context)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[TS-RENDERPATH 공통] 파서 견고화 — 중괄호 포함 식', () => {
|
||||
it('보간 문자열 안의 중괄호 식이 원본으로 남지 않는다', () => {
|
||||
const context = { x: null };
|
||||
const out = engine.resolveBindings('a {{x ?? {}}} b', context);
|
||||
expect(out).not.toContain('{{');
|
||||
});
|
||||
|
||||
it('인접 바인딩 2개가 각각 치환된다 (greedy 오판 해소)', () => {
|
||||
const context = { a: '1', b: '2' };
|
||||
expect(engine.resolveBindings('{{a}}-{{b}}', context)).toBe('1-2');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,8 @@
|
||||
*/
|
||||
|
||||
import { DataBindingEngine, BindingContext } from '../DataBindingEngine';
|
||||
import { hasPipes } from '../PipeRegistry';
|
||||
import { extractSingleBinding, resolveSingleBindingValue } from '../BindingShape';
|
||||
import { stripRawPrefix } from '../rawMarkers';
|
||||
import { createLogger } from '../../utils/Logger';
|
||||
|
||||
const logger = createLogger('ConditionEvaluator');
|
||||
@@ -128,76 +129,25 @@ export interface ConditionBranchResult {
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* JavaScript 리터럴 값 해석
|
||||
* 조건 문자열의 단일 바인딩 값을 형태에 따라 해석
|
||||
*
|
||||
* `true`, `false`, `null`, `undefined` 등의 리터럴을 직접 반환합니다.
|
||||
* resolve()가 이들을 컨텍스트 경로로 해석하는 것을 방지합니다.
|
||||
* 판정(단일 바인딩 여부·리터럴·파이프·표현식·경로)은 BindingShape 정본을 쓰고,
|
||||
* 실행 정책(캐시 무시, 빈 바인딩 경고)만 이 지점이 정한다.
|
||||
*
|
||||
* @since engine-v1.17.6
|
||||
* @returns `{ value: any }` 또는 리터럴이 아닌 경우 `undefined`
|
||||
* @since engine-v1.55.0
|
||||
*/
|
||||
function resolveLiteral(expr: string): { value: any } | undefined {
|
||||
switch (expr) {
|
||||
case 'true':
|
||||
return { value: true };
|
||||
case 'false':
|
||||
return { value: false };
|
||||
case 'null':
|
||||
return { value: null };
|
||||
case 'undefined':
|
||||
return { value: undefined };
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 복잡한 표현식 여부 감지
|
||||
*
|
||||
* 삼항 연산자(?:), 논리 연산자(&&, ||), 비교 연산자, 함수 호출 등이 포함된 경우 true 반환
|
||||
*/
|
||||
function isComplexExpression(expr: string): boolean {
|
||||
return /[?:|&!+\-*/<>=()]/.test(expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 바인딩 표현식 추출
|
||||
*
|
||||
* `{{expression}}` 형태에서 expression 부분만 추출합니다.
|
||||
* 문자열 리터럴 내부의 `}` 문자를 올바르게 처리합니다.
|
||||
*/
|
||||
function extractSingleBinding(value: string): string | null {
|
||||
if (!value.startsWith('{{') || !value.endsWith('}}')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inner = value.slice(2, -2);
|
||||
let inString: string | null = null;
|
||||
let braceCount = 0;
|
||||
|
||||
for (let i = 0; i < inner.length; i++) {
|
||||
const char = inner[i];
|
||||
const prevChar = i > 0 ? inner[i - 1] : '';
|
||||
|
||||
// 이스케이프 문자 처리
|
||||
if (prevChar === '\\') continue;
|
||||
|
||||
// 문자열 시작/종료 감지
|
||||
if ((char === '"' || char === "'") && !inString) {
|
||||
inString = char;
|
||||
} else if (char === inString) {
|
||||
inString = null;
|
||||
}
|
||||
|
||||
// 문자열 외부에서만 중괄호 카운트
|
||||
if (!inString) {
|
||||
if (char === '{') braceCount++;
|
||||
if (char === '}') braceCount--;
|
||||
if (braceCount < 0) return null;
|
||||
}
|
||||
}
|
||||
|
||||
return braceCount === 0 ? inner.trim() : null;
|
||||
function resolveConditionBinding(
|
||||
expr: string,
|
||||
context: BindingContext,
|
||||
bindingEngine: DataBindingEngine,
|
||||
label: string,
|
||||
): unknown {
|
||||
return resolveSingleBindingValue(stripRawPrefix(expr), context, bindingEngine, {
|
||||
skipCache: true,
|
||||
onEmpty: () => {
|
||||
logger.warn(`${label}: 빈 바인딩 \`{{}}\` — undefined 로 해석합니다.`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -238,23 +188,15 @@ export function evaluateStringCondition(
|
||||
|
||||
let resolved: any;
|
||||
if (singleBindingPath !== null) {
|
||||
// JavaScript 리터럴 값 직접 처리 (resolve 경로 탐색 방지)
|
||||
const literalValue = resolveLiteral(singleBindingPath);
|
||||
if (literalValue !== undefined) {
|
||||
resolved = literalValue.value;
|
||||
} else if (hasPipes(singleBindingPath)) {
|
||||
// 파이프 표현식은 resolveBindings 로 위임 (파이프 체인 실행 포함).
|
||||
// isComplexExpression 은 `|` 를 연산자로 보므로 그대로 두면
|
||||
// `value | pipe` 가 JS 비트 OR 로 평가되어 조건이 잘못 판정된다.
|
||||
// @since engine-v1.54.3
|
||||
resolved = bindingEngine.resolveBindings(`{{${singleBindingPath}}}`, context, { skipCache: true });
|
||||
} else if (isComplexExpression(singleBindingPath)) {
|
||||
// Optional chaining(?.)이나 복잡한 표현식은 evaluateExpression 사용
|
||||
resolved = bindingEngine.evaluateExpression(singleBindingPath, context);
|
||||
} else {
|
||||
// 단순 경로는 resolve 메서드 사용 (원본 타입 유지)
|
||||
resolved = bindingEngine.resolve(singleBindingPath, context, { skipCache: true });
|
||||
}
|
||||
// 리터럴 → 파이프 → 표현식 → 경로 라우팅. 조건은 원본 타입으로 판정해야 한다 —
|
||||
// 문자열로 서식하면 `false`/`0` 이 아래 문자열 보정 분기에 의존하게 되고,
|
||||
// 빈 배열 `[]` 은 `"[]"`(truthy)이 된다. @since engine-v1.54.9 / v1.55.0
|
||||
resolved = resolveConditionBinding(
|
||||
singleBindingPath,
|
||||
context,
|
||||
bindingEngine,
|
||||
'evaluateStringCondition',
|
||||
);
|
||||
} else {
|
||||
// 문자열 보간인 경우 resolveBindings 사용
|
||||
resolved = bindingEngine.resolveBindings(condition, context, { skipCache: true });
|
||||
|
||||
@@ -14,8 +14,13 @@ import { ComponentDefinition } from '../DynamicRenderer';
|
||||
import { responsiveManager } from '../ResponsiveManager';
|
||||
import { getActionDispatcher } from '../ActionDispatcher';
|
||||
import { createLogger } from '../../utils/Logger';
|
||||
import { RAW_PREFIX, RAW_MARKER_START, RAW_MARKER_END, RAW_PLACEHOLDER_MARKER, isRawWrapped, unwrapRaw, containsRawMarker, wrapRawDeep } from '../rawMarkers';
|
||||
import { RAW_PREFIX, RAW_MARKER_START, RAW_MARKER_END, RAW_PLACEHOLDER_MARKER, isRawWrapped, unwrapRaw, containsRawMarker, wrapRawDeep, stripRawDeep, stripRawPrefix } from '../rawMarkers';
|
||||
import { hasPipes } from '../PipeRegistry';
|
||||
import {
|
||||
extractSingleBinding as canonicalExtractSingleBinding,
|
||||
isComplexExpression as canonicalIsComplexExpression,
|
||||
resolveSingleBindingValue,
|
||||
} from '../BindingShape';
|
||||
import type { G7DevToolsInterface } from '../G7CoreGlobals';
|
||||
import {
|
||||
evaluateConditionExpression,
|
||||
@@ -27,24 +32,6 @@ import {
|
||||
|
||||
const logger = createLogger('RenderHelpers');
|
||||
|
||||
/**
|
||||
* JavaScript 리터럴 값 매핑
|
||||
*
|
||||
* resolve()가 리터럴을 컨텍스트 경로로 해석하는 것을 방지합니다.
|
||||
* undefined는 Map에서 get() 시 undefined를 반환하므로 별도 센티널 불필요 —
|
||||
* `has()`로 존재 여부를 먼저 확인하지 않고 `get() !== undefined`로 체크합니다.
|
||||
* null은 falsy이므로 undefined와 동일하게 처리됩니다.
|
||||
*
|
||||
* @since engine-v1.29.2
|
||||
*/
|
||||
const LITERALS = new Map<string, any>([
|
||||
['true', true],
|
||||
['false', false],
|
||||
['null', null],
|
||||
// undefined는 Map.get()의 기본 반환값과 구분 불가하므로 제외
|
||||
// → resolve("undefined")는 context에 없으면 undefined 반환 → Boolean(undefined) = false (정상)
|
||||
]);
|
||||
|
||||
/**
|
||||
* G7Core.devTools 인터페이스 가져오기
|
||||
*
|
||||
@@ -116,76 +103,22 @@ export interface RenderItemChildrenOptions {
|
||||
/**
|
||||
* 복잡한 표현식 여부 감지
|
||||
*
|
||||
* 삼항 연산자(?:), 논리 연산자(&&, ||), 비교 연산자, 함수 호출,
|
||||
* 배열/객체 리터럴 등이 포함된 경우 true 반환
|
||||
* 판정은 BindingShape 정본에 위임한다 — 이 모듈의 방언이 종전 6종 중 최대집합이었고,
|
||||
* 그것이 정본이 되었다. 다른 렌더 경로들이 좁은 집합을 쓰던 것이 비대칭의 원인이었다.
|
||||
*
|
||||
* true 인 경우 evaluateExpression(JS 평가)로, false 인 경우 resolve(dot/bracket
|
||||
* path 탐색)로 라우팅된다.
|
||||
*
|
||||
* `[`/`]`/`{`/`}` 포함 (@since engine-v1.50.0):
|
||||
* `{{['a','b']}}` 같은 배열 리터럴이나 `{{[{...}]}}` 같은 객체 리터럴이
|
||||
* "단순 경로"로 오판되어 resolve() 경로탐색 → undefined 가 되던 문제를 해결한다.
|
||||
* 순수 숫자 대괄호 인덱싱(`items[0]`, `entry[1]`)은 resolve()/evaluateExpression()
|
||||
* 양 경로의 결과가 동일하므로(회귀 테스트로 입증) 경로 이동에 따른 동작 변화가 없다.
|
||||
*
|
||||
* @param expr - 검사할 표현식
|
||||
* @returns 복잡한 표현식 여부
|
||||
* @since engine-v1.55.0
|
||||
*/
|
||||
function isComplexExpression(expr: string): boolean {
|
||||
// () 추가: $localized(menu.name) 같은 함수 호출 표현식도 복잡한 표현식으로 처리
|
||||
// []{} 추가: 배열/객체 리터럴(`['a','b']`, `[{...}]`)을 evaluateExpression 으로 라우팅
|
||||
return /[?:|&!+\-*/<>=()[\]{}]/.test(expr);
|
||||
}
|
||||
const isComplexExpression = canonicalIsComplexExpression;
|
||||
|
||||
/**
|
||||
* 단일 바인딩 표현식 추출
|
||||
*
|
||||
* `{{expression}}` 형태에서 expression 부분만 추출합니다.
|
||||
* 문자열 리터럴 내부의 `}` 문자를 올바르게 처리합니다.
|
||||
* `{{expression}}` 형태에서 expression 부분만 추출한다.
|
||||
* 판정은 BindingShape 정본에 위임한다 (따옴표·이스케이프·중괄호 균형 추적).
|
||||
*
|
||||
* @param value - 검사할 문자열
|
||||
* @returns 추출된 표현식 또는 null (유효하지 않은 경우)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* extractSingleBinding("{{row.name}}") // "row.name"
|
||||
* extractSingleBinding("{{row.status === 'active' ? 'yes' : 'no'}}") // "row.status === 'active' ? 'yes' : 'no'"
|
||||
* extractSingleBinding("hello {{name}}") // null (단일 바인딩이 아님)
|
||||
* ```
|
||||
* @since engine-v1.55.0
|
||||
*/
|
||||
function extractSingleBinding(value: string): string | null {
|
||||
if (!value.startsWith('{{') || !value.endsWith('}}')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inner = value.slice(2, -2);
|
||||
let inString: string | null = null;
|
||||
let braceCount = 0;
|
||||
|
||||
for (let i = 0; i < inner.length; i++) {
|
||||
const char = inner[i];
|
||||
const prevChar = i > 0 ? inner[i - 1] : '';
|
||||
|
||||
// 이스케이프 문자 처리
|
||||
if (prevChar === '\\') continue;
|
||||
|
||||
// 문자열 시작/종료 감지 (따옴표 추적)
|
||||
if ((char === '"' || char === "'") && !inString) {
|
||||
inString = char;
|
||||
} else if (char === inString) {
|
||||
inString = null;
|
||||
}
|
||||
|
||||
// 문자열 외부에서만 중괄호 카운트
|
||||
if (!inString) {
|
||||
if (char === '{') braceCount++;
|
||||
if (char === '}') braceCount--;
|
||||
if (braceCount < 0) return null;
|
||||
}
|
||||
}
|
||||
|
||||
return braceCount === 0 ? inner.trim() : null;
|
||||
}
|
||||
const extractSingleBinding = canonicalExtractSingleBinding;
|
||||
|
||||
/**
|
||||
* iteration source 표현식을 평가하여 배열을 반환합니다.
|
||||
@@ -220,6 +153,19 @@ export function resolveIterationSource(
|
||||
normalizedSource = source.slice(2, -2).trim();
|
||||
}
|
||||
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 (원본 타입 = 배열 유지).
|
||||
// isComplexExpression 은 `|` 를 연산자로 보므로 그대로 두면 evaluateExpression 이
|
||||
// JS 비트 OR 로 평가해 배열이 아닌 값이 나오고, 그러면 반복 대상이 없어져
|
||||
// 목록 전체가 렌더되지 않는다 (에러 없이 빈 화면). @since engine-v1.54.10
|
||||
if (hasPipes(normalizedSource)) {
|
||||
try {
|
||||
return bindingEngine.evaluatePipeExpression(normalizedSource, context, { skipCache: true });
|
||||
} catch (error) {
|
||||
logger.warn(`resolveIterationSource: 파이프 표현식 평가 실패: ${source}`, error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// 표현식 여부 확인 (?, :, ||, &&, !, 산술/비교 연산자 등)
|
||||
if (isComplexExpression(normalizedSource)) {
|
||||
// 표현식인 경우 evaluateExpression 사용 (원본 타입 유지)
|
||||
@@ -435,22 +381,22 @@ export function evaluateIfCondition(
|
||||
|
||||
let resolved: any;
|
||||
if (singleBindingPath !== null) {
|
||||
// JavaScript 리터럴 값 직접 처리 (resolve 경로 탐색 방지) @since engine-v1.29.2
|
||||
const literal = LITERALS.get(singleBindingPath);
|
||||
if (literal !== undefined) {
|
||||
resolved = literal;
|
||||
} else if (hasPipes(singleBindingPath)) {
|
||||
// 파이프 표현식은 resolveBindings 로 위임 (파이프 체인 실행 포함).
|
||||
// `|` 를 연산자로 보아 evaluateExpression 으로 보내면 JS 비트 OR 로
|
||||
// 평가되어 조건이 잘못 판정된다. @since engine-v1.54.3
|
||||
resolved = bindingEngine.resolveBindings(`{{${singleBindingPath}}}`, context, { skipCache: true });
|
||||
} else if (isComplexExpression(singleBindingPath)) {
|
||||
// Optional chaining(?.)이나 복잡한 표현식은 evaluateExpression 사용
|
||||
resolved = bindingEngine.evaluateExpression(singleBindingPath, context);
|
||||
} else {
|
||||
// 단순 경로는 resolve 메서드 사용 (원본 타입 유지)
|
||||
resolved = bindingEngine.resolve(singleBindingPath, context, { skipCache: true });
|
||||
}
|
||||
// 리터럴 → 파이프 → 표현식 → 경로 라우팅 (판정은 BindingShape 정본).
|
||||
// 조건은 원본 타입으로 판정한다 — ConditionEvaluator 와 동일 규칙.
|
||||
// @since engine-v1.54.9 / v1.55.0
|
||||
resolved = resolveSingleBindingValue(
|
||||
stripRawPrefix(singleBindingPath),
|
||||
context,
|
||||
bindingEngine,
|
||||
{
|
||||
skipCache: true,
|
||||
onEmpty: () => {
|
||||
logger.warn(
|
||||
`evaluateIfCondition: 빈 바인딩 \`{{}}\` (컴포넌트: ${componentId || 'unknown'}) — undefined 로 해석합니다.`,
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// 문자열 보간인 경우 resolveBindings 사용
|
||||
resolved = bindingEngine.resolveBindings(ifCondition, context, { skipCache: true });
|
||||
@@ -691,6 +637,35 @@ export function renderItemChildren(
|
||||
// 이전에는 DataGrid 등에서 수동으로 수행하던 로직
|
||||
const effectiveContext = getEffectiveContext(itemContext, options?.componentContext);
|
||||
|
||||
/**
|
||||
* 값의 리프 문자열에 남은 `$t:` 토큰을 번역
|
||||
*
|
||||
* 표현식 평가 결과가 배열/객체인 경우(예: `{{row.badges}}` → `['$t:a', '$t:b']`)에도
|
||||
* 번역이 닿도록 재귀 처리한다. raw 마커가 붙은 문자열은 번역 면제이므로 건드리지 않는다.
|
||||
*
|
||||
* @param value 평가 결과
|
||||
* @param context 항목 컨텍스트
|
||||
* @returns 번역이 적용된 값
|
||||
*
|
||||
* @since engine-v1.56.0
|
||||
*/
|
||||
const translateLeafTokens = (value: any, context: Record<string, any>): any => {
|
||||
if (typeof value === 'string') {
|
||||
if (isRawWrapped(value) || containsRawMarker(value)) return value;
|
||||
if (!/\$t:[a-zA-Z0-9._-]+/.test(value)) return value;
|
||||
return translationEngine.resolveTranslations(value, translationContext, context);
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => translateLeafTokens(item, context));
|
||||
}
|
||||
if (value && typeof value === 'object') {
|
||||
const out: Record<string, any> = {};
|
||||
for (const [k, v] of Object.entries(value)) out[k] = translateLeafTokens(v, context);
|
||||
return out;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* props 값을 바인딩/표현식 평가하여 해석
|
||||
* 반복 렌더링이므로 캐시를 사용하지 않음 (같은 경로가 다른 row에서 다른 값을 가져야 함)
|
||||
@@ -747,6 +722,35 @@ export function renderItemChildren(
|
||||
);
|
||||
}
|
||||
|
||||
// 문자열 **중간**의 `$t:` 토큰 (예: "27$t:admin.roles.form.suffix")
|
||||
//
|
||||
// 종전에는 `startsWith('$t:')` 만 봤기 때문에 반복 렌더 경로에서는 중간 `$t:` 가
|
||||
// 번역되지 않고 원본 키가 그대로 화면에 노출됐다. 일반 컴포넌트 경로
|
||||
// (DynamicRenderer.resolveTranslationsDeep)는 이미 정규식으로 중간 토큰을 처리한다.
|
||||
//
|
||||
// 이 분기는 위의 `$t:defer:` 처리보다 **뒤에** 둔다. 다만 실사용이 실제로 의존하는
|
||||
// 것은 분기 순서가 아니라 **항목 컨텍스트(`context`) 전달**이다 — 반복 서브트리 안의
|
||||
// `$t:defer:` 32건(고유 24종)은 전부 `|param={{row.x}}` 형태이고, `row` 는
|
||||
// DynamicRenderer 시점에 존재하지 않아 이 경로가 항목 컨텍스트와 함께 해소해야 한다.
|
||||
// (순서만 바꾸는 변경은 아래 `{{` 가드 때문에 결과가 같다. 컨텍스트를 떨구면 파라미터
|
||||
// 값만 조용히 사라진다 — `RenderHelpers.repeatParity.test.tsx` 가 그쪽을 잠근다.)
|
||||
//
|
||||
// JSON 구조 문자열(`{...}` / `[...]`) 안의 `$t:` 는 데이터의 일부이므로 번역하지 않는다
|
||||
// (DynamicRenderer 의 동일 가드 이식). @since engine-v1.56.0
|
||||
if (/\$t:[a-zA-Z0-9._-]+/.test(value)) {
|
||||
const trimmed = value.trim();
|
||||
const looksLikeJson =
|
||||
(trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
||||
(trimmed.startsWith('[') && trimmed.endsWith(']'));
|
||||
if (!looksLikeJson && !value.includes('{{')) {
|
||||
return translationEngine.resolveTranslations(
|
||||
value,
|
||||
translationContext,
|
||||
context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 단일 바인딩 표현식
|
||||
const singleBindingPath = extractSingleBinding(value);
|
||||
if (singleBindingPath !== null) {
|
||||
@@ -760,7 +764,7 @@ export function renderItemChildren(
|
||||
|
||||
let result;
|
||||
if (hasPipes(effectivePath)) {
|
||||
// 파이프 표현식은 resolveBindings 로 위임 (파이프 체인 실행 포함)
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다.
|
||||
// isComplexExpression 은 `|` 를 연산자로 보아 evaluateExpression 으로 보내는데,
|
||||
// 그 경로에서는 `value | pipe` 가 JS 비트 OR 로 평가되어
|
||||
// 인자 있는 파이프는 예외(값 소실), 인자 없는 파이프는 조용한 오답이 된다.
|
||||
@@ -768,7 +772,14 @@ export function renderItemChildren(
|
||||
// raw: 접두사는 아래 공통 분기(wrapRawDeep)가 담당하므로 여기서는 제거된
|
||||
// effectivePath 만 넘긴다 — 원본 value 를 넘기면 raw 마커가 이중으로 감싸진다.
|
||||
// @since engine-v1.54.3
|
||||
result = bindingEngine.resolveBindings(`{{${effectivePath}}}`, context, { skipCache: true });
|
||||
// 반복 렌더의 props 값도 원본 타입이 필요하다 (문자열 서식 미적용).
|
||||
// @since engine-v1.54.9
|
||||
try {
|
||||
result = bindingEngine.evaluatePipeExpression(effectivePath, context, { skipCache: true });
|
||||
} catch (error) {
|
||||
logger.warn('renderItemChildren: 파이프 표현식 평가 실패:', error);
|
||||
return undefined;
|
||||
}
|
||||
} else if (isComplexExpression(effectivePath)) {
|
||||
try {
|
||||
result = bindingEngine.evaluateExpression(effectivePath, context);
|
||||
@@ -785,12 +796,10 @@ export function renderItemChildren(
|
||||
// 예: {{row.published ? '$t:module.key.published' : '$t:module.key.unpublished'}}
|
||||
// → 평가 결과 "$t:module.key.published"를 번역하여 "발행" 반환
|
||||
// @since engine-v1.28.1
|
||||
if (!isRawBinding && typeof result === 'string' && /\$t:[a-zA-Z0-9._-]+/.test(result)) {
|
||||
result = translationEngine.resolveTranslations(
|
||||
result,
|
||||
translationContext,
|
||||
context
|
||||
);
|
||||
// 결과가 배열/객체면 리프 문자열까지 재귀로 처리한다 — 종전에는 문자열만 보고
|
||||
// 배열/객체 안의 `$t:` 키가 번역되지 않은 채 화면에 노출됐다. @since engine-v1.56.0
|
||||
if (!isRawBinding) {
|
||||
result = translateLeafTokens(result, context);
|
||||
}
|
||||
|
||||
return isRawBinding && result != null ? wrapRawDeep(result) : result;
|
||||
@@ -819,6 +828,22 @@ export function renderItemChildren(
|
||||
}
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
// $switch 특수 객체 — 일반 컴포넌트 경로(resolveObject)는 지원하는데 반복 경로에만
|
||||
// 없어서, 같은 작성이 여기서는 `{ $switch: "{{...}}", $cases: {...} }` 객체 그대로
|
||||
// 컴포넌트에 전달됐다(조용한 미동작). 평가는 엔진에 위임한다.
|
||||
// @since engine-v1.56.0
|
||||
if (bindingEngine.isSwitchExpression(value)) {
|
||||
return bindingEngine.resolveSwitch(value as any, context, { skipCache: true });
|
||||
}
|
||||
|
||||
// 액션 정의 객체는 선평가하지 않는다 — 콜백 prop(onAddressSelect 등)에 담긴
|
||||
// 액션 정의를 렌더 시점에 해소하면 내부 `{{_local.xxx}}` 가 그 시점 값으로 고정되고,
|
||||
// 실행 시점의 최신 상태를 보지 못한다. ActionDispatcher 가 실행 시점에 해소한다.
|
||||
// resolveObject 의 동일 규칙을 반복 경로에도 맞춘다. @since engine-v1.56.0
|
||||
if (bindingEngine.isActionDefinition(value)) {
|
||||
return { ...value };
|
||||
}
|
||||
|
||||
const resolved: Record<string, any> = {};
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
resolved[k] = resolveValue(v, context);
|
||||
@@ -921,7 +946,9 @@ export function renderItemChildren(
|
||||
|
||||
// remount 지원: getRemountKey가 제공되면 사용, 아니면 기본 키 생성
|
||||
// childDef.id의 표현식을 해석 (예: "toggle_{{row.id}}" → "toggle_3")
|
||||
const resolvedIterationId = childDef.id ? String(resolveValue(childDef.id, iterationContext)) : undefined;
|
||||
const resolvedIterationId = childDef.id
|
||||
? String(stripRawDeep(resolveValue(childDef.id, iterationContext)))
|
||||
: undefined;
|
||||
const iterationFallbackKey = `${baseKey}-iter-${itemIndex}`;
|
||||
const iterationKey = options?.getRemountKey
|
||||
? options.getRemountKey(resolvedIterationId, iterationFallbackKey)
|
||||
@@ -955,7 +982,17 @@ export function renderItemChildren(
|
||||
devTools.trackRender(childDef.name);
|
||||
}
|
||||
|
||||
return [React.createElement(Component, { key: iterationKey, ...finalProps }, childrenNodes)];
|
||||
// raw 마커 최종 제거 — 마커는 번역 패스를 건너뛰게 하려는 내부 표식이므로
|
||||
// 화면으로 나가기 전에 벗긴다. 단발 렌더 경로는 resolveTranslationsDeep 안에서
|
||||
// 벗기지만 이 경로에는 그 패스가 없어 Unicode Noncharacter 두 글자가 그대로
|
||||
// DOM 에 실렸다. @since engine-v1.56.3
|
||||
return [
|
||||
React.createElement(
|
||||
Component,
|
||||
{ key: iterationKey, ...stripRawDeep(finalProps) },
|
||||
stripRawDeep(childrenNodes)
|
||||
),
|
||||
];
|
||||
});
|
||||
};
|
||||
|
||||
@@ -969,7 +1006,9 @@ export function renderItemChildren(
|
||||
|
||||
// remount 지원: getRemountKey가 제공되면 사용, 아니면 기본 키 생성
|
||||
// childDef.id의 표현식을 해석 (예: "toggle_{{row.id}}" → "toggle_3")
|
||||
const resolvedId = childDef.id ? String(resolveValue(childDef.id, effectiveContext)) : undefined;
|
||||
const resolvedId = childDef.id
|
||||
? String(stripRawDeep(resolveValue(childDef.id, effectiveContext)))
|
||||
: undefined;
|
||||
const fallbackKey = keyPrefix ? `${keyPrefix}-${resolvedId || index}` : (resolvedId || `child-${index}`);
|
||||
const key = options?.getRemountKey
|
||||
? options.getRemountKey(resolvedId, fallbackKey)
|
||||
@@ -1038,7 +1077,10 @@ export function renderItemChildren(
|
||||
devTools.trackRender(childDef.name);
|
||||
}
|
||||
|
||||
return [React.createElement(Component, { key, ...finalProps }, childrenNodes)];
|
||||
// raw 마커 최종 제거 (iteration 경로와 동일) @since engine-v1.56.3
|
||||
return [
|
||||
React.createElement(Component, { key, ...stripRawDeep(finalProps) }, stripRawDeep(childrenNodes)),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* 반복 렌더 경로 기능 격차 회귀 테스트 (engine-v1.56.0)
|
||||
*
|
||||
* 일반 컴포넌트 경로(DynamicRenderer)에는 있고 반복 렌더 경로(renderItemChildren)에는
|
||||
* 없던 기능, 그리고 그 반대 방향의 비대칭을 고정한다.
|
||||
*
|
||||
* 순서 의존 회귀선: `$t:defer:` 는 DynamicRenderer 가 건너뛰고 이 경로가 항목 컨텍스트와
|
||||
* 함께 처리한다. 저장소 실사용 72건이 그 분담에 의존하므로, 새로 추가한 "문자열 중간 $t:"
|
||||
* 처리가 `$t:defer:` 보다 앞서면 안 된다.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { renderItemChildren } from '../RenderHelpers';
|
||||
import { ComponentRegistry } from '../../ComponentRegistry';
|
||||
import { DataBindingEngine } from '../../DataBindingEngine';
|
||||
import { TranslationEngine } from '../../TranslationEngine';
|
||||
import { RAW_MARKER_START, RAW_MARKER_END } from '../../rawMarkers';
|
||||
|
||||
const Probe: React.FC<{ value?: any; children?: React.ReactNode }> = ({ value, children }) => (
|
||||
<span data-testid="probe" data-json={JSON.stringify(value ?? null)}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
describe('renderItemChildren — 반복 렌더 경로 기능 격차', () => {
|
||||
let registry: ComponentRegistry;
|
||||
let bindingEngine: DataBindingEngine;
|
||||
let translationEngine: TranslationEngine;
|
||||
|
||||
beforeEach(() => {
|
||||
registry = ComponentRegistry.getInstance();
|
||||
(registry as any).registry = {
|
||||
Probe: { component: Probe, metadata: { name: 'Probe', type: 'basic' } },
|
||||
};
|
||||
bindingEngine = new DataBindingEngine();
|
||||
translationEngine = new TranslationEngine();
|
||||
(translationEngine as any).translations.set('test-template:ko', {
|
||||
shop: { unit: '개', published: '발행', draft: '비공개' },
|
||||
admin: { count_suffix: '건', vendor: '제공: {vendor}', paid: '{amount} 결제' },
|
||||
});
|
||||
});
|
||||
|
||||
const renderItems = (children: any[], item: Record<string, any>) => {
|
||||
const nodes = renderItemChildren(children, item, { Probe }, 'test', {
|
||||
translationContext: { templateId: 'test-template', locale: 'ko' },
|
||||
bindingEngine,
|
||||
translationEngine,
|
||||
actionDispatcher: { createHandler: () => vi.fn() } as any,
|
||||
});
|
||||
return render(<>{nodes}</>);
|
||||
};
|
||||
|
||||
it('문자열 중간의 $t: 토큰이 번역된다 (종전에는 원본 키가 그대로 노출)', () => {
|
||||
const view = renderItems(
|
||||
[{ id: 'p', type: 'basic', name: 'Probe', text: '27$t:admin.count_suffix' }],
|
||||
{ row: {} },
|
||||
);
|
||||
expect(screen.getByTestId('probe').textContent).toBe('27건');
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('$t:defer: 는 종전대로 이 경로에서 번역된다 (반복 서브트리 실사용 회귀선)', () => {
|
||||
const view = renderItems(
|
||||
[{ id: 'p', type: 'basic', name: 'Probe', text: '$t:defer:shop.unit' }],
|
||||
{ row: {} },
|
||||
);
|
||||
expect(screen.getByTestId('probe').textContent).toBe('개');
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
/**
|
||||
* 대표 케이스 회귀선 — 저장소 실사용 `$t:defer:` 32건(고유 24종)은 **전부**
|
||||
* `$t:defer:key|param={{row.x}}` 파라미터 형태다. `$t:defer:` 를 이 경로에 남겨 둔
|
||||
* 설계 근거 자체가 "파라미터의 `row` 는 DynamicRenderer 시점에 존재하지 않는다" 이므로,
|
||||
* 파라미터 없는 형태만 검사하면 순서 회귀를 놓친다 — 파라미터 없는 defer 는 뒤이은
|
||||
* `startsWith('$t:')` 분기로도 우연히 통과하기 때문이다.
|
||||
*/
|
||||
it('$t:defer: 파라미터가 항목 컨텍스트로 해소된다 (실사용 형태 대표 케이스)', () => {
|
||||
const view = renderItems(
|
||||
[
|
||||
{
|
||||
id: 'p',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
text: '$t:defer:admin.vendor|vendor={{row.vendor}}',
|
||||
},
|
||||
],
|
||||
{ row: { vendor: '시르소프트' } },
|
||||
);
|
||||
expect(screen.getByTestId('probe').textContent).toBe('제공: 시르소프트');
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('$t:defer: 파라미터가 문자열 중간에 있어도 항목 값으로 해소된다', () => {
|
||||
const view = renderItems(
|
||||
[
|
||||
{
|
||||
id: 'p',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
text: '합계 $t:defer:admin.paid|amount={{row.total}}',
|
||||
},
|
||||
],
|
||||
{ row: { total: '12,000원' } },
|
||||
);
|
||||
// 중간 `$t:` 처리가 `$t:defer:` 분기보다 앞서면 파라미터가 해소되지 않은 채 번역돼
|
||||
// 값이 사라진다 — 이 단언이 그 순서를 고정한다.
|
||||
expect(screen.getByTestId('probe').textContent).toBe('합계 12,000원 결제');
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('$t:defer: 파라미터는 항목마다 다른 값으로 해소된다', () => {
|
||||
const nodes = renderItemChildren(
|
||||
[
|
||||
{
|
||||
id: 'p',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
text: '$t:defer:admin.vendor|vendor={{row.vendor}}',
|
||||
},
|
||||
],
|
||||
{ row: { vendor: 'A사' } },
|
||||
{ Probe },
|
||||
'first',
|
||||
{
|
||||
translationContext: { templateId: 'test-template', locale: 'ko' },
|
||||
bindingEngine,
|
||||
translationEngine,
|
||||
actionDispatcher: { createHandler: () => vi.fn() } as any,
|
||||
},
|
||||
);
|
||||
const second = renderItemChildren(
|
||||
[
|
||||
{
|
||||
id: 'p',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
text: '$t:defer:admin.vendor|vendor={{row.vendor}}',
|
||||
},
|
||||
],
|
||||
{ row: { vendor: 'B사' } },
|
||||
{ Probe },
|
||||
'second',
|
||||
{
|
||||
translationContext: { templateId: 'test-template', locale: 'ko' },
|
||||
bindingEngine,
|
||||
translationEngine,
|
||||
actionDispatcher: { createHandler: () => vi.fn() } as any,
|
||||
},
|
||||
);
|
||||
|
||||
const first = render(<>{nodes}</>);
|
||||
expect(screen.getByTestId('probe').textContent).toBe('제공: A사');
|
||||
first.unmount();
|
||||
|
||||
const secondView = render(<>{second}</>);
|
||||
expect(screen.getByTestId('probe').textContent).toBe('제공: B사');
|
||||
secondView.unmount();
|
||||
});
|
||||
|
||||
it('JSON 구조 문자열 안의 $t: 는 번역하지 않는다 (데이터의 일부)', () => {
|
||||
const view = renderItems(
|
||||
[
|
||||
{
|
||||
id: 'p',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
props: { value: '{"text":"$t:shop.unit"}' },
|
||||
},
|
||||
],
|
||||
{ row: {} },
|
||||
);
|
||||
expect(JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!)).toBe(
|
||||
'{"text":"$t:shop.unit"}',
|
||||
);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('표현식 결과가 배열이면 리프의 $t: 까지 번역된다', () => {
|
||||
const view = renderItems(
|
||||
[{ id: 'p', type: 'basic', name: 'Probe', props: { value: '{{row.badges}}' } }],
|
||||
{ row: { badges: ['$t:shop.published', '$t:shop.draft'] } },
|
||||
);
|
||||
expect(JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!)).toEqual([
|
||||
'발행',
|
||||
'비공개',
|
||||
]);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('$switch 객체가 값으로 해석된다 (종전에는 객체 그대로 전달)', () => {
|
||||
const view = renderItems(
|
||||
[
|
||||
{
|
||||
id: 'p',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
props: {
|
||||
value: {
|
||||
$switch: '{{row.status}}',
|
||||
$cases: { active: 'text-green-500', inactive: 'text-gray-500' },
|
||||
$default: 'text-gray-400',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
{ row: { status: 'active' } },
|
||||
);
|
||||
expect(JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!)).toBe(
|
||||
'text-green-500',
|
||||
);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('액션 정의 객체는 선평가하지 않는다 (실행 시점 최신 상태 보장)', () => {
|
||||
const view = renderItems(
|
||||
[
|
||||
{
|
||||
id: 'p',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
props: {
|
||||
value: {
|
||||
handler: 'apiCall',
|
||||
target: '/api/x',
|
||||
params: { id: '{{_local.selectedId}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
{ row: {}, _local: { selectedId: 1 } },
|
||||
);
|
||||
const passed = JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!);
|
||||
expect(passed.params.id).toBe('{{_local.selectedId}}');
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
// ── raw 마커 최종 제거 (engine-v1.56.3) ─────────────────────────────
|
||||
//
|
||||
// 마커( )는 번역 패스를 건너뛰게 하려는 내부 표식이다. 단발 렌더 경로는
|
||||
// resolveTranslationsDeep 안에서 벗기지만 이 경로에는 그 패스가 없어, 마커가 붙은 채
|
||||
// React 로 넘어가 Unicode Noncharacter 두 글자가 그대로 DOM 에 실렸다.
|
||||
|
||||
it('raw: 바인딩의 text 에 raw 마커가 남지 않는다', () => {
|
||||
const view = renderItems(
|
||||
[{ id: 'p', type: 'basic', name: 'Probe', text: '{{raw:row.label}}' }],
|
||||
{ row: { label: '$t:shop.unit' } },
|
||||
);
|
||||
const text = screen.getByTestId('probe').textContent!;
|
||||
// raw: 이므로 번역은 되지 않고
|
||||
expect(text).toBe('$t:shop.unit');
|
||||
// 마커는 화면으로 새지 않는다
|
||||
expect(text).not.toContain(RAW_MARKER_START);
|
||||
expect(text).not.toContain(RAW_MARKER_END);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('raw: 바인딩의 props 값에도 raw 마커가 남지 않는다', () => {
|
||||
const view = renderItems(
|
||||
[{ id: 'p', type: 'basic', name: 'Probe', props: { value: '{{raw:row.label}}' } }],
|
||||
{ row: { label: '$t:shop.unit' } },
|
||||
);
|
||||
const passed = JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!);
|
||||
expect(passed).toBe('$t:shop.unit');
|
||||
expect(JSON.stringify(passed)).not.toContain(RAW_MARKER_START);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('raw: 결과가 배열/객체여도 리프까지 마커가 남지 않는다', () => {
|
||||
const view = renderItems(
|
||||
[{ id: 'p', type: 'basic', name: 'Probe', props: { value: '{{raw:row.meta}}' } }],
|
||||
{ row: { meta: { a: '$t:shop.unit', b: ['$t:shop.draft'] } } },
|
||||
);
|
||||
const passed = JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!);
|
||||
expect(passed).toEqual({ a: '$t:shop.unit', b: ['$t:shop.draft'] });
|
||||
expect(JSON.stringify(passed)).not.toContain(RAW_MARKER_START);
|
||||
expect(JSON.stringify(passed)).not.toContain(RAW_MARKER_END);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('중첩 자식 안의 raw: 도 마커 없이 렌더된다', () => {
|
||||
const view = renderItems(
|
||||
[
|
||||
{
|
||||
id: 'outer',
|
||||
type: 'basic',
|
||||
name: 'Probe',
|
||||
children: [{ id: 'inner', type: 'basic', name: 'Probe', text: '{{raw:row.label}}' }],
|
||||
},
|
||||
],
|
||||
{ row: { label: '$t:shop.unit' } },
|
||||
);
|
||||
const all = screen.getAllByTestId('probe');
|
||||
const text = all[all.length - 1].textContent!;
|
||||
expect(text).toBe('$t:shop.unit');
|
||||
expect(text).not.toContain(RAW_MARKER_START);
|
||||
view.unmount();
|
||||
});
|
||||
|
||||
it('일반 바인딩은 종전대로 즉시 해석된다 (회귀 보호)', () => {
|
||||
const view = renderItems(
|
||||
[{ id: 'p', type: 'basic', name: 'Probe', props: { value: '{{row.id}}' } }],
|
||||
{ row: { id: 42 } },
|
||||
);
|
||||
expect(JSON.parse(screen.getByTestId('probe').getAttribute('data-json')!)).toBe(42);
|
||||
view.unmount();
|
||||
});
|
||||
});
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
import type { ComputedRecipeSpec, ConditionParamSpec } from './specTypes';
|
||||
import { dataBindingEngine, type BindingContext } from '../../DataBindingEngine';
|
||||
import { hasPipes } from '../../PipeRegistry';
|
||||
|
||||
// ── 친화 보기(프리셋) — spec 주도 build/match ─────────────────────────
|
||||
|
||||
@@ -598,9 +599,14 @@ export function evaluateComputedPreview(
|
||||
}
|
||||
const inner = stripOuterBraces(exprValue);
|
||||
try {
|
||||
const value = dataBindingEngine.evaluateExpression(inner, sampleContext ?? {}, {
|
||||
skipCache: true,
|
||||
});
|
||||
// 파이프 표현식은 evaluatePipeExpression 으로 평가한다 — 런타임(DynamicRenderer /
|
||||
// ActionDispatcher)의 computed 재계산과 같은 규칙이라야 편집기 미리보기가
|
||||
// 실제 화면과 같은 값을 보여준다. @since engine-v1.54.10
|
||||
const value = hasPipes(inner)
|
||||
? dataBindingEngine.evaluatePipeExpression(inner, sampleContext ?? {}, { skipCache: true })
|
||||
: dataBindingEngine.evaluateExpression(inner, sampleContext ?? {}, {
|
||||
skipCache: true,
|
||||
});
|
||||
return { ok: true, value, type: classifyType(value) };
|
||||
} catch (e) {
|
||||
// 미리보기 평가 실패는 정상 경로(샘플 컨텍스트에 그 데이터가 없을 수 있음) —
|
||||
|
||||
@@ -10,6 +10,17 @@
|
||||
/** raw: 바인딩 접두사 */
|
||||
export const RAW_PREFIX = 'raw:';
|
||||
|
||||
/**
|
||||
* 바인딩 식에서 `raw:` 접두사 제거 (없으면 원본 그대로)
|
||||
*
|
||||
* 접두사를 벗기지 않은 채 식으로 평가하면 `raw:` 의 콜론이 식의 일부로 파싱된다.
|
||||
*
|
||||
* @since engine-v1.54.9
|
||||
*/
|
||||
export function stripRawPrefix(expr: string): string {
|
||||
return expr.startsWith(RAW_PREFIX) ? expr.slice(RAW_PREFIX.length) : expr;
|
||||
}
|
||||
|
||||
/** 번역 면제 시작 마커 (Unicode Noncharacter) */
|
||||
export const RAW_MARKER_START = '\uFDD0';
|
||||
|
||||
@@ -62,3 +73,56 @@ export function wrapRawDeep(value: any): any {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 값의 모든 리프 문자열에서 raw 마커를 재귀적으로 제거 (`wrapRawDeep` 의 역연산)
|
||||
*
|
||||
* 마커는 번역 패스가 raw 값을 건너뛰게 하려고 붙이는 **내부 표식**이므로, 값이 화면
|
||||
* (React props/children)으로 나가기 전에 반드시 벗겨야 한다. 남으면 Unicode Noncharacter
|
||||
* 두 글자가 그대로 DOM 에 실린다.
|
||||
*
|
||||
* 단발 렌더 경로는 `resolveTranslationsDeep` 안에서 벗기지만, 반복 렌더 경로
|
||||
* (`renderItemChildren`)에는 그 패스가 없어 마커가 그대로 나갔다.
|
||||
*
|
||||
* React 엘리먼트는 순회하지 않는다 — 이미 렌더된 자식 트리이고, 그 안의 값은 각자
|
||||
* 자기 경로에서 이미 처리됐다.
|
||||
*
|
||||
* @since engine-v1.56.3
|
||||
*/
|
||||
export function stripRawDeep(value: any): any {
|
||||
if (typeof value === 'string') {
|
||||
if (isRawWrapped(value)) return unwrapRaw(value);
|
||||
// 혼합 보간으로 문자열 중간에 마커가 박힌 경우
|
||||
if (containsRawMarker(value) || value.includes(RAW_MARKER_END)) {
|
||||
return value.split(RAW_MARKER_START).join('').split(RAW_MARKER_END).join('');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
let changed = false;
|
||||
const out = value.map((v) => {
|
||||
const next = stripRawDeep(v);
|
||||
if (next !== v) changed = true;
|
||||
return next;
|
||||
});
|
||||
// 마커가 없으면 원본 참조를 그대로 돌려준다 (반복 렌더에서 매 항목 재할당 방지)
|
||||
return changed ? out : value;
|
||||
}
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
// React 엘리먼트는 이미 렌더된 자식 트리다 — 순회하지 않는다
|
||||
if ((value as any).$$typeof !== undefined) return value;
|
||||
|
||||
let changed = false;
|
||||
const result: Record<string, any> = {};
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
const next = stripRawDeep(v);
|
||||
if (next !== v) changed = true;
|
||||
result[k] = next;
|
||||
}
|
||||
return changed ? result : value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -30,6 +30,11 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- 표와 카드 목록에서 항목별 표시 조건이 서로 다르게 판정되던 문제를 수정했습니다. 같은 조건을 써도 표에서는 항상 보이고 카드에서는 아무것도 보이지 않는 식으로 결과가 갈렸으며, 특히 화면 상태값을 참조하는 조건에서 그랬습니다. 이제 두 목록 모두 다른 화면 요소와 같은 기준으로 조건을 판정합니다.
|
||||
- 표 하단 합계 영역에 넣은 내용이 표 본문과 다른 방식으로 처리되던 문제를 수정했습니다. 합계 영역도 각 항목과 같은 규칙으로 값을 해석합니다.
|
||||
- 표 하단 합계 영역의 값이 계산되지 않고 표현식 원문 그대로 표시되던 문제를 수정했습니다. 주문 상세의 합계 행에서 수량·적립금·배송비 자리에 값 대신 알 수 없는 문자열이 보였습니다.
|
||||
- 표의 셀 안쪽·확장 영역·합계 영역에서 일부 화면 요소가 아무 표시 없이 사라지던 문제를 수정했습니다. 표 바깥에서는 정상 표시되는 요소가 표 안에서만 빠졌으며, 본인인증 이력의 확장 영역에서 부가 정보 내용이 제목만 남고 보이지 않았습니다.
|
||||
- 카드 목록의 카드 내용 항목 이름이 실제 동작과 다르게 안내되던 문제를 수정했습니다(레이아웃 편집기의 속성 목록에 없는 이름이 표시되고 실제로 쓰는 이름은 빠져 있었습니다).
|
||||
- 모듈·플러그인·템플릿 관리 화면에서 '업데이트 확인'을 눌러 업데이트가 발견돼도 항상 "모두 최신 상태입니다"라고 안내되던 문제를 수정했습니다.
|
||||
- 게시판·상품 카테고리 목록의 새로고침 버튼과 언어팩 재활성화 안내에서 번역되지 않은 내부 문구가 그대로 보이던 문제를 수정했습니다.
|
||||
- 모듈·플러그인·템플릿 관리 화면의 목록 요약이 항상 "총 0개 중 1-0개 표시"로 나오고, 목록이 한 화면을 넘어도 다음 쪽으로 넘어갈 수 없던 문제를 수정했습니다.
|
||||
|
||||
@@ -1182,7 +1182,7 @@
|
||||
"type": "composite",
|
||||
"description": "Data grid with sorting, filtering, and pagination",
|
||||
"path": "src/components/composite/DataGrid.tsx",
|
||||
"skipBindingKeys": ["cellChildren", "expandChildren", "expandContext", "render", "subRowChildren", "subRowCondition"],
|
||||
"skipBindingKeys": ["cellChildren", "expandChildren", "expandContext", "render", "subRowChildren", "subRowCondition", "footerCells", "footerCardChildren"],
|
||||
"props": {
|
||||
"columns": {
|
||||
"type": "array",
|
||||
@@ -1235,17 +1235,17 @@
|
||||
"type": "composite",
|
||||
"description": "Card grid layout with automatic skeleton loading and pagination support",
|
||||
"path": "src/components/composite/CardGrid.tsx",
|
||||
"skipBindingKeys": ["cardChildren"],
|
||||
"skipBindingKeys": ["cardColumns"],
|
||||
"props": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"required": false,
|
||||
"description": "표시할 데이터 배열 (undefined일 경우 자동 스켈레톤 표시)"
|
||||
},
|
||||
"cardChildren": {
|
||||
"cardColumns": {
|
||||
"type": "array",
|
||||
"required": true,
|
||||
"description": "카드 내용 정의 배열 (CardGridCellChild[])"
|
||||
"description": "카드 내용 정의 배열 (CardGridColumn[] — 첫 항목의 cellChildren 을 카드 본문으로 사용)"
|
||||
},
|
||||
"columns": {
|
||||
"type": "number",
|
||||
|
||||
@@ -1 +1 @@
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-scroll-snap-strictness:proximity;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:"Pretendard", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-yellow-950:oklch(28.6% .066 53.813);--color-lime-50:oklch(98.6% .031 120.757);--color-lime-100:oklch(96.7% .067 122.328);--color-lime-200:oklch(93.8% .127 124.321);--color-lime-300:oklch(89.7% .196 126.665);--color-lime-400:oklch(84.1% .238 128.85);--color-lime-500:oklch(76.8% .233 130.85);--color-lime-600:oklch(64.8% .2 131.684);--color-lime-700:oklch(53.2% .157 131.589);--color-lime-800:oklch(45.3% .124 130.933);--color-lime-900:oklch(40.5% .101 131.063);--color-lime-950:oklch(27.4% .072 132.109);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .08Line truncated
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-scroll-snap-strictness:proximity;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:"Pretendard", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-yellow-950:oklch(28.6% .066 53.813);--color-lime-50:oklch(98.6% .031 120.757);--color-lime-100:oklch(96.7% .067 122.328);--color-lime-200:oklch(93.8% .127 124.321);--color-lime-300:oklch(89.7% .196 126.665);--color-lime-400:oklch(84.1% .238 128.85);--color-lime-500:oklch(76.8% .233 130.85);--color-lime-600:oklch(64.8% .2 131.684);--color-lime-700:oklch(53.2% .157 131.589);--color-lime-800:oklch(45.3% .124 130.933);--color-lime-900:oklch(40.5% .101 131.063);--color-lime-950:oklch(27.4% .072 132.109);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .08Line truncated
|
||||
@@ -1,19 +1,20 @@
|
||||
var SirsoftAdminBasic=(function(R,h,v,Tn){"use strict";var F5=Object.defineProperty;var z5=(R,h,v)=>h in R?F5(R,h,{enumerable:!0,configurable:!0,writable:!0,value:v}):R[h]=v;var $e=(R,h,v)=>z5(R,typeof h!="symbol"?h+"":h,v);var Aa,fr;function eb(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const n in t)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,r.get?r:{enumerable:!0,get:()=>t[n]})}}return e.default=t,Object.freeze(e)}const F=eb(v),tb=()=>window.G7Core,oe=v.forwardRef(({children:t,variant:e="primary",size:n="md",className:r="",...s},o)=>{const i="button",a=tb()?.style?.mergeClasses?.(i,r)??`${i} ${r}`;return h.jsx("button",{ref:o,className:a,...s,children:t})});oe.displayName="Button";const Md=v.forwardRef(({accept:t,maxSize:e,onChange:n,onError:r,buttonText:s="Browse",placeholder:o="No file selected",className:i="",disabled:a,...l},c)=>{const d=v.useRef(null),u=c||d,[f,g]=v.useState(""),m={},p={};Object.entries(l).forEach(([x,E])=>{x.startsWith("data-editor-")||x==="id"||x==="onMouseMove"||x==="onMouseLeave"?m[x]=E:p[x]=E});const y=v.useCallback(()=>{!a&&u.current&&u.current.click()},[a,u]),b=v.useCallback(x=>{const E=x.target.files?.[0]||null;if(!E){g(""),n?.({target:{value:null,name:""}});return}if(e&&E.size>e*1024*1024){const C=`File size exceeds ${e}MB limit`;r?.(C),u.current&&(u.current.value=""),g("");return}g(E.name),n?.({target:{value:E,name:E.name}})},[e,n,r,u]),_=v.useCallback(x=>{x.stopPropagation(),u.current&&(u.current.value=""),g(""),n?.({target:{value:null,name:""}})},[n,u]);return h.jsxs("div",{className:`flex items-center gap-2 ${i}`,...m,children:[h.jsx("input",{ref:u,type:"file",accept:t,onChange:b,disabled:a,className:"hidden",...p}),h.jsx("button",{type:"button",onClick:y,disabled:a,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed",children:s}),h.jsx("span",{className:"flex-1 text-sm text-gray-600 dark:text-gray-400 truncate",children:f||o}),f&&!a&&h.jsx("button",{type:"button",onClick:_,className:"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200","aria-label":"Clear file",children:h.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})});Md.displayName="FileInput";const nb=t=>t==="checkbox"?"checkbox":t==="radio"?"radio":"input",rb=(t,e)=>{const n=nb(t);return e?e.split(/\s+/).filter(Boolean).includes(n)?e:`${n} ${e}`:n},kt=v.forwardRef(({label:t,error:e,className:n="",onChange:r,onKeyPress:s,value:o,defaultValue:i,type:a="text",checked:l,defaultChecked:c,...d},u)=>{const f=a==="radio"||a==="checkbox",g=rb(a,n),m=v.useRef(!1),p=v.useRef(null);v.useImperativeHandle(u,()=>p.current);const[y,b]=v.useState(o??i??"");v.useEffect(()=>{if(!f&&!m.current&&o!==void 0){if(p.current&&document.activeElement===p.current)return;b(o)}},[o,f]);const _=v.useCallback(()=>{m.current=!0},[]),x=v.useCallback(M=>{if(m.current=!1,r){const T=M.target.value;b(T);const P=new Event("change",{bubbles:!0});Object.defineProperty(P,"target",{writable:!1,value:{value:T}}),Object.defineProperty(P,"currentTarget",{writable:!1,value:{value:T}}),r(P)}},[r]),E=v.useCallback(M=>{const T=M.target.value;b(T),!m.current&&r&&r(M)},[r]),C=v.useCallback(M=>{m.current||s&&s(M)},[s]),{loadingactions:k,formdata:N,...S}=d;if(f)return h.jsx("input",{ref:p,type:a,className:g,onChange:r,checked:l,defaultChecked:c,value:o,...S});const w=o!==void 0?{value:y}:{defaultValue:i};return h.jsx("input",{ref:p,type:a,className:g,onChange:E,onKeyPress:C,onCompositionStart:_,onCompositionEnd:x,...w,...S})});kt.displayName="Input";const Qn=({label:t,error:e,className:n="",onChange:r,value:s,defaultValue:o,...i})=>{const a=v.useRef(!1),l=v.useRef(null),[c,d]=v.useState(s??o??"");v.useEffect(()=>{!a.current&&s!==void 0&&d(s)},[s]);const u=v.useCallback(()=>{a.current=!0},[]),f=v.useCallback(E=>{if(a.current=!1,r){const C=E.target.value;d(C);const k=new Event("change",{bubbles:!0});Object.defineProperty(k,"target",{writable:!1,value:{value:C}}),Object.defineProperty(k,"currentTarget",{writable:!1,value:{value:C}}),r(k)}},[r]),g=v.useCallback(E=>{const C=E.target.value;d(C),!a.current&&r&&r(E)},[r]),{loadingactions:m,formdata:p,...y}=i,b=s!==void 0?{value:c}:{defaultValue:o},x=(n??"").split(/\s+/).filter(Boolean).includes("textarea")?n??"":["textarea",n].filter(Boolean).join(" ");return h.jsx("textarea",{ref:l,className:x,onChange:g,onCompositionStart:u,onCompositionEnd:f,...b,...y})},sn=({children:t,className:e="",...n})=>h.jsx("label",{className:e,...n,children:t}),I=v.forwardRef(({children:t,className:e="",...n},r)=>h.jsx("div",{ref:r,classNameLine truncated
|
||||
var SirsoftAdminBasic=(function(R,h,v,$n){"use strict";var sA=Object.defineProperty;var oA=(R,h,v)=>h in R?sA(R,h,{enumerable:!0,configurable:!0,writable:!0,value:v}):R[h]=v;var Pe=(R,h,v)=>oA(R,typeof h!="symbol"?h+"":h,v);var Va,wr;function fb(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:()=>e[n]})}}return t.default=e,Object.freeze(t)}const F=fb(v),hb=()=>window.G7Core,ae=v.forwardRef(({children:e,variant:t="primary",size:n="md",className:r="",...s},o)=>{const i="button",a=hb()?.style?.mergeClasses?.(i,r)??`${i} ${r}`;return h.jsx("button",{ref:o,className:a,...s,children:e})});ae.displayName="Button";const Pd=v.forwardRef(({accept:e,maxSize:t,onChange:n,onError:r,buttonText:s="Browse",placeholder:o="No file selected",className:i="",disabled:a,...l},c)=>{const d=v.useRef(null),u=c||d,[f,g]=v.useState(""),m={},p={};Object.entries(l).forEach(([x,M])=>{x.startsWith("data-editor-")||x==="id"||x==="onMouseMove"||x==="onMouseLeave"?m[x]=M:p[x]=M});const y=v.useCallback(()=>{!a&&u.current&&u.current.click()},[a,u]),b=v.useCallback(x=>{const M=x.target.files?.[0]||null;if(!M){g(""),n?.({target:{value:null,name:""}});return}if(t&&M.size>t*1024*1024){const C=`File size exceeds ${t}MB limit`;r?.(C),u.current&&(u.current.value=""),g("");return}g(M.name),n?.({target:{value:M,name:M.name}})},[t,n,r,u]),_=v.useCallback(x=>{x.stopPropagation(),u.current&&(u.current.value=""),g(""),n?.({target:{value:null,name:""}})},[n,u]);return h.jsxs("div",{className:`flex items-center gap-2 ${i}`,...m,children:[h.jsx("input",{ref:u,type:"file",accept:e,onChange:b,disabled:a,className:"hidden",...p}),h.jsx("button",{type:"button",onClick:y,disabled:a,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed",children:s}),h.jsx("span",{className:"flex-1 text-sm text-gray-600 dark:text-gray-400 truncate",children:f||o}),f&&!a&&h.jsx("button",{type:"button",onClick:_,className:"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200","aria-label":"Clear file",children:h.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})});Pd.displayName="FileInput";const gb=e=>e==="checkbox"?"checkbox":e==="radio"?"radio":"input",pb=(e,t)=>{const n=gb(e);return t?t.split(/\s+/).filter(Boolean).includes(n)?t:`${n} ${t}`:n},St=v.forwardRef(({label:e,error:t,className:n="",onChange:r,onKeyPress:s,value:o,defaultValue:i,type:a="text",checked:l,defaultChecked:c,...d},u)=>{const f=a==="radio"||a==="checkbox",g=pb(a,n),m=v.useRef(!1),p=v.useRef(null);v.useImperativeHandle(u,()=>p.current);const[y,b]=v.useState(o??i??"");v.useEffect(()=>{if(!f&&!m.current&&o!==void 0){if(p.current&&document.activeElement===p.current)return;b(o)}},[o,f]);const _=v.useCallback(()=>{m.current=!0},[]),x=v.useCallback(E=>{if(m.current=!1,r){const D=E.target.value;b(D);const P=new Event("change",{bubbles:!0});Object.defineProperty(P,"target",{writable:!1,value:{value:D}}),Object.defineProperty(P,"currentTarget",{writable:!1,value:{value:D}}),r(P)}},[r]),M=v.useCallback(E=>{const D=E.target.value;b(D),!m.current&&r&&r(E)},[r]),C=v.useCallback(E=>{m.current||s&&s(E)},[s]),{loadingactions:k,formdata:N,...S}=d;if(f)return h.jsx("input",{ref:p,type:a,className:g,onChange:r,checked:l,defaultChecked:c,value:o,...S});const w=o!==void 0?{value:y}:{defaultValue:i};return h.jsx("input",{ref:p,type:a,className:g,onChange:M,onKeyPress:C,onCompositionStart:_,onCompositionEnd:x,...w,...S})});St.displayName="Input";const ir=({label:e,error:t,className:n="",onChange:r,value:s,defaultValue:o,...i})=>{const a=v.useRef(!1),l=v.useRef(null),[c,d]=v.useState(s??o??"");v.useEffect(()=>{!a.current&&s!==void 0&&d(s)},[s]);const u=v.useCallback(()=>{a.current=!0},[]),f=v.useCallback(M=>{if(a.current=!1,r){const C=M.target.value;d(C);const k=new Event("change",{bubbles:!0});Object.defineProperty(k,"target",{writable:!1,value:{value:C}}),Object.defineProperty(k,"currentTarget",{writable:!1,value:{value:C}}),r(k)}},[r]),g=v.useCallback(M=>{const C=M.target.value;d(C),!a.current&&r&&r(M)},[r]),{loadingactions:m,formdata:p,...y}=i,b=s!==void 0?{value:c}:{defaultValue:o},x=(n??"").split(/\s+/).filter(Boolean).includes("textarea")?n??"":["textarea",n].filter(Boolean).join(" ");return h.jsx("textarea",{ref:l,className:x,onChange:g,onCompositionStart:u,onCompositionEnd:f,...b,...y})},ln=({children:e,className:t="",...n})=>h.jsx("label",{className:t,...n,children:e}),I=v.forwardRef(({children:e,className:t="",...n},r)=>h.jsx("div",{ref:r,classNameLine truncated
|
||||
inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium
|
||||
${he?"bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300"}
|
||||
`,children:[h.jsx(re,{name:"globe",className:"w-3 h-3"}),H(X),he&&h.jsx(Q,{className:"text-red-500 dark:text-red-400",children:"*"}),!he&&h.jsx(oe,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",onClick:()=>z(X),disabled:l,children:h.jsx(re,{name:"xmark",className:"w-3 h-3"})})]}),he&&Z&&W()]}),n==="textarea"?h.jsx(Qn,{name:`${f}[${X}]`,value:C[X]||"",onChange:le=>D(X,le.target.value),placeholder:J,required:a&&he,disabled:l,maxLength:c,rows:d,className:`${ne} resize-vertical`}):h.jsx(kt,{type:"text",name:`${f}[${X}]`,value:C[X]||"",onChange:le=>D(X,le.target.value),placeholder:J,required:a&&he,disabled:l,maxLength:c,className:ee})]},X)},[f,C,D,i,a,l,c,d,n,H,x,z,W,b]),B=v.useCallback(X=>{const Z=b?"input input-error":"input",ee=b?"textarea input-error":"textarea",ne=`${i}${i?" (":""}${H(X)}${i?")":""}`;return h.jsx(I,{children:n==="textarea"?h.jsx(Qn,{name:`${f}[${X}]`,value:C[X]||"",onChange:he=>D(X,he.target.value),placeholder:ne,required:a&&X===x,disabled:l,maxLength:c,rows:d,className:`${ee} resize-vertical`}):h.jsx(kt,{type:"text",name:`${f}[${X}]`,value:C[X]||"",onChange:he=>D(X,he.target.value),placeholder:ne,required:a&&X===x,disabled:l,maxLength:c,className:Z})},X)},[f,C,D,i,a,l,c,d,n,x,H,b]),G=()=>h.jsx(I,{className:u,children:h.jsx(I,{className:"space-y-4",children:w.map((X,Z)=>U(X,Z===0))})}),V=()=>h.jsxs(I,{className:u,children:[h.jsxs(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:[w.map(X=>h.jsxs(oe,{type:"button",className:`
|
||||
${ue?"bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300"}
|
||||
`,children:[h.jsx(oe,{name:"globe",className:"w-3 h-3"}),H(K),ue&&h.jsx(Q,{className:"text-red-500 dark:text-red-400",children:"*"}),!ue&&h.jsx(ae,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",onClick:()=>z(K),disabled:l,children:h.jsx(oe,{name:"xmark",className:"w-3 h-3"})})]}),ue&&q&&G()]}),n==="textarea"?h.jsx(ir,{name:`${f}[${K}]`,value:C[K]||"",onChange:ce=>O(K,ce.target.value),placeholder:J,required:a&&ue,disabled:l,maxLength:c,rows:d,className:`${ee} resize-vertical`}):h.jsx(St,{type:"text",name:`${f}[${K}]`,value:C[K]||"",onChange:ce=>O(K,ce.target.value),placeholder:J,required:a&&ue,disabled:l,maxLength:c,className:ne})]},K)},[f,C,O,i,a,l,c,d,n,H,x,z,G,b]),W=v.useCallback(K=>{const q=b?"input input-error":"input",ne=b?"textarea input-error":"textarea",ee=`${i}${i?" (":""}${H(K)}${i?")":""}`;return h.jsx(I,{children:n==="textarea"?h.jsx(ir,{name:`${f}[${K}]`,value:C[K]||"",onChange:ue=>O(K,ue.target.value),placeholder:ee,required:a&&K===x,disabled:l,maxLength:c,rows:d,className:`${ne} resize-vertical`}):h.jsx(St,{type:"text",name:`${f}[${K}]`,value:C[K]||"",onChange:ue=>O(K,ue.target.value),placeholder:ee,required:a&&K===x,disabled:l,maxLength:c,className:q})},K)},[f,C,O,i,a,l,c,d,n,x,H,b]),Y=()=>h.jsx(I,{className:u,children:h.jsx(I,{className:"space-y-4",children:w.map((K,q)=>U(K,q===0))})}),V=()=>h.jsxs(I,{className:u,children:[h.jsxs(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:[w.map(K=>h.jsxs(ae,{type:"button",className:`
|
||||
inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold transition-all
|
||||
${T===X?X===x?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":X===x?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"}
|
||||
`,onClick:()=>P(X),children:[h.jsx(re,{name:"globe",className:"w-3 h-3"}),g?h.jsxs(h.Fragment,{children:[h.jsx(Q,{className:"hidden md:inline",children:H(X)}),h.jsx(Q,{className:"inline md:hidden",children:X.toUpperCase()})]}):H(X),X===x&&h.jsx(Q,{className:"text-red-500 dark:text-red-400",children:"*"}),j(X)&&T!==X&&h.jsx(re,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"}),X!==x&&h.jsx(oe,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-300 dark:hover:bg-gray-500 transition-colors",onClick:Z=>{Z.stopPropagation(),z(X)},disabled:l,children:h.jsx(re,{name:"xmark",className:"w-3 h-3"})})]},X)),W()]}),w.map(X=>h.jsx(I,{className:T===X?"block":"hidden",children:B(X)},X))]}),K=()=>{const X=b?"input input-error":"input",Z=b?"textarea input-error":"textarea";return h.jsx(I,{className:u,children:h.jsxs(I,{className:"flex items-center gap-1",children:[h.jsx(I,{className:"flex items-center shrink-0",children:w.map(ee=>h.jsxs(oe,{type:"button",className:`
|
||||
${D===K?K===x?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":K===x?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"}
|
||||
`,onClick:()=>P(K),children:[h.jsx(oe,{name:"globe",className:"w-3 h-3"}),g?h.jsxs(h.Fragment,{children:[h.jsx(Q,{className:"hidden md:inline",children:H(K)}),h.jsx(Q,{className:"inline md:hidden",children:K.toUpperCase()})]}):H(K),K===x&&h.jsx(Q,{className:"text-red-500 dark:text-red-400",children:"*"}),j(K)&&D!==K&&h.jsx(oe,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"}),K!==x&&h.jsx(ae,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-300 dark:hover:bg-gray-500 transition-colors",onClick:q=>{q.stopPropagation(),z(K)},disabled:l,children:h.jsx(oe,{name:"xmark",className:"w-3 h-3"})})]},K)),G()]}),w.map(K=>h.jsx(I,{className:D===K?"block":"hidden",children:W(K)},K))]}),X=()=>{const K=b?"input input-error":"input",q=b?"textarea input-error":"textarea";return h.jsx(I,{className:u,children:h.jsxs(I,{className:"flex items-center gap-1",children:[h.jsx(I,{className:"flex items-center shrink-0",children:w.map(ne=>h.jsxs(ae,{type:"button",className:`
|
||||
px-2 py-1.5 text-xs font-medium border-y border-l first:rounded-l last:rounded-r last:border-r transition-colors
|
||||
${T===ee?"bg-blue-500 text-white border-blue-500 dark:bg-blue-600 dark:border-blue-600":"bg-white text-gray-600 border-gray-200 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700"}
|
||||
`,onClick:()=>P(ee),title:H(ee),children:[ee.toUpperCase(),ee===x&&h.jsx(Q,{className:"text-red-400 ml-0.5",children:"*"})]},ee))}),h.jsx(I,{className:"flex-1 min-w-0",children:n==="textarea"?h.jsx(Qn,{name:`${f}[${T}]`,value:C[T]||"",onChange:ee=>D(T,ee.target.value),placeholder:i,required:a&&T===x,disabled:l,maxLength:c,rows:d,className:Z}):h.jsx(kt,{type:"text",name:`${f}[${T}]`,value:C[T]||"",onChange:ee=>D(T,ee.target.value),placeholder:i,required:a&&T===x,disabled:l,maxLength:c,className:X})})]})})};return h.jsxs(I,{className:l?"opacity-50 cursor-not-allowed":void 0,id:p,...y,children:[r==="compact"?K():r==="tabs"?V():G(),b&&m&&h.jsx(Q,{className:"text-xs text-red-500 dark:text-red-400 mt-1 block",children:m})]})};Ha.displayName="MultilingualInput";const vb=()=>window.G7Core,Ua=({checked:t,value:e,onChange:n,disabled:r=!1,label:s,description:o,size:i="md",className:a="",name:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(()=>t??e??!1);v.useEffect(()=>{f(t??e??!1)},[t,e]);const g=_=>{const x=_.target.checked;f(x),n&&!r&&n(_)},p={sm:{track:"w-9 h-5",thumb:"after:h-4 after:w-4"},md:{track:"w-11 h-6",thumb:"after:h-5 after:w-5"},lg:{track:"w-14 h-7",thumb:"after:h-6 after:w-6"}}[i],y=()=>{if(!r){const _=!u;if(f(_),n){const x=vb();x?.createChangeEvent&&n(x.createChangeEvent({checked:_,name:l}))}}},b=_=>{r||_.key!==" "&&_.key!=="Enter"&&_.key!=="Spacebar"||(_.preventDefault(),y())};return h.jsxs("div",{className:`toggle-container ${a}`,id:c,...d,children:[h.jsxs("div",{className:`toggle-switch-wrapper ${r?"cursor-not-allowed opacity-50":"cursor-pointer"}`,role:"switch","aria-checked":u,"aria-disabled":r||void 0,"aria-label":s??l,tabIndex:r?-1:0,onKeyDown:b,onClick:_=>{_.preventDefault(),_.stopPropagation(),y()},children:[h.jsx("input",{type:"checkbox",id:l,name:l,checked:u,onChange:g,disabled:r,className:"sr-only peer",tabIndex:-1}),h.jsx("div",{className:`toggle-switch-track ${p.track} ${p.thumb}`})]}),s&&h.jsx("span",{className:`toggle-label ${r?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:y,children:s}),o&&h.jsx("p",{className:"toggle-description",children:o})]})},Fo=({status:t,label:e,showIcon:n=!0,iconName:r,className:s="",style:o,id:i,editorAttrs:a})=>{const l={success:"bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-400 border-green-200 dark:border-green-800",warning:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800",error:"bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-400 border-red-200 dark:border-red-800",info:"bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 border-blue-200 dark:border-blue-800",pending:"bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-200 dark:border-gray-600",default:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-600"},c={success:ce.CheckCircle,warning:ce.ExclamationTriangle,error:ce.XCircle,info:ce.InfoCircle,pending:ce.Clock,default:ce.QuestionCircle},d={success:"성공",warning:"경고",error:"오류",info:"정보",pending:"대기 중",default:"기본"},u=r||c[t],f=e||d[t],g=l[t];return h.jsxs(Q,{className:`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium border ${g} ${s}`,style:o,id:i,...a,children:[n&&h.jsx(re,{name:u,className:"w-4 h-4"}),h.jsx(Q,{children:f})]})},Id={blue:"bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300",green:"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300",red:"bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300",gray:"bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300",yellow:"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300",purple:"bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300",orange:"bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300",teal:"bg-teal-100 text-teal-800 dark:bg-teal-900/30 dark:text-teal-300",cyan:"bg-cyan-100 text-cyan-800 dark:bg-cyan-900/30 dark:text-cyan-300"},xb={success:"green",warning:"yellow",danger:"red",error:"red",info:"blue",primary:"blue",secondary:"gray",text:"gray",default:"gray"},Ld={sm:"px-1.5 py-0.5 text-[10px]",md:"px-2 py-0.5 text-xs",lg:"px-2.5 py-1 text-sm"},Pd=({color:t,variant:e,text:n,size:r="md",className:s="",style:o,children:i,id:a,editorAttrs:l})=>{const c=t||(e?xb[e]:void 0)||"gray",d=Id[c]||Id.gray,u=Ld[r]||Ld.md;return h.jsxs(Q,{className:`inline-flex items-center rounded-full font-medium ${u} ${d} ${s}`,style:o,id:a,...l,children:[n,i]})};const{entries:$d,setPrototypeOf:Fd,isFrozen:wb,getPrototypeOf:kb,getOwnPropertyDescriptor:Cb}=Object;let{freeze:Gt,seal:cn,create:zd}=Object,{apply:Ba,construct:ja}=typeof Reflect<"u"&&Reflect;Gt||(Gt=function(e){return e}),cn||(cn=function(e){return e}),Ba||(Ba=function(e,n){for(var r=arguments.length,s=new Array(r>2?r-2:0),o=2;o<r;o++)s[o-2]=arguments[o];return e.apply(n,s)}),ja||(ja=function(e){for(var n=
Line truncated
|
||||
`+de),J&&zo([P,L,$],Ee=>{de=xs(de,Ee," ")}),E&&we?E.createHTML(de):de},e.setConfig=function(){let ke=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};rn(ke),ae=!0},e.clearConfig=function(){qe=null,ae=!1},e.isValidAttribute=function(ke,Y,ie){qe||rn({});const xe=De(ke),ht=De(Y);return Do(xe,ht,ie)},e.addHook=function(ke,Y){typeof Y=="function"&&vs(T[ke],Y)},e.removeHook=function(ke,Y){if(Y!==void 0){const ie=Sb(T[ke],Y);return ie===-1?void 0:_b(T[ke],ie,1)[0]}return Vd(T[ke])},e.removeHooks=function(ke){T[ke]=[]},e.removeAllHooks=function(){T=Xd()},e}var Bb=Kd();const Za=({content:t,text:e,isHtml:n=!0,className:r="",purifyConfig:s,id:o,editorAttrs:i})=>{const a=e??t??"";if(!a||a.trim()==="")return null;if(!n){const u=`
|
||||
${D===ne?"bg-blue-500 text-white border-blue-500 dark:bg-blue-600 dark:border-blue-600":"bg-white text-gray-600 border-gray-200 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700"}
|
||||
`,onClick:()=>P(ne),title:H(ne),children:[ne.toUpperCase(),ne===x&&h.jsx(Q,{className:"text-red-400 ml-0.5",children:"*"})]},ne))}),h.jsx(I,{className:"flex-1 min-w-0",children:n==="textarea"?h.jsx(ir,{name:`${f}[${D}]`,value:C[D]||"",onChange:ne=>O(D,ne.target.value),placeholder:i,required:a&&D===x,disabled:l,maxLength:c,rows:d,className:q}):h.jsx(St,{type:"text",name:`${f}[${D}]`,value:C[D]||"",onChange:ne=>O(D,ne.target.value),placeholder:i,required:a&&D===x,disabled:l,maxLength:c,className:K})})]})})};return h.jsxs(I,{className:l?"opacity-50 cursor-not-allowed":void 0,id:p,...y,children:[r==="compact"?X():r==="tabs"?V():Y(),b&&m&&h.jsx(Q,{className:"text-xs text-red-500 dark:text-red-400 mt-1 block",children:m})]})};Za.displayName="MultilingualInput";const Ab=()=>window.G7Core,Qa=({checked:e,value:t,onChange:n,disabled:r=!1,label:s,description:o,size:i="md",className:a="",name:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(()=>e??t??!1);v.useEffect(()=>{f(e??t??!1)},[e,t]);const g=_=>{const x=_.target.checked;f(x),n&&!r&&n(_)},p={sm:{track:"w-9 h-5",thumb:"after:h-4 after:w-4"},md:{track:"w-11 h-6",thumb:"after:h-5 after:w-5"},lg:{track:"w-14 h-7",thumb:"after:h-6 after:w-6"}}[i],y=()=>{if(!r){const _=!u;if(f(_),n){const x=Ab();x?.createChangeEvent&&n(x.createChangeEvent({checked:_,name:l}))}}},b=_=>{r||_.key!==" "&&_.key!=="Enter"&&_.key!=="Spacebar"||(_.preventDefault(),y())};return h.jsxs("div",{className:`toggle-container ${a}`,id:c,...d,children:[h.jsxs("div",{className:`toggle-switch-wrapper ${r?"cursor-not-allowed opacity-50":"cursor-pointer"}`,role:"switch","aria-checked":u,"aria-disabled":r||void 0,"aria-label":s??l,tabIndex:r?-1:0,onKeyDown:b,onClick:_=>{_.preventDefault(),_.stopPropagation(),y()},children:[h.jsx("input",{type:"checkbox",id:l,name:l,checked:u,onChange:g,disabled:r,className:"sr-only peer",tabIndex:-1}),h.jsx("div",{className:`toggle-switch-track ${p.track} ${p.thumb}`})]}),s&&h.jsx("span",{className:`toggle-label ${r?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:y,children:s}),o&&h.jsx("p",{className:"toggle-description",children:o})]})},Ko=({status:e,label:t,showIcon:n=!0,iconName:r,className:s="",style:o,id:i,editorAttrs:a})=>{const l={success:"bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-400 border-green-200 dark:border-green-800",warning:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800",error:"bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-400 border-red-200 dark:border-red-800",info:"bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 border-blue-200 dark:border-blue-800",pending:"bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-200 dark:border-gray-600",default:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-600"},c={success:de.CheckCircle,warning:de.ExclamationTriangle,error:de.XCircle,info:de.InfoCircle,pending:de.Clock,default:de.QuestionCircle},d={success:"성공",warning:"경고",error:"오류",info:"정보",pending:"대기 중",default:"기본"},u=r||c[e],f=t||d[e],g=l[e];return h.jsxs(Q,{className:`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium border ${g} ${s}`,style:o,id:i,...a,children:[n&&h.jsx(oe,{name:u,className:"w-4 h-4"}),h.jsx(Q,{children:f})]})},Ud={blue:"bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300",green:"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300",red:"bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300",gray:"bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300",yellow:"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300",purple:"bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300",orange:"bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300",teal:"bg-teal-100 text-teal-800 dark:bg-teal-900/30 dark:text-teal-300",cyan:"bg-cyan-100 text-cyan-800 dark:bg-cyan-900/30 dark:text-cyan-300"},Db={success:"green",warning:"yellow",danger:"red",error:"red",info:"blue",primary:"blue",secondary:"gray",text:"gray",default:"gray"},Bd={sm:"px-1.5 py-0.5 text-[10px]",md:"px-2 py-0.5 text-xs",lg:"px-2.5 py-1 text-sm"},jd=({color:e,variant:t,text:n,size:r="md",className:s="",style:o,children:i,id:a,editorAttrs:l})=>{const c=e||(t?Db[t]:void 0)||"gray",d=Ud[c]||Ud.gray,u=Bd[r]||Bd.md;return h.jsxs(Q,{className:`inline-flex items-center rounded-full font-medium ${u} ${d} ${s}`,style:o,id:a,...l,children:[n,i]})};function Wd(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function Ib(e){if(Array.isArray(e))return e}function Lb(e,t){var n=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(n!=null){var r,s,o,i,a=[],l=!0,c=!1;try{if(o=(n=n.call(e)).next,t!==0)for(;!(l=(r=o.call(n)).done)&&(a.push(r.value),a.length!==t);l=!0);}catch(d){c=!0,s=d}finally{try{if
Line truncated
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function $b(e,t){return Ib(e)||Lb(e,t)||Fb(e,t)||Pb()}function Fb(e,t){if(e){if(typeof e=="string")return Wd(e,t);var n={}.toString.call(e).slice(8,-1);return n==="Object"&&e.constructor&&(n=e.constructor.name),n==="Map"||n==="Set"?Array.from(e):n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Wd(e,t):void 0}}const Gd=Object.entries,Yd=Object.setPrototypeOf,zb=Object.isFrozen,Vb=Object.getPrototypeOf,Hb=Object.getOwnPropertyDescriptor;let Zt=Object.freeze,hn=Object.seal,Zr=Object.create,Xd=typeof Reflect<"u"&&Reflect,Ja=Xd.apply,Ra=Xd.construct;Zt||(Zt=function(t){return t}),hn||(hn=function(t){return t}),Ja||(Ja=function(t,n){for(var r=arguments.length,s=new Array(r>2?r-2:0),o=2;o<r;o++)s[o-2]=arguments[o];return t.apply(n,s)}),Ra||(Ra=function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),s=1;s<n;s++)r[s-1]=arguments[s];return new t(...r)});const kr=_t(Array.prototype.forEach),Ub=_t(Array.prototype.lastIndexOf),Kd=_t(Array.prototype.pop),Qr=_t(Array.prototype.push),Bb=_t(Array.prototype.splice),Qt=Array.isArray,$s=_t(String.prototype.toLowerCase),el=_t(String.prototype.toString),qd=_t(String.prototype.match),Jr=_t(String.prototype.replace),Zd=_t(String.prototype.indexOf),jb=_t(String.prototype.trim),Wb=_t(Number.prototype.toString),Gb=_t(Boolean.prototype.toString),Qd=typeof BigInt>"u"?null:_t(BigInt.prototype.toString),Jd=typeof Symbol>"u"?null:_t(Symbol.prototype.toString),xt=_t(Object.prototype.hasOwnProperty),Fs=_t(Object.prototype.toString),zt=_t(RegExp.prototype.test),Rr=Yb(TypeError);function _t(e){return function(t){t instanceof RegExp&&(t.lastIndex=0);for(var n=arguments.length,r=new Array(n>1?n-1:0),s=1;s<n;s++)r[s-1]=arguments[s];return Ja(e,t,r)}}function Yb(e){return function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return Ra(e,n)}}function Ve(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:$s;if(Yd&&Yd(e,null),!Qt(t))return e;let r=t.length;for(;r--;){let s=t[r];if(typeof s=="string"){const o=n(s);o!==s&&(zb(t)||(t[r]=o),s=o)}e[s]=!0}return e}function Xb(e){for(let t=0;t<e.length;t++)xt(e,t)||(e[t]=null);return e}function Bt(e){const t=Zr(null);for(const r of Gd(e)){var n=$b(r,2);const s=n[0],o=n[1];xt(e,s)&&(Qt(o)?t[s]=Xb(o):o&&typeof o=="object"&&o.constructor===Object?t[s]=Bt(o):t[s]=o)}return t}function Kb(e){switch(typeof e){case"string":return e;case"number":return Wb(e);case"boolean":return Gb(e);case"bigint":return Qd?Qd(e):"0";case"symbol":return Jd?Jd(e):"Symbol()";case"undefined":return Fs(e);case"function":case"object":{if(e===null)return Fs(e);const t=e,n=Fn(t,"toString");if(typeof n=="function"){const r=n(t);return typeof r=="string"?r:Fs(r)}return Fs(e)}default:return Fs(e)}}function Fn(e,t){for(;e!==null;){const r=Hb(e,t);if(r){if(r.get)return _t(r.get);if(typeof r.value=="function")return _t(r.value)}e=Vb(e)}function n(){return null}return n}function qb(e){try{return zt(e,""),!0}catch{return!1}}const Rd=Zt(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","search","section","select","shadow","slot","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),tl=Zt(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","enterkeyhint","exportparts","filter","font","g","glyph","glyphref","hkern","image","inputmode","line","lineargradient","marker","mask","metadata","mpath","part","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),nl=Zt(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),Zb=Zt(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","s
Line truncated
|
||||
`+At),pe&&kr([T,z,H],Sn=>{At=Jr(At,Sn," ")}),k&&Xe?w(At):At},t.setConfig=function(){let se=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};kn(se),ie=!0},t.clearConfig=function(){Ct=null,ie=!1},t.isValidAttribute=function(se,B,te){Ct||kn({});const he=Ue(se),ot=Ue(B);return Ua(he,ot,te)},t.addHook=function(se,B){typeof B=="function"&&Qr(O[se],B)},t.removeHook=function(se,B){if(B!==void 0){const te=Ub(O[se],B);return te===-1?void 0:Bb(O[se],te,1)[0]}return Kd(O[se])},t.removeHooks=function(se){O[se]=[]},t.removeAllHooks=function(){O=su()},t}var cy=ou();const ol=({content:e,text:t,isHtml:n=!0,className:r="",purifyConfig:s,id:o,editorAttrs:i})=>{const a=t??e??"";if(!a||a.trim()==="")return null;if(!n){const u=`
|
||||
whitespace-pre-wrap
|
||||
text-gray-900 dark:text-gray-100
|
||||
font-sans
|
||||
${r}
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(I,{className:u,id:o,...i,children:a})}const l={FORBID_TAGS:["script","noscript","iframe","frame","frameset","object","embed","applet","portal","form","input","textarea","select","button","style","link","meta","base","body","head","html","title","svg","math","audio","video","source","track","canvas","details","dialog","plaintext","xmp","listing","marquee","noframes","noembed","template","slot"],FORBID_ATTR:["onerror","onload","onclick","onmouseover","onfocus","onblur","ontoggle","oncanplay","onloadstart","formaction","xlink:href","action"],ALLOW_DATA_ATTR:!0,ADD_ATTR:["target"]},c=v.useMemo(()=>{const u=s?{...l,...s,FORBID_TAGS:[...l.FORBID_TAGS,...s.FORBID_TAGS||[]],FORBID_ATTR:[...l.FORBID_ATTR,...s.FORBID_ATTR||[]]}:l;return Bb.sanitize(a,u).replace(/<a\s+([^>]*?)href=["']([^"']+)["']([^>]*?)>/gi,(g,m,p,y)=>(p.startsWith("http://")||p.startsWith("https://"))&&!g.includes("rel=")?`<a ${m}href="${p}"${y} rel="noopener noreferrer">`:g)},[a,s]),d=`
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(I,{className:u,id:o,...i,children:a})}const l={FORBID_TAGS:["script","noscript","iframe","frame","frameset","object","embed","applet","portal","form","input","textarea","select","button","style","link","meta","base","body","head","html","title","svg","math","audio","video","source","track","canvas","details","dialog","plaintext","xmp","listing","marquee","noframes","noembed","template","slot"],FORBID_ATTR:["onerror","onload","onclick","onmouseover","onfocus","onblur","ontoggle","oncanplay","onloadstart","formaction","xlink:href","action"],ALLOW_DATA_ATTR:!0,ADD_ATTR:["target"]},c=v.useMemo(()=>{const u=s?{...l,...s,FORBID_TAGS:[...l.FORBID_TAGS,...s.FORBID_TAGS||[]],FORBID_ATTR:[...l.FORBID_ATTR,...s.FORBID_ATTR||[]]}:l;return cy.sanitize(a,u).replace(/<a\s+([^>]*?)href=["']([^"']+)["']([^>]*?)>/gi,(g,m,p,y)=>(p.startsWith("http://")||p.startsWith("https://"))&&!g.includes("rel=")?`<a ${m}href="${p}"${y} rel="noopener noreferrer">`:g)},[a,s]),d=`
|
||||
prose dark:prose-invert max-w-none
|
||||
prose-p:my-2
|
||||
prose-headings:font-bold prose-headings:mt-6 prose-headings:mb-4
|
||||
@@ -35,67 +36,67 @@ var SirsoftAdminBasic=(function(R,h,v,Tn){"use strict";var F5=Object.definePrope
|
||||
[&_tr:hover]:bg-gray-50 dark:[&_tr:hover]:bg-gray-700/50
|
||||
after:content-[''] after:block after:clear-both
|
||||
${r}
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(I,{className:d,dangerouslySetInnerHTML:{__html:c},id:o,...i})},Uo=window.G7Core?.createLogger?.("Comp:DataGrid")??{log:(...t)=>console.log("[Comp:DataGrid]",...t),warn:(...t)=>console.warn("[Comp:DataGrid]",...t),error:(...t)=>console.error("[Comp:DataGrid]",...t)},Ss=(t,e)=>window.G7Core?.t?.(t,e)??t,Qa={Div:I,Span:Q,Button:oe,Icon:re,Checkbox:vn,P:yt,A:Lr,Label:sn,Input:kt,H1:Da,H2:Jn,H3:Ir,H4:Nd,Img:yn,Select:Pr,Textarea:Qn,Table:$a,Thead:Fa,Tbody:za,Tfoot:Va,Tr:Rn,Th:$r,Td:Zt,MultilingualInput:Ha,Toggle:Ua,StatusBadge:Fo,Badge:Pd,HtmlContent:Za,ActionMenu:ys},Ja=t=>{if(t==null)return"";if(typeof t=="string"||typeof t=="number"||typeof t=="boolean")return String(t);if(Array.isArray(t))return t.map(e=>Ja(e)).join(", ");if(typeof t=="object"){if(v.isValidElement(t))return t;if(Object.keys(t).length===0)return"";try{return JSON.stringify(t)}catch{return Uo.warn("객체를 문자열로 변환할 수 없습니다:",t),"[Object]"}}return String(t)},qd=(t,e)=>{if(!t)return!0;const n=t.match(/^\{\{(.+)\}\}$/);if(!n)return!0;const r=n[1].trim();try{return!!new Function("row",`return ${r}`)(e)}catch(s){return Uo.warn("subRow 조건 평가 실패:",t,s),!0}},_s=(t,e,n,r="",s)=>{const o=window.G7Core;if(o?.renderItemChildren){const i={row:e,value:n,$value:n};return o.renderItemChildren(t,i,Qa,r,s?{componentContext:s}:void 0)}return Uo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},Ra=(t,e,n="",r)=>{const s=window.G7Core;if(s?.renderItemChildren){const o={row:e,$value:e};return s.renderItemChildren(t,o,Qa,n,r?{componentContext:r}:void 0)}return Uo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},jb=({id:t,columns:e,data:n,sortable:r=!0,pagination:s=!1,pageSize:o=10,onRowClick:i,className:a="",style:l,sortField:c,sortDirection:d,onSortChange:u,selectable:f=!1,selectedIds:g=[],onSelectionChange:m,idField:p="id",showColumnSelector:y=!1,visibleColumns:b,onColumnVisibilityChange:_,responsiveBreakpoint:x=768,cardRenderer:E,rowActions:C,onRowAction:k,alwaysShowPagination:N=!1,serverSidePagination:S=!1,serverCurrentPage:w,serverTotalPages:M,onPageChange:T,prevText:P,nextText:L,columnSelectorText:$,emptyMessage:A,actionsColumnHeader:D,requiredText:O,selectedCountText:z,showFirstLast:H=!0,expandable:j=!1,expandedRowIds:W,onExpandChange:U,expandChildren:B,expandedRowRender:G,expandColumnWidth:V="48px",expandIconExpanded:K=ce.ChevronDown,expandIconCollapsed:X=ce.ChevronRight,expandConditionField:Z,dynamicColumns:ee,dynamicColumnsInsertAfter:ne,expandContext:he,__componentContext:J,subRowChildren:le,subRowCondition:ue,subRowClassName:ae="px-6 py-2 bg-gray-50 dark:bg-gray-900",subRowRender:ve,footerCells:fe,footerClassName:ye="bg-gray-50 dark:bg-gray-800/50 font-medium",footerCardChildren:we,editorAttrs:He})=>{const se=P||void 0,Oe=L||void 0,Le=$??Ss("common.column_selector"),Ie=A??Ss("common.no_data"),Be=D??Ss("common.actions"),st=O??Ss("common.required"),ut=z??Ss("common.selected_count"),je=window.G7Core,nt=je?.useControllableState,Ye=je?.shallowArrayEqual,[te,ge]=nt?nt(c,null):[c??null,()=>{}],[me,pe]=nt?nt(d,"asc"):[d??"asc",()=>{}],[be,Ue]=v.useState(1),[Ce,ze]=nt?nt(W,[],U,{isEqual:Ye}):[W??[],()=>{}],[Se,Re]=v.useState(!1),ot=v.useRef(null),Pe=je?.useResponsive,Te=Pe?.(),Ve=Te?Te.width<x:typeof window<"u"&&window.innerWidth<x,De=v.useMemo(()=>{if(!ee||!Array.isArray(ee)||ee.length===0)return e;if(ne){const q=e.findIndex(de=>de.field===ne);if(q!==-1)return[...e.slice(0,q+1),...ee,...e.slice(q+1)]}return[...e,...ee]},[e,ee,ne]),[qe,it]=v.useState(()=>b&&b.length>0?b:e.filter(q=>!q.hidden).map(q=>q.field));v.useEffect(()=>{b&&b.length>0&&it(b)},[b]);const pt=v.useRef("");v.useEffect(()=>{const q=e.map(de=>de.field).join(",");pt.current&&q!==pt.current&&(!b||b.length===0)&&it(e.filter(de=>!de.hidden).map(de=>de.field)),pt.current=q},[e,b]);const rn=v.useRef([]),bt=v.useRef(null);v.useEffect(()=>{if(ee&&ee.length>0){const q=ee.map(Ze=>Ze.field);if(bt.current===null&&(bt.current=b!==void 0&&b.length>0,bt.current)){rn.current=q;return}const de=rn.current,Ee=q.filter(Ze=>!de.includes(Ze));if(Ee.length>0){const Ze=Ee.filter(Fe=>!qe.includes(Fe));if(Ze.length>0){const Fe=[...qe,...Ze];it(Fe),_?.(Fe)}}rn.current=q}},[ee]),v.useEffect(()=>{S||Ue(1)},[n,S]),v.useEffect(()=>{const q=de=>{ot.current&&!ot.current.contains(de.target)&&Re(!1)};return Se&&document.addEventListener("mousedown",q),()=>document.removeEventListener("mousedown",q)},[Se]);const wt=v.useMemo(()=>!y&&(!b||b.length===0)?De.filter(q=>!q.hidden):De.filter(q=>qe.includes(q.field)),[De,qe,y,b]),jt=r&&!(S&&!u),ft=v.useMemo(()=>!te||!r||!n?n||[]:u||S?n:[...n].sort((q,de)=>{const Ee=q[te],Ze=de[te];if(Ee===Ze)return 0;const Fe=Ee>Ze?1:-1;return me==="asc"?Fe:-Fe}),[n,te,me,r,u,S]),Xe=v.useMemo(()=>{if(S||!s)return ft;const q=(be-1)*o,de=q+o;return ft.slice(q,de)},[ft,be,o,s,S]),Nn=S?w||1:be,Wt=S?M||1:Math.ceil((ft?.length||0)/o),bn=v.useCallback(q=>{S&&T?T(q):Ue(q)},[S,T]),hr=q=>{if(!j
Line truncated
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(I,{className:d,dangerouslySetInnerHTML:{__html:c},id:o,...i})},Zo=window.G7Core?.createLogger?.("Comp:DataGrid")??{log:(...e)=>console.log("[Comp:DataGrid]",...e),warn:(...e)=>console.warn("[Comp:DataGrid]",...e),error:(...e)=>console.error("[Comp:DataGrid]",...e)},zs=(e,t)=>window.G7Core?.t?.(e,t)??e,dy={Div:I,Span:Q,Button:ae,Icon:oe,Checkbox:En,P:vt,A:Yr,Label:ln,Input:St,H1:Ba,H2:ar,H3:Gr,H4:$d,Img:_n,Select:Xr,Textarea:ir,Table:Ya,Thead:Xa,Tbody:Ka,Tfoot:qa,Tr:lr,Th:Kr,Td:en,MultilingualInput:Za,Toggle:Qa,StatusBadge:Ko,Badge:jd,HtmlContent:ol,ActionMenu:Ps},il=()=>{const e=window.G7Core?.getComponentMap?.();return e&&Object.keys(e).length>0?e:dy},al=e=>{if(e==null)return"";if(typeof e=="string"||typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e))return e.map(t=>al(t)).join(", ");if(typeof e=="object"){if(v.isValidElement(e))return e;if(Object.keys(e).length===0)return"";try{return JSON.stringify(e)}catch{return Zo.warn("객체를 문자열로 변환할 수 없습니다:",e),"[Object]"}}return String(e)},iu=(e,t)=>{if(!e)return!0;const n=window.G7Core;return typeof n?.evaluateCondition=="function"?n.evaluateCondition(e,{row:t,item:t,$item:t}):(Zo.warn("G7Core.evaluateCondition 미노출 — subRow 조건을 평가할 수 없습니다:",e),!0)},es=(e,t,n,r="",s)=>{const o=window.G7Core;if(o?.renderItemChildren){const i={row:t,value:n,$value:n};return o.renderItemChildren(e,i,il(),r,s?{componentContext:s}:void 0)}return Zo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},au=(e,t,n)=>e?!e.includes("{{")&&!e.includes("$t:")?e:es([{type:"basic",name:"Span",text:e}],{},null,t,n):"",ll=(e,t,n="",r)=>{const s=window.G7Core;if(s?.renderItemChildren){const o={row:t,$value:t};return s.renderItemChildren(e,o,il(),n,r?{componentContext:r}:void 0)}return Zo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},uy=({id:e,columns:t,data:n,sortable:r=!0,pagination:s=!1,pageSize:o=10,onRowClick:i,className:a="",style:l,sortField:c,sortDirection:d,onSortChange:u,selectable:f=!1,selectedIds:g=[],onSelectionChange:m,idField:p="id",showColumnSelector:y=!1,visibleColumns:b,onColumnVisibilityChange:_,responsiveBreakpoint:x=768,cardRenderer:M,rowActions:C,onRowAction:k,alwaysShowPagination:N=!1,serverSidePagination:S=!1,serverCurrentPage:w,serverTotalPages:E,onPageChange:D,prevText:P,nextText:L,columnSelectorText:$,emptyMessage:A,actionsColumnHeader:O,requiredText:T,selectedCountText:z,showFirstLast:H=!0,expandable:j=!1,expandedRowIds:G,onExpandChange:U,expandChildren:W,expandedRowRender:Y,expandColumnWidth:V="48px",expandIconExpanded:X=de.ChevronDown,expandIconCollapsed:K=de.ChevronRight,expandConditionField:q,dynamicColumns:ne,dynamicColumnsInsertAfter:ee,expandContext:ue,__componentContext:J,subRowChildren:ce,subRowCondition:me,subRowClassName:le="px-6 py-2 bg-gray-50 dark:bg-gray-900",subRowRender:ve,footerCells:ge,footerClassName:pe="bg-gray-50 dark:bg-gray-800/50 font-medium",footerCardChildren:ke,editorAttrs:$e})=>{const ie=P||void 0,Me=L||void 0,Ae=$??zs("common.column_selector"),Ie=A??zs("common.no_data"),Xe=O??zs("common.actions"),gt=T??zs("common.required"),pt=z??zs("common.selected_count"),Be=window.G7Core,et=Be?.useControllableState,je=Be?.shallowArrayEqual,[re,fe]=et?et(c,null):[c??null,()=>{}],[ye,we]=et?et(d,"asc"):[d??"asc",()=>{}],[xe,He]=v.useState(1),[_e,Le]=et?et(G,[],U,{isEqual:je}):[G??[],()=>{}],[Ce,We]=v.useState(!1),Ze=v.useRef(null),Fe=Be?.useResponsive,Ne=Fe?.(),ze=Ne?Ne.width<x:typeof window<"u"&&window.innerWidth<x,Ke=v.useMemo(()=>{if(!ne||!Array.isArray(ne)||ne.length===0)return t;if(ee){const Z=t.findIndex(be=>be.field===ee);if(Z!==-1)return[...t.slice(0,Z+1),...ne,...t.slice(Z+1)]}return[...t,...ne]},[t,ne,ee]),[tt,at]=v.useState(()=>b&&b.length>0?b:t.filter(Z=>!Z.hidden).map(Z=>Z.field));v.useEffect(()=>{b&&b.length>0&&at(b)},[b]);const ct=v.useRef("");v.useEffect(()=>{const Z=t.map(be=>be.field).join(",");ct.current&&Z!==ct.current&&(!b||b.length===0)&&at(t.filter(be=>!be.hidden).map(be=>be.field)),ct.current=Z},[t,b]);const wn=v.useRef([]),yt=v.useRef(null);v.useEffect(()=>{if(ne&&ne.length>0){const Z=ne.map(Qe=>Qe.field);if(yt.current===null&&(yt.current=b!==void 0&&b.length>0,yt.current)){wn.current=Z;return}const be=wn.current,De=Z.filter(Qe=>!be.includes(Qe));if(De.length>0){const Qe=De.filter(se=>!tt.includes(se));if(Qe.length>0){const se=[...tt,...Qe];at(se),_?.(se)}}wn.current=Z}},[ne]),v.useEffect(()=>{S||He(1)},[n,S]),v.useEffect(()=>{const Z=be=>{Ze.current&&!Ze.current.contains(be.target)&&We(!1)};return Ce&&document.addEventListener("mousedown",Z),()=>document.removeEventListener("mousedown",Z)},[Ce]);const Ue=v.useMemo(()=>!y&&(!b||b.length===0)?Ke.filter(Z=>!Z.hidden):Ke.filter(Z=>tt.includes(Z.field)),[Ke,tt,y,b]),Ct=r&&!(S&&!u),Ut=v.useMemo(()=>!re||!r||!n?n||[]:u||S?n:[...n].sort((Z,be)=>{const De=Z[re],Qe=be[re];if(De===Qe)return 0;const se=De>Qe?1:-1;return ye==="
Line truncated
|
||||
flex items-center gap-3 p-4 rounded-lg border shadow-lg
|
||||
min-w-[300px] max-w-md
|
||||
transition-all duration-300
|
||||
${s.bg}
|
||||
${n?"opacity-100 translate-y-0":`opacity-0 ${i}`}
|
||||
`,role:"alert",children:[h.jsx(re,{name:o,className:`w-5 h-5 flex-shrink-0 ${s.text}`}),h.jsx(Q,{className:`flex-1 text-sm font-medium ${s.text}`,children:t.message}),h.jsx(oe,{onClick:r,className:`flex-shrink-0 p-1 rounded hover:bg-black/10 dark:hover:bg-white/10 ${s.text} focus:outline-none`,"aria-label":fy("common.close_notification"),children:h.jsx(re,{name:ce.Times,className:"w-4 h-4"})})]})},ou=({items:t,separator:e=h.jsx(re,{name:ce.ChevronRight,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),showHome:n=!1,homeHref:r="/",maxItems:s,className:o="",id:i,editorAttrs:a})=>{let c=n?[{label:"Home",href:r},...t]:t,d=c,u=!1;if(s&&c.length>s){u=!0;const f=c.slice(0,1),g=c.slice(-(s-2));d=[...f,...g]}return h.jsx(Po,{className:`flex items-center space-x-2 text-sm ${o}`,"aria-label":"Breadcrumb",id:i,...a,children:h.jsx(I,{className:"flex items-center space-x-2",children:d.map((f,g)=>{const m=g===d.length-1,p=g===0;return h.jsxs(v.Fragment,{children:[u&&p&&h.jsxs(h.Fragment,{children:[h.jsx(iu,{item:f,isLast:!1}),h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:e}),h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:"..."}),d.length>1&&h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:e})]}),(!u||!p)&&h.jsxs(h.Fragment,{children:[h.jsx(iu,{item:f,isLast:m}),!m&&h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:e})]})]},g)})})})},iu=({item:t,isLast:e})=>e||!t.href&&!t.onClick?h.jsx(Q,{className:"font-medium text-gray-900 dark:text-white","aria-current":e?"page":void 0,children:t.label}):t.onClick?h.jsx(Lr,{href:"#",onClick:n=>{n.preventDefault(),t.onClick?.()},className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:t.label}):h.jsx(Lr,{href:t.href,className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:t.label}),by=({title:t,description:e,iconName:n,illustrationSrc:r,illustrationAlt:s="Empty state illustration",actions:o=[],className:i="",id:a,editorAttrs:l})=>{const c={primary:"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",secondary:"bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600 focus:ring-gray-500",danger:"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",ghost:"bg-transparent text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:ring-gray-500"};return h.jsxs(I,{className:`flex flex-col items-center justify-center p-8 text-center ${i}`,role:"status","aria-live":"polite",id:a,...l,children:[r?h.jsx(yn,{src:r,alt:s,className:"w-48 h-48 mb-6 object-contain"}):n?h.jsx(I,{className:"mb-6 p-4 rounded-full bg-gray-100 dark:bg-gray-700",children:h.jsx(re,{name:n,className:"w-12 h-12 text-gray-400 dark:text-gray-500"})}):null,h.jsx(Jn,{className:"text-xl font-semibold text-gray-900 dark:text-white mb-2",children:t}),e&&h.jsx(yt,{className:"text-gray-600 dark:text-gray-400 mb-6 max-w-md",children:e}),o.length>0&&h.jsx(I,{className:"flex flex-wrap gap-3 justify-center",children:o.map((d,u)=>h.jsxs(oe,{onClick:d.onClick,className:`
|
||||
`,role:"alert",children:[h.jsx(oe,{name:o,className:`w-5 h-5 flex-shrink-0 ${s.text}`}),h.jsx(Q,{className:`flex-1 text-sm font-medium ${s.text}`,children:e.message}),h.jsx(ae,{onClick:r,className:`flex-shrink-0 p-1 rounded hover:bg-black/10 dark:hover:bg-white/10 ${s.text} focus:outline-none`,"aria-label":Py("common.close_notification"),children:h.jsx(oe,{name:de.Times,className:"w-4 h-4"})})]})},bu=({items:e,separator:t=h.jsx(oe,{name:de.ChevronRight,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),showHome:n=!1,homeHref:r="/",maxItems:s,className:o="",id:i,editorAttrs:a})=>{let c=n?[{label:"Home",href:r},...e]:e,d=c,u=!1;if(s&&c.length>s){u=!0;const f=c.slice(0,1),g=c.slice(-(s-2));d=[...f,...g]}return h.jsx(Yo,{className:`flex items-center space-x-2 text-sm ${o}`,"aria-label":"Breadcrumb",id:i,...a,children:h.jsx(I,{className:"flex items-center space-x-2",children:d.map((f,g)=>{const m=g===d.length-1,p=g===0;return h.jsxs(v.Fragment,{children:[u&&p&&h.jsxs(h.Fragment,{children:[h.jsx(yu,{item:f,isLast:!1}),h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:t}),h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:"..."}),d.length>1&&h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:t})]}),(!u||!p)&&h.jsxs(h.Fragment,{children:[h.jsx(yu,{item:f,isLast:m}),!m&&h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:t})]})]},g)})})})},yu=({item:e,isLast:t})=>t||!e.href&&!e.onClick?h.jsx(Q,{className:"font-medium text-gray-900 dark:text-white","aria-current":t?"page":void 0,children:e.label}):e.onClick?h.jsx(Yr,{href:"#",onClick:n=>{n.preventDefault(),e.onClick?.()},className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:e.label}):h.jsx(Yr,{href:e.href,className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:e.label}),Hy=({title:e,description:t,iconName:n,illustrationSrc:r,illustrationAlt:s="Empty state illustration",actions:o=[],className:i="",id:a,editorAttrs:l})=>{const c={primary:"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",secondary:"bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600 focus:ring-gray-500",danger:"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",ghost:"bg-transparent text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:ring-gray-500"};return h.jsxs(I,{className:`flex flex-col items-center justify-center p-8 text-center ${i}`,role:"status","aria-live":"polite",id:a,...l,children:[r?h.jsx(_n,{src:r,alt:s,className:"w-48 h-48 mb-6 object-contain"}):n?h.jsx(I,{className:"mb-6 p-4 rounded-full bg-gray-100 dark:bg-gray-700",children:h.jsx(oe,{name:n,className:"w-12 h-12 text-gray-400 dark:text-gray-500"})}):null,h.jsx(ar,{className:"text-xl font-semibold text-gray-900 dark:text-white mb-2",children:e}),t&&h.jsx(vt,{className:"text-gray-600 dark:text-gray-400 mb-6 max-w-md",children:t}),o.length>0&&h.jsx(I,{className:"flex flex-wrap gap-3 justify-center",children:o.map((d,u)=>h.jsxs(ae,{onClick:d.onClick,className:`
|
||||
px-4 py-2 rounded-md font-medium transition-colors
|
||||
focus:outline-none focus:ring-2 focus:ring-offset-2
|
||||
${c[d.variant||"primary"]}
|
||||
`,children:[d.iconName&&h.jsx(re,{name:d.iconName,className:"w-4 h-4 mr-2 inline-block"}),d.label]},u))})]})},yy=(t,e)=>window.G7Core?.t?.(t,e)??t,vy=({id:t,size:e="md",color:n="text-blue-600",fullscreen:r=!1,text:s,className:o="",editorAttrs:i})=>{const a={sm:"w-4 h-4 border-2",md:"w-8 h-8 border-2",lg:"w-12 h-12 border-3",xl:"w-16 h-16 border-4"},l={sm:"text-xs",md:"text-sm",lg:"text-base",xl:"text-lg"},c=h.jsxs(I,{className:`flex flex-col items-center justify-center gap-3 ${o}`,children:[h.jsx(I,{className:`
|
||||
${a[e]}
|
||||
`,children:[d.iconName&&h.jsx(oe,{name:d.iconName,className:"w-4 h-4 mr-2 inline-block"}),d.label]},u))})]})},Uy=(e,t)=>window.G7Core?.t?.(e,t)??e,By=({id:e,size:t="md",color:n="text-blue-600",fullscreen:r=!1,text:s,className:o="",editorAttrs:i})=>{const a={sm:"w-4 h-4 border-2",md:"w-8 h-8 border-2",lg:"w-12 h-12 border-3",xl:"w-16 h-16 border-4"},l={sm:"text-xs",md:"text-sm",lg:"text-base",xl:"text-lg"},c=h.jsxs(I,{className:`flex flex-col items-center justify-center gap-3 ${o}`,children:[h.jsx(I,{className:`
|
||||
${a[t]}
|
||||
border-gray-200 dark:border-gray-700 border-t-current
|
||||
rounded-full animate-spin
|
||||
${n}
|
||||
`,role:"status","aria-label":yy("common.loading")}),s&&h.jsx(Q,{className:`${l[e]} ${n} font-medium`,children:s})]});return r?h.jsx(I,{id:t,className:"fixed inset-0 z-50 flex items-center justify-center bg-white dark:bg-gray-900 bg-opacity-90 dark:bg-opacity-90","aria-live":"polite","aria-busy":"true",...i,children:c}):h.jsx(I,{id:t,className:"flex items-center justify-center p-4","aria-live":"polite","aria-busy":"true",...i,children:c})},zr=()=>window.G7Core,ol=(t,e)=>window.G7Core?.t?.(t,e)??t,xy=({logo:t,logoAlt:e="Logo",menu:n=[],collapsed:r=!1,onToggleCollapse:s,className:o="",currentLocale:i,id:a,editorAttrs:l})=>{const[c,d]=v.useState(new Set),[u,f]=v.useState(i||zr()?.locale?.current?.()||"ko"),[g,m]=v.useState(window.location.pathname),p=v.useMemo(()=>{const w=[],M=T=>{for(const P of T)P.url&&w.push(P.url),P.children&&M(P.children)};return M(n),w},[n]),y=v.useCallback(w=>{let M=null;for(const T of p)(w===T||w.startsWith(T+"/"))&&(!M||T.length>M.length)&&(M=T);return M},[p]);v.useEffect(()=>{const w=zr();return w?.state?.subscribe?w.state.subscribe(T=>{const P=T?._global?.locale||w?.locale?.current?.()||"ko";f(P);const L=T?._global?.currentPath||window.location.pathname;m(L)}):void 0},[]),v.useEffect(()=>{const w=()=>{m(window.location.pathname)};return window.addEventListener("popstate",w),()=>window.removeEventListener("popstate",w)},[]),v.useEffect(()=>{i&&f(i)},[i]);const b=(w,M)=>!w||typeof M!="string"?!1:M===w?!0:M.startsWith(w+"/")?y(M)===w:!1,_=(w,M)=>{const T=new Set,P=L=>{for(const $ of L)if($.children&&$.children.length>0){if(P($.children)||b($.url,M))return T.add($.id),!0}else if(b($.url,M))return!0;return!1};return P(w),T};v.useEffect(()=>{if(n.length>0&&g){const w=_(n,g);w.size>0&&d(M=>new Set([...M,...w]))}},[g,n]);const x=w=>b(w,g),E=w=>w.children?w.children.some(M=>x(M.url)||E(M)):!1,C=w=>{d(M=>{const T=new Set(M);return T.has(w)?T.delete(w):T.add(w),T})},k=w=>typeof w=="string"?w:w[u]||w.en||Object.values(w)[0]||"",N=(w,M)=>{zr()?.dispatch?(w.preventDefault(),zr().navigation?.onComplete(()=>{zr().dispatch({handler:"setState",params:{target:"global",sidebarOpen:!1}})}),zr().dispatch({handler:"navigate",params:{path:M}})):window.location.href=M},S=(w,M=0,T=!1)=>{const P=w.children&&w.children.length>0,L=c.has(w.id),$=k(w.name),A=M>0,D=x(w.url),O=E(w),z=A?"admin-sidebar-subitem":"admin-sidebar-item",H="admin-sidebar-item-active",j="admin-sidebar-item-active-parent",W="admin-sidebar-item-default";return h.jsxs(I,{className:A?"relative group":"",children:[A&&h.jsxs(h.Fragment,{children:[!T&&h.jsx(I,{className:"admin-sidebar-tree-line-vertical"}),h.jsx(I,{className:"admin-sidebar-tree-line-curve"})]}),P&&w.url?h.jsxs(I,{className:`
|
||||
`,role:"status","aria-label":Uy("common.loading")}),s&&h.jsx(Q,{className:`${l[t]} ${n} font-medium`,children:s})]});return r?h.jsx(I,{id:e,className:"fixed inset-0 z-50 flex items-center justify-center bg-white dark:bg-gray-900 bg-opacity-90 dark:bg-opacity-90","aria-live":"polite","aria-busy":"true",...i,children:c}):h.jsx(I,{id:e,className:"flex items-center justify-center p-4","aria-live":"polite","aria-busy":"true",...i,children:c})},ts=()=>window.G7Core,hl=(e,t)=>window.G7Core?.t?.(e,t)??e,jy=({logo:e,logoAlt:t="Logo",menu:n=[],collapsed:r=!1,onToggleCollapse:s,className:o="",currentLocale:i,id:a,editorAttrs:l})=>{const[c,d]=v.useState(new Set),[u,f]=v.useState(i||ts()?.locale?.current?.()||"ko"),[g,m]=v.useState(window.location.pathname),p=v.useMemo(()=>{const w=[],E=D=>{for(const P of D)P.url&&w.push(P.url),P.children&&E(P.children)};return E(n),w},[n]),y=v.useCallback(w=>{let E=null;for(const D of p)(w===D||w.startsWith(D+"/"))&&(!E||D.length>E.length)&&(E=D);return E},[p]);v.useEffect(()=>{const w=ts();return w?.state?.subscribe?w.state.subscribe(D=>{const P=D?._global?.locale||w?.locale?.current?.()||"ko";f(P);const L=D?._global?.currentPath||window.location.pathname;m(L)}):void 0},[]),v.useEffect(()=>{const w=()=>{m(window.location.pathname)};return window.addEventListener("popstate",w),()=>window.removeEventListener("popstate",w)},[]),v.useEffect(()=>{i&&f(i)},[i]);const b=(w,E)=>!w||typeof E!="string"?!1:E===w?!0:E.startsWith(w+"/")?y(E)===w:!1,_=(w,E)=>{const D=new Set,P=L=>{for(const $ of L)if($.children&&$.children.length>0){if(P($.children)||b($.url,E))return D.add($.id),!0}else if(b($.url,E))return!0;return!1};return P(w),D};v.useEffect(()=>{if(n.length>0&&g){const w=_(n,g);w.size>0&&d(E=>new Set([...E,...w]))}},[g,n]);const x=w=>b(w,g),M=w=>w.children?w.children.some(E=>x(E.url)||M(E)):!1,C=w=>{d(E=>{const D=new Set(E);return D.has(w)?D.delete(w):D.add(w),D})},k=w=>typeof w=="string"?w:w[u]||w.en||Object.values(w)[0]||"",N=(w,E)=>{ts()?.dispatch?(w.preventDefault(),ts().navigation?.onComplete(()=>{ts().dispatch({handler:"setState",params:{target:"global",sidebarOpen:!1}})}),ts().dispatch({handler:"navigate",params:{path:E}})):window.location.href=E},S=(w,E=0,D=!1)=>{const P=w.children&&w.children.length>0,L=c.has(w.id),$=k(w.name),A=E>0,O=x(w.url),T=M(w),z=A?"admin-sidebar-subitem":"admin-sidebar-item",H="admin-sidebar-item-active",j="admin-sidebar-item-active-parent",G="admin-sidebar-item-default";return h.jsxs(I,{className:A?"relative group":"",children:[A&&h.jsxs(h.Fragment,{children:[!D&&h.jsx(I,{className:"admin-sidebar-tree-line-vertical"}),h.jsx(I,{className:"admin-sidebar-tree-line-curve"})]}),P&&w.url?h.jsxs(I,{className:`
|
||||
${z}
|
||||
${D?H:O?j:W}
|
||||
${O?H:T?j:G}
|
||||
${r?"justify-center":""}
|
||||
`,children:[h.jsxs(oe,{onClick:B=>N(B,w.url),className:"flex items-center justify-start flex-1 min-w-0",children:[w.icon&&h.jsx(mt,{className:`${w.icon} ${r?"":"mr-3"} w-5 h-5 flex items-center justify-center`}),!r&&h.jsx(Q,{className:"truncate",children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(oe,{onClick:()=>C(w.id),className:"p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600","aria-label":ol(L?"common.collapse":"common.expand"),children:h.jsx(mt,{className:`fas fa-chevron-down w-4 h-4 transition-transform ${L?"rotate-180":""}`})})]})]}):P?h.jsxs(oe,{onClick:()=>C(w.id),className:`
|
||||
`,children:[h.jsxs(ae,{onClick:W=>N(W,w.url),className:"flex items-center justify-start flex-1 min-w-0",children:[w.icon&&h.jsx(bt,{className:`${w.icon} ${r?"":"mr-3"} w-5 h-5 flex items-center justify-center`}),!r&&h.jsx(Q,{className:"truncate",children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(bt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(ae,{onClick:()=>C(w.id),className:"p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600","aria-label":hl(L?"common.collapse":"common.expand"),children:h.jsx(bt,{className:`fas fa-chevron-down w-4 h-4 transition-transform ${L?"rotate-180":""}`})})]})]}):P?h.jsxs(ae,{onClick:()=>C(w.id),className:`
|
||||
${z}
|
||||
${O?j:W}
|
||||
${T?j:G}
|
||||
${r?"justify-center":""}
|
||||
`,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(mt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(mt,{className:`fas fa-chevron-right w-4 h-4 text-slate-400 transition-transform duration-200 ${L?"rotate-90":""}`})]})]}):w.url?h.jsxs(oe,{onClick:B=>N(B,w.url),className:`
|
||||
`,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(bt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(bt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(bt,{className:`fas fa-chevron-right w-4 h-4 text-slate-400 transition-transform duration-200 ${L?"rotate-90":""}`})]})]}):w.url?h.jsxs(ae,{onClick:W=>N(W,w.url),className:`
|
||||
${z}
|
||||
${D?H:W}
|
||||
${O?H:G}
|
||||
${r?"justify-center":""}
|
||||
text-left
|
||||
`,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(mt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}):h.jsxs(oe,{className:`
|
||||
`,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(bt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&w.module_id&&h.jsx(bt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}):h.jsxs(ae,{className:`
|
||||
${z}
|
||||
admin-sidebar-item-disabled
|
||||
${r?"justify-center":""}
|
||||
text-left
|
||||
`,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(mt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}),P&&!r&&L&&h.jsx(I,{className:"relative ml-2",children:w.children.map((B,G)=>S(B,M+1,G===w.children.length-1))})]},w.id)};return h.jsxs(I,{className:o,id:a,...l,children:[(t||s)&&h.jsxs(I,{className:"admin-sidebar-header",children:[t&&!r&&h.jsx(yn,{src:t,alt:e,className:"h-8"}),s&&h.jsx(oe,{onClick:s,className:"admin-sidebar-collapse-btn","aria-label":ol(r?"common.expand_sidebar":"common.collapse_sidebar"),children:h.jsx(mt,{className:`${r?"fas fa-chevron-right":"fas fa-chevron-left"} w-5 h-5 text-slate-600 dark:text-slate-400`})})]}),h.jsx(I,{className:"admin-sidebar-menu-list",children:n.map((w,M)=>S(w,0,M===n.length-1))})]})},Es=(t,e)=>window.G7Core?.t?.(t,e)??t,wy=({user:t={name:"",email:""},notifications:e=[],onNotificationClick:n,onProfileClick:r,onLogoutClick:s,className:o=""})=>{const[i,a]=v.useState(!1),[l,c]=v.useState(!1),d=v.useRef(null),u=v.useRef(null),f=e.filter(g=>!g.read).length;return v.useEffect(()=>{const g=m=>{d.current&&!d.current.contains(m.target)&&a(!1),u.current&&!u.current.contains(m.target)&&c(!1)};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[]),h.jsxs(I,{className:`
|
||||
`,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(bt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&w.module_id&&h.jsx(bt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}),P&&!r&&L&&h.jsx(I,{className:"relative ml-2",children:w.children.map((W,Y)=>S(W,E+1,Y===w.children.length-1))})]},w.id)};return h.jsxs(I,{className:o,id:a,...l,children:[(e||s)&&h.jsxs(I,{className:"admin-sidebar-header",children:[e&&!r&&h.jsx(_n,{src:e,alt:t,className:"h-8"}),s&&h.jsx(ae,{onClick:s,className:"admin-sidebar-collapse-btn","aria-label":hl(r?"common.expand_sidebar":"common.collapse_sidebar"),children:h.jsx(bt,{className:`${r?"fas fa-chevron-right":"fas fa-chevron-left"} w-5 h-5 text-slate-600 dark:text-slate-400`})})]}),h.jsx(I,{className:"admin-sidebar-menu-list",children:n.map((w,E)=>S(w,0,E===n.length-1))})]})},Vs=(e,t)=>window.G7Core?.t?.(e,t)??e,Wy=({user:e={name:"",email:""},notifications:t=[],onNotificationClick:n,onProfileClick:r,onLogoutClick:s,className:o=""})=>{const[i,a]=v.useState(!1),[l,c]=v.useState(!1),d=v.useRef(null),u=v.useRef(null),f=t.filter(g=>!g.read).length;return v.useEffect(()=>{const g=m=>{d.current&&!d.current.contains(m.target)&&a(!1),u.current&&!u.current.contains(m.target)&&c(!1)};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[]),h.jsxs(I,{className:`
|
||||
bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-4 flex items-center justify-end gap-4
|
||||
${o}
|
||||
`,children:[h.jsxs(I,{ref:d,className:"relative",children:[h.jsxs(oe,{onClick:()=>a(!i),className:"relative p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors","aria-label":Es("common.notifications"),children:[h.jsx(re,{name:ce.Bell,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),f>0&&h.jsx(Q,{className:"absolute top-0 right-0 flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 rounded-full",children:f>9?"9+":f})]}),i&&h.jsxs(I,{className:"absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsx(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx(Q,{className:"font-semibold text-gray-900 dark:text-white",children:Es("common.notifications")})}),h.jsx(I,{className:"max-h-96 overflow-y-auto",children:e.length===0?h.jsx(I,{className:"px-4 py-8 text-center text-gray-500 dark:text-gray-400",children:h.jsx(Q,{children:Es("common.no_notifications")})}):e.map(g=>h.jsx(oe,{onClick:()=>{g.onClick?.(),n?.(g.id),a(!1)},className:`
|
||||
`,children:[h.jsxs(I,{ref:d,className:"relative",children:[h.jsxs(ae,{onClick:()=>a(!i),className:"relative p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors","aria-label":Vs("common.notifications"),children:[h.jsx(oe,{name:de.Bell,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),f>0&&h.jsx(Q,{className:"absolute top-0 right-0 flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 rounded-full",children:f>9?"9+":f})]}),i&&h.jsxs(I,{className:"absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsx(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx(Q,{className:"font-semibold text-gray-900 dark:text-white",children:Vs("common.notifications")})}),h.jsx(I,{className:"max-h-96 overflow-y-auto",children:t.length===0?h.jsx(I,{className:"px-4 py-8 text-center text-gray-500 dark:text-gray-400",children:h.jsx(Q,{children:Vs("common.no_notifications")})}):t.map(g=>h.jsx(ae,{onClick:()=>{g.onClick?.(),n?.(g.id),a(!1)},className:`
|
||||
w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 border-b border-gray-100 dark:border-gray-700 transition-colors
|
||||
${g.read?"":"bg-blue-50 dark:bg-blue-900/20"}
|
||||
`,children:h.jsxs(I,{className:"flex items-start gap-3",children:[g.iconName&&h.jsx(re,{name:g.iconName,className:"w-5 h-5 text-gray-600 dark:text-gray-400 mt-0.5"}),h.jsxs(I,{className:"flex-1",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:g.title}),h.jsx(I,{className:"text-gray-600 dark:text-gray-400 text-sm mt-1",children:g.message}),h.jsx(I,{className:"text-gray-400 dark:text-gray-500 text-xs mt-1",children:g.time})]}),!g.read&&h.jsx(Q,{className:"w-2 h-2 bg-blue-500 rounded-full mt-1.5"})]})},g.id))})]})]}),h.jsxs(I,{ref:u,className:"relative",children:[h.jsxs(oe,{onClick:()=>c(!l),className:"flex items-center gap-3 px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[t.avatar?h.jsx(yn,{src:t.avatar,alt:t.name,className:"w-8 h-8 rounded-full object-cover"}):h.jsx(I,{className:"w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center",children:h.jsx(re,{name:ce.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:"hidden md:block text-left",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:t.name}),t.role&&h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-xs",children:t.role})]}),h.jsx(re,{name:ce.ChevronDown,className:"w-4 h-4 text-gray-600 dark:text-gray-400"})]}),l&&h.jsxs(I,{className:"absolute right-0 mt-2 w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white",children:t.name}),h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-sm",children:t.email})]}),h.jsxs(I,{className:"py-2",children:[h.jsxs(oe,{onClick:()=>{r?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors text-gray-900 dark:text-white",children:[h.jsx(re,{name:ce.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{children:Es("common.profile_settings")})]}),h.jsxs(oe,{onClick:()=>{s?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 text-red-600 dark:text-red-400 transition-colors",children:[h.jsx(re,{name:ce.ArrowRight,className:"w-5 h-5"}),h.jsx(Q,{children:Es("common.logout")})]})]})]})]})]})},ky=({notifications:t=[],unreadCount:e=0,hasMore:n=!1,loading:r=!1,titleText:s="Notifications",emptyText:o="No notifications",markAllReadText:i="Mark all as read",deleteAllText:a="Delete all",unreadOnlyText:l="Unread only",unreadOnly:c=!1,onNotificationClick:d,onClose:u,onLoadMore:f,onMarkAllRead:g,onDeleteAll:m,onDelete:p,onUnreadOnlyToggle:y,dropdownAlign:b="right",className:_="",id:x,editorAttrs:E})=>{const[C,k]=v.useState(!1),N=v.useRef(null),S=v.useRef(null),w=v.useRef(null),M=v.useRef(null),T=v.useRef(new Set),P=v.useRef(f),L=v.useRef(n),$=v.useRef(r),A=v.useRef(t.length),D=v.useRef(-1);v.useEffect(()=>{P.current=f,L.current=n,$.current=r,A.current=t.length}),v.useEffect(()=>{if(!M.current||!w.current||!C)return;D.current=-1;const j=new IntersectionObserver(([W])=>{W.isIntersecting&&($.current||!L.current||D.current!==A.current&&(D.current=A.current,P.current?.()))},{root:w.current,threshold:.1});return j.observe(M.current),()=>j.disconnect()},[C]),v.useEffect(()=>{if(!w.current||!C)return;const j=new IntersectionObserver(U=>{U.forEach(B=>{const G=B.target.getAttribute("data-notification-id"),V=B.target.getAttribute("data-unread")==="true";G&&V&&B.isIntersecting&&T.current.add(G)})},{root:w.current,threshold:.5});return w.current.querySelectorAll("[data-notification-id]").forEach(U=>j.observe(U)),()=>j.disconnect()},[C,t]);const O=e>0?e:t.filter(j=>!j.read).length,z=v.useCallback(()=>{if(C){if(O>0){const j=Array.from(T.current).filter(W=>{const U=t.find(B=>String(B.id)===String(W));return U&&!U.read});j.length>0&&u?.(j)}T.current.clear(),k(!1)}},[C,u,O,t]);v.useEffect(()=>{if(!C)return;const j=W=>{N.current&&!N.current.contains(W.target)&&z()};return document.addEventListener("mousedown",j),()=>document.removeEventListener("mousedown",j)},[C,z]);const H=v.useCallback(()=>{C?z():k(!0)},[C,z]);return v.useLayoutEffect(()=>{const j=S.current;if(!C||!j)return;j.style.transform="";const W=j.getBoundingClientRect(),U=typeof window<"u"?window.innerWidth:0;if(W.width===0&&W.height===0)return;const B=8;let G=0;W.right>U-B?G=-(W.right-(U-B)):W.left<B&&(G=B-W.left),G!==0&&(j.style.transform=`translateX(${G}px)`)},[C,b,t.length]),h.jsxs(I,{ref:N,className:`relative ${_}`,id:x,...E,children:[h.jsxs(oe,{onClick:H,className:"relative w-9 h-9 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors cursor-pointer","aria-label":s,children:[h.jsx(re,{name:ce.Bell,className:"text-sm text-slate-500 dark:text-slate-400"}),O>0&&h.jsx(Q,{clas
Line truncated
|
||||
`,children:h.jsxs(I,{className:"flex items-start gap-3",children:[g.iconName&&h.jsx(oe,{name:g.iconName,className:"w-5 h-5 text-gray-600 dark:text-gray-400 mt-0.5"}),h.jsxs(I,{className:"flex-1",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:g.title}),h.jsx(I,{className:"text-gray-600 dark:text-gray-400 text-sm mt-1",children:g.message}),h.jsx(I,{className:"text-gray-400 dark:text-gray-500 text-xs mt-1",children:g.time})]}),!g.read&&h.jsx(Q,{className:"w-2 h-2 bg-blue-500 rounded-full mt-1.5"})]})},g.id))})]})]}),h.jsxs(I,{ref:u,className:"relative",children:[h.jsxs(ae,{onClick:()=>c(!l),className:"flex items-center gap-3 px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[e.avatar?h.jsx(_n,{src:e.avatar,alt:e.name,className:"w-8 h-8 rounded-full object-cover"}):h.jsx(I,{className:"w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center",children:h.jsx(oe,{name:de.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:"hidden md:block text-left",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:e.name}),e.role&&h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-xs",children:e.role})]}),h.jsx(oe,{name:de.ChevronDown,className:"w-4 h-4 text-gray-600 dark:text-gray-400"})]}),l&&h.jsxs(I,{className:"absolute right-0 mt-2 w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white",children:e.name}),h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-sm",children:e.email})]}),h.jsxs(I,{className:"py-2",children:[h.jsxs(ae,{onClick:()=>{r?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors text-gray-900 dark:text-white",children:[h.jsx(oe,{name:de.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{children:Vs("common.profile_settings")})]}),h.jsxs(ae,{onClick:()=>{s?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 text-red-600 dark:text-red-400 transition-colors",children:[h.jsx(oe,{name:de.ArrowRight,className:"w-5 h-5"}),h.jsx(Q,{children:Vs("common.logout")})]})]})]})]})]})},Gy=({notifications:e=[],unreadCount:t=0,hasMore:n=!1,loading:r=!1,titleText:s="Notifications",emptyText:o="No notifications",markAllReadText:i="Mark all as read",deleteAllText:a="Delete all",unreadOnlyText:l="Unread only",unreadOnly:c=!1,onNotificationClick:d,onClose:u,onLoadMore:f,onMarkAllRead:g,onDeleteAll:m,onDelete:p,onUnreadOnlyToggle:y,dropdownAlign:b="right",className:_="",id:x,editorAttrs:M})=>{const[C,k]=v.useState(!1),N=v.useRef(null),S=v.useRef(null),w=v.useRef(null),E=v.useRef(null),D=v.useRef(new Set),P=v.useRef(f),L=v.useRef(n),$=v.useRef(r),A=v.useRef(e.length),O=v.useRef(-1);v.useEffect(()=>{P.current=f,L.current=n,$.current=r,A.current=e.length}),v.useEffect(()=>{if(!E.current||!w.current||!C)return;O.current=-1;const j=new IntersectionObserver(([G])=>{G.isIntersecting&&($.current||!L.current||O.current!==A.current&&(O.current=A.current,P.current?.()))},{root:w.current,threshold:.1});return j.observe(E.current),()=>j.disconnect()},[C]),v.useEffect(()=>{if(!w.current||!C)return;const j=new IntersectionObserver(U=>{U.forEach(W=>{const Y=W.target.getAttribute("data-notification-id"),V=W.target.getAttribute("data-unread")==="true";Y&&V&&W.isIntersecting&&D.current.add(Y)})},{root:w.current,threshold:.5});return w.current.querySelectorAll("[data-notification-id]").forEach(U=>j.observe(U)),()=>j.disconnect()},[C,e]);const T=t>0?t:e.filter(j=>!j.read).length,z=v.useCallback(()=>{if(C){if(T>0){const j=Array.from(D.current).filter(G=>{const U=e.find(W=>String(W.id)===String(G));return U&&!U.read});j.length>0&&u?.(j)}D.current.clear(),k(!1)}},[C,u,T,e]);v.useEffect(()=>{if(!C)return;const j=G=>{N.current&&!N.current.contains(G.target)&&z()};return document.addEventListener("mousedown",j),()=>document.removeEventListener("mousedown",j)},[C,z]);const H=v.useCallback(()=>{C?z():k(!0)},[C,z]);return v.useLayoutEffect(()=>{const j=S.current;if(!C||!j)return;j.style.transform="";const G=j.getBoundingClientRect(),U=typeof window<"u"?window.innerWidth:0;if(G.width===0&&G.height===0)return;const W=8;let Y=0;G.right>U-W?Y=-(G.right-(U-W)):G.left<W&&(Y=W-G.left),Y!==0&&(j.style.transform=`translateX(${Y}px)`)},[C,b,e.length]),h.jsxs(I,{ref:N,className:`relative ${_}`,id:x,...M,children:[h.jsxs(ae,{onClick:H,className:"relative w-9 h-9 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors cursor-pointer","aria-label":s,children:[h.jsx(oe,{name:de.Bell,className:"text-sm text-slate-500 dark:text-slate-400"}),T>0&&h.jsx(Q,{clas
Line truncated
|
||||
px-3 py-1.5 text-sm font-medium rounded-md transition-colors
|
||||
${g===x?"bg-blue-600 text-white":"bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"}
|
||||
${u?"opacity-50 cursor-not-allowed":""}
|
||||
`,children:lu(x)},x))})]}):h.jsxs(I,{ref:p,className:`relative ${s}`,id:a,...l,children:[h.jsxs(oe,{onClick:()=>d(!c),className:i?"flex items-center gap-1.5 px-2.5 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors cursor-pointer":"p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors","aria-haspopup":"listbox","aria-expanded":c,"aria-label":e,children:[h.jsx(re,{name:ce.Globe,className:i?"w-4 h-4":"w-5 h-5 text-gray-600 dark:text-gray-400"}),i&&h.jsx(Q,{className:"font-medium uppercase",children:g}),i&&h.jsx(re,{name:ce.ChevronDown,className:"w-3 h-3"})]}),c&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsx(I,{className:"py-2",children:t.map(x=>h.jsxs(oe,{onClick:()=>_(x),disabled:u,className:`
|
||||
`,children:xu(x)},x))})]}):h.jsxs(I,{ref:p,className:`relative ${s}`,id:a,...l,children:[h.jsxs(ae,{onClick:()=>d(!c),className:i?"flex items-center gap-1.5 px-2.5 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors cursor-pointer":"p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors","aria-haspopup":"listbox","aria-expanded":c,"aria-label":t,children:[h.jsx(oe,{name:de.Globe,className:i?"w-4 h-4":"w-5 h-5 text-gray-600 dark:text-gray-400"}),i&&h.jsx(Q,{className:"font-medium uppercase",children:g}),i&&h.jsx(oe,{name:de.ChevronDown,className:"w-3 h-3"})]}),c&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsx(I,{className:"py-2",children:e.map(x=>h.jsxs(ae,{onClick:()=>_(x),disabled:u,className:`
|
||||
w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors
|
||||
${g===x?"bg-gray-50 dark:bg-gray-700":""}
|
||||
${u?"opacity-50 cursor-not-allowed":""}
|
||||
`,children:[h.jsx(Q,{className:"text-gray-900 dark:text-white flex-1",children:lu(x)}),g===x&&h.jsx(re,{name:ce.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400"})]},x))})})]})},_y=({copyright:t="© 2026 G7",version:e,quickLinks:n=[],className:r="",changelogModalId:s,copyrightModalId:o,id:i,editorAttrs:a})=>{const l=v.useCallback(()=>{s&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/changelog",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreChangelogContent:"{{response.data.content}}"}},{handler:"openModal",target:s}],onError:[{handler:"openModal",target:s}]}]})},[s]),c=v.useCallback(()=>{o&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/license",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreLicenseContent:"{{response.data.content}}"}},{handler:"openModal",target:o}],onError:[{handler:"openModal",target:o}]}]})},[o]);return h.jsx(I,{className:`admin-footer ${r}`,id:i,...a,children:h.jsxs(I,{className:"admin-footer-row",children:[h.jsxs(I,{className:"admin-footer-group",children:[h.jsx(Q,{className:o?"admin-footer-link-hover":"",onClick:o?c:void 0,children:t}),e&&h.jsxs(h.Fragment,{children:[h.jsx(Q,{className:"hidden md:inline",children:"•"}),h.jsxs(Q,{className:`flex-center gap-2 ${s?"admin-footer-link-hover":""}`,onClick:s?l:void 0,children:[h.jsx(re,{name:ce.Tag,className:"w-4 h-4"}),h.jsxs(Q,{children:["v",e]})]})]})]}),n.length>0&&h.jsx(I,{className:"flex-center gap-4",children:n.map(d=>h.jsxs(Lr,{href:d.url,className:"admin-footer-link",children:[d.iconName&&h.jsx(re,{name:d.iconName,className:"w-4 h-4"}),h.jsx(Q,{children:d.label})]},d.id))})]})})},Ey=({title:t,description:e,breadcrumbItems:n,tabs:r,onTabChange:s,actions:o,className:i="",children:a,id:l,editorAttrs:c})=>{const d=(u="secondary")=>({primary:"page-action-primary",secondary:"page-action-secondary",danger:"page-action-danger",ghost:"page-action-ghost"})[u];return h.jsxs(I,{className:i,id:l,...c,children:[n&&n.length>0&&h.jsx(I,{className:"mb-md",children:h.jsx(ou,{items:n})}),h.jsxs(I,{className:"flex-between mb-lg",children:[h.jsxs(I,{children:[h.jsx(Da,{className:"page-title",children:t}),e&&h.jsx(yt,{className:"page-description",children:e})]}),a?h.jsx(I,{className:"flex-center page-actions",children:a}):o&&o.length>0?h.jsx(I,{className:"page-actions",children:o.map(u=>h.jsxs(oe,{onClick:u.onClick,disabled:u.disabled,className:d(u.variant),children:[u.iconName&&h.jsx(re,{name:u.iconName,className:"icon-md"}),u.variant!=="ghost"&&u.label&&h.jsx(I,{children:u.label})]},u.id))}):null]}),r&&r.length>0&&h.jsx(I,{className:"tab-container",children:r.map(u=>h.jsxs(oe,{onClick:()=>s?.(u.value),className:u.active?"tab-btn-active":"tab-btn",children:[h.jsx(I,{children:u.label}),u.badge!==void 0&&h.jsx(I,{className:u.active?"tab-badge-active":"tab-badge",children:u.badge})]},u.id))})]})},My=()=>window.G7Core,Ny=({title:t,description:e,className:n="",style:r,children:s,id:o,editorAttrs:i})=>{const a="flex items-center justify-between",l=My()?.style?.mergeClasses?.(a,n)??`${a} ${n}`;return h.jsxs(I,{className:l,style:r,id:o,...i,children:[h.jsxs(I,{children:[t&&h.jsx(Jn,{className:"text-base font-semibold text-gray-900 dark:text-white",children:t}),e&&h.jsx(yt,{className:"mt-1 text-sm text-gray-500 dark:text-gray-400",children:e})]}),s&&h.jsx(I,{className:"flex items-center gap-2",children:s})]})},er=(t,e)=>window.G7Core?.t?.(t,e)??t,Oy=({image:t,imageAlt:e,vendor:n,name:r,version:s,status:o,updateAvailable:i=!1,latestVersion:a,dependencies:l=[],showLayoutEditButton:c=!1,actions:d=[],onLayoutEditClick:u,className:f="",id:g,editorAttrs:m})=>{const p=b=>({active:"success",inactive:"default",pending:"pending",error:"error"})[b],y=b=>({active:er("common.status_active"),inactive:er("common.status_inactive"),pending:er("common.status_pending"),error:er("common.status_error")})[b];return h.jsxs(I,{className:`
|
||||
`,children:[h.jsx(Q,{className:"text-gray-900 dark:text-white flex-1",children:xu(x)}),g===x&&h.jsx(oe,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400"})]},x))})})]})},Ky=({copyright:e="© 2026 G7",version:t,quickLinks:n=[],className:r="",changelogModalId:s,copyrightModalId:o,id:i,editorAttrs:a})=>{const l=v.useCallback(()=>{s&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/changelog",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreChangelogContent:"{{response.data.content}}"}},{handler:"openModal",target:s}],onError:[{handler:"openModal",target:s}]}]})},[s]),c=v.useCallback(()=>{o&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/license",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreLicenseContent:"{{response.data.content}}"}},{handler:"openModal",target:o}],onError:[{handler:"openModal",target:o}]}]})},[o]);return h.jsx(I,{className:`admin-footer ${r}`,id:i,...a,children:h.jsxs(I,{className:"admin-footer-row",children:[h.jsxs(I,{className:"admin-footer-group",children:[h.jsx(Q,{className:o?"admin-footer-link-hover":"",onClick:o?c:void 0,children:e}),t&&h.jsxs(h.Fragment,{children:[h.jsx(Q,{className:"hidden md:inline",children:"•"}),h.jsxs(Q,{className:`flex-center gap-2 ${s?"admin-footer-link-hover":""}`,onClick:s?l:void 0,children:[h.jsx(oe,{name:de.Tag,className:"w-4 h-4"}),h.jsxs(Q,{children:["v",t]})]})]})]}),n.length>0&&h.jsx(I,{className:"flex-center gap-4",children:n.map(d=>h.jsxs(Yr,{href:d.url,className:"admin-footer-link",children:[d.iconName&&h.jsx(oe,{name:d.iconName,className:"w-4 h-4"}),h.jsx(Q,{children:d.label})]},d.id))})]})})},qy=({title:e,description:t,breadcrumbItems:n,tabs:r,onTabChange:s,actions:o,className:i="",children:a,id:l,editorAttrs:c})=>{const d=(u="secondary")=>({primary:"page-action-primary",secondary:"page-action-secondary",danger:"page-action-danger",ghost:"page-action-ghost"})[u];return h.jsxs(I,{className:i,id:l,...c,children:[n&&n.length>0&&h.jsx(I,{className:"mb-md",children:h.jsx(bu,{items:n})}),h.jsxs(I,{className:"flex-between mb-lg",children:[h.jsxs(I,{children:[h.jsx(Ba,{className:"page-title",children:e}),t&&h.jsx(vt,{className:"page-description",children:t})]}),a?h.jsx(I,{className:"flex-center page-actions",children:a}):o&&o.length>0?h.jsx(I,{className:"page-actions",children:o.map(u=>h.jsxs(ae,{onClick:u.onClick,disabled:u.disabled,className:d(u.variant),children:[u.iconName&&h.jsx(oe,{name:u.iconName,className:"icon-md"}),u.variant!=="ghost"&&u.label&&h.jsx(I,{children:u.label})]},u.id))}):null]}),r&&r.length>0&&h.jsx(I,{className:"tab-container",children:r.map(u=>h.jsxs(ae,{onClick:()=>s?.(u.value),className:u.active?"tab-btn-active":"tab-btn",children:[h.jsx(I,{children:u.label}),u.badge!==void 0&&h.jsx(I,{className:u.active?"tab-badge-active":"tab-badge",children:u.badge})]},u.id))})]})},Zy=()=>window.G7Core,Qy=({title:e,description:t,className:n="",style:r,children:s,id:o,editorAttrs:i})=>{const a="flex items-center justify-between",l=Zy()?.style?.mergeClasses?.(a,n)??`${a} ${n}`;return h.jsxs(I,{className:l,style:r,id:o,...i,children:[h.jsxs(I,{children:[e&&h.jsx(ar,{className:"text-base font-semibold text-gray-900 dark:text-white",children:e}),t&&h.jsx(vt,{className:"mt-1 text-sm text-gray-500 dark:text-gray-400",children:t})]}),s&&h.jsx(I,{className:"flex items-center gap-2",children:s})]})},cr=(e,t)=>window.G7Core?.t?.(e,t)??e,Jy=({image:e,imageAlt:t,vendor:n,name:r,version:s,status:o,updateAvailable:i=!1,latestVersion:a,dependencies:l=[],showLayoutEditButton:c=!1,actions:d=[],onLayoutEditClick:u,className:f="",id:g,editorAttrs:m})=>{const p=b=>({active:"success",inactive:"default",pending:"pending",error:"error"})[b],y=b=>({active:cr("common.status_active"),inactive:cr("common.status_inactive"),pending:cr("common.status_pending"),error:cr("common.status_error")})[b];return h.jsxs(I,{className:`
|
||||
bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow
|
||||
${f}
|
||||
`,id:g,...m,children:[t&&h.jsxs(I,{className:"relative",children:[h.jsx(yn,{src:t,alt:e||er("common.template_preview",{vendor:n,name:r}),className:"w-full h-48 object-cover"}),h.jsx(I,{className:"absolute top-3 right-3",children:h.jsx(Fo,{status:p(o),label:y(o),showIcon:!0})}),i&&h.jsx(I,{className:"absolute top-3 left-3",children:h.jsxs(Q,{className:"flex items-center gap-1 px-2 py-1 bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border border-yellow-200 dark:border-yellow-800 rounded-md text-xs font-semibold",children:[h.jsx(re,{name:ce.ExclamationTriangle,className:"w-3 h-3"}),h.jsx(Q,{children:er("common.update_available")})]})})]}),h.jsxs(I,{className:"p-4",children:[h.jsxs(I,{className:"flex items-start justify-between mb-3",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(Ir,{className:"text-lg font-bold text-gray-900 dark:text-white",children:[n,"/",r]}),h.jsxs(I,{className:"flex items-center gap-2 mt-1 text-sm text-gray-600 dark:text-gray-400",children:[h.jsxs(Q,{children:["v",s]}),i&&a&&h.jsxs(h.Fragment,{children:[h.jsx(Q,{children:"→"}),h.jsxs(Q,{className:"text-yellow-600 dark:text-yellow-400 font-semibold",children:["v",a]})]})]}),!t&&h.jsx(I,{className:"mt-2",children:h.jsx(Fo,{status:p(o),label:y(o),showIcon:!0})})]}),d.length>0&&h.jsx(ys,{items:d,triggerIconName:ce.EllipsisVertical,position:"left"})]}),l.length>0&&h.jsxs(I,{className:"mb-3",children:[h.jsxs(yt,{className:"text-xs text-gray-500 dark:text-gray-400 mb-1",children:[er("common.dependencies"),":"]}),h.jsx(I,{className:"flex flex-wrap gap-1",children:l.map((b,_)=>h.jsx(Q,{className:"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs rounded",children:b},_))})]}),c&&u&&h.jsxs(oe,{onClick:u,className:"w-full px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 transition-colors flex items-center justify-center gap-2 font-medium",children:[h.jsx(re,{name:ce.Edit,className:"w-4 h-4"}),h.jsx(Q,{children:er("common.edit_layout")})]})]})]})},cu=({type:t,message:e,dismissible:n=!1,onDismiss:r,className:s="",id:o,editorAttrs:i})=>{const a={info:{containerClass:"alert-info",iconClass:"alert-icon-info",textClass:"alert-text-info",icon:ce.InfoCircle},success:{containerClass:"alert-success",iconClass:"alert-icon-success",textClass:"alert-text-success",icon:ce.CheckCircle},warning:{containerClass:"alert-warning",iconClass:"alert-icon-warning",textClass:"alert-text-warning",icon:ce.ExclamationTriangle},error:{containerClass:"alert-error",iconClass:"alert-icon-error",textClass:"alert-text-error",icon:ce.TimesCircle}},l=a[t]??a.info;return h.jsxs(I,{className:`${l.containerClass} ${s}`,role:"alert","aria-live":"polite",id:o,...i,children:[h.jsx(re,{name:l.icon,className:l.iconClass,ariaLabel:`${t} icon`}),h.jsx(yt,{className:l.textClass,children:e}),n&&h.jsx(oe,{onClick:r,className:`alert-dismiss ${l.textClass}`,"aria-label":"Close alert",children:h.jsx(re,{name:ce.Times,className:l.iconClass,ariaLabel:"Close"})})]})},Ty=({layoutName:t,onBack:e,onPreview:n,onSave:r,isSaving:s=!1,className:o=""})=>{const i=`
|
||||
`,id:g,...m,children:[e&&h.jsxs(I,{className:"relative",children:[h.jsx(_n,{src:e,alt:t||cr("common.template_preview",{vendor:n,name:r}),className:"w-full h-48 object-cover"}),h.jsx(I,{className:"absolute top-3 right-3",children:h.jsx(Ko,{status:p(o),label:y(o),showIcon:!0})}),i&&h.jsx(I,{className:"absolute top-3 left-3",children:h.jsxs(Q,{className:"flex items-center gap-1 px-2 py-1 bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border border-yellow-200 dark:border-yellow-800 rounded-md text-xs font-semibold",children:[h.jsx(oe,{name:de.ExclamationTriangle,className:"w-3 h-3"}),h.jsx(Q,{children:cr("common.update_available")})]})})]}),h.jsxs(I,{className:"p-4",children:[h.jsxs(I,{className:"flex items-start justify-between mb-3",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(Gr,{className:"text-lg font-bold text-gray-900 dark:text-white",children:[n,"/",r]}),h.jsxs(I,{className:"flex items-center gap-2 mt-1 text-sm text-gray-600 dark:text-gray-400",children:[h.jsxs(Q,{children:["v",s]}),i&&a&&h.jsxs(h.Fragment,{children:[h.jsx(Q,{children:"→"}),h.jsxs(Q,{className:"text-yellow-600 dark:text-yellow-400 font-semibold",children:["v",a]})]})]}),!e&&h.jsx(I,{className:"mt-2",children:h.jsx(Ko,{status:p(o),label:y(o),showIcon:!0})})]}),d.length>0&&h.jsx(Ps,{items:d,triggerIconName:de.EllipsisVertical,position:"left"})]}),l.length>0&&h.jsxs(I,{className:"mb-3",children:[h.jsxs(vt,{className:"text-xs text-gray-500 dark:text-gray-400 mb-1",children:[cr("common.dependencies"),":"]}),h.jsx(I,{className:"flex flex-wrap gap-1",children:l.map((b,_)=>h.jsx(Q,{className:"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs rounded",children:b},_))})]}),c&&u&&h.jsxs(ae,{onClick:u,className:"w-full px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 transition-colors flex items-center justify-center gap-2 font-medium",children:[h.jsx(oe,{name:de.Edit,className:"w-4 h-4"}),h.jsx(Q,{children:cr("common.edit_layout")})]})]})]})},wu=({type:e,message:t,dismissible:n=!1,onDismiss:r,className:s="",id:o,editorAttrs:i})=>{const a={info:{containerClass:"alert-info",iconClass:"alert-icon-info",textClass:"alert-text-info",icon:de.InfoCircle},success:{containerClass:"alert-success",iconClass:"alert-icon-success",textClass:"alert-text-success",icon:de.CheckCircle},warning:{containerClass:"alert-warning",iconClass:"alert-icon-warning",textClass:"alert-text-warning",icon:de.ExclamationTriangle},error:{containerClass:"alert-error",iconClass:"alert-icon-error",textClass:"alert-text-error",icon:de.TimesCircle}},l=a[e]??a.info;return h.jsxs(I,{className:`${l.containerClass} ${s}`,role:"alert","aria-live":"polite",id:o,...i,children:[h.jsx(oe,{name:l.icon,className:l.iconClass,ariaLabel:`${e} icon`}),h.jsx(vt,{className:l.textClass,children:t}),n&&h.jsx(ae,{onClick:r,className:`alert-dismiss ${l.textClass}`,"aria-label":"Close alert",children:h.jsx(oe,{name:de.Times,className:l.iconClass,ariaLabel:"Close"})})]})},Ry=({layoutName:e,onBack:t,onPreview:n,onSave:r,isSaving:s=!1,className:o=""})=>{const i=`
|
||||
flex items-center justify-between gap-4 p-4 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700
|
||||
${o}
|
||||
`.trim().replace(/\s+/g," ");return h.jsxs(I,{className:i,children:[h.jsxs(I,{className:"flex items-center gap-3",children:[h.jsx(oe,{onClick:e,className:"p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors bg-transparent border-0 cursor-pointer","aria-label":"뒤로가기",children:h.jsx(re,{name:ce.ArrowLeft,className:"text-gray-700 dark:text-gray-300",ariaLabel:"뒤로가기"})}),h.jsx(Jn,{className:"text-xl font-semibold text-gray-900 dark:text-gray-100",children:t})]}),h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(oe,{onClick:n,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-lg transition-colors border-0 cursor-pointer","aria-label":"미리보기",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(re,{name:ce.Eye,className:"text-gray-700 dark:text-gray-300",ariaLabel:"미리보기 아이콘"}),h.jsx("span",{children:"미리보기"})]})}),h.jsx(oe,{onClick:r,disabled:s,className:`
|
||||
`.trim().replace(/\s+/g," ");return h.jsxs(I,{className:i,children:[h.jsxs(I,{className:"flex items-center gap-3",children:[h.jsx(ae,{onClick:t,className:"p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors bg-transparent border-0 cursor-pointer","aria-label":"뒤로가기",children:h.jsx(oe,{name:de.ArrowLeft,className:"text-gray-700 dark:text-gray-300",ariaLabel:"뒤로가기"})}),h.jsx(ar,{className:"text-xl font-semibold text-gray-900 dark:text-gray-100",children:e})]}),h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(ae,{onClick:n,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-lg transition-colors border-0 cursor-pointer","aria-label":"미리보기",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(oe,{name:de.Eye,className:"text-gray-700 dark:text-gray-300",ariaLabel:"미리보기 아이콘"}),h.jsx("span",{children:"미리보기"})]})}),h.jsx(ae,{onClick:r,disabled:s,className:`
|
||||
px-4 py-2 rounded-lg transition-colors border-0 cursor-pointer
|
||||
${s?"bg-blue-400 dark:bg-blue-600 cursor-not-allowed":"bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-600"}
|
||||
text-white
|
||||
`.trim().replace(/\s+/g," "),"aria-label":s?"저장 중...":"저장",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(re,{name:s?ce.Spinner:ce.Save,className:`text-white ${s?"animate-spin":""}`,ariaLabel:s?"저장 중 아이콘":"저장 아이콘"}),h.jsx("span",{children:s?"저장 중...":"저장"})]})})]})]})},Ay=({files:t,selectedId:e,onSelect:n,className:r=""})=>{const s=a=>{try{const l=new Date(a);if(isNaN(l.getTime()))return a;const c=l.getFullYear(),d=String(l.getMonth()+1).padStart(2,"0"),u=String(l.getDate()).padStart(2,"0"),f=String(l.getHours()).padStart(2,"0"),g=String(l.getMinutes()).padStart(2,"0");return`${c}-${d}-${u} ${f}:${g}`}catch{return a}},o=a=>{n(a)},i=`
|
||||
`.trim().replace(/\s+/g," "),"aria-label":s?"저장 중...":"저장",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(oe,{name:s?de.Spinner:de.Save,className:`text-white ${s?"animate-spin":""}`,ariaLabel:s?"저장 중 아이콘":"저장 아이콘"}),h.jsx("span",{children:s?"저장 중...":"저장"})]})})]})]})},ev=({files:e,selectedId:t,onSelect:n,className:r=""})=>{const s=a=>{try{const l=new Date(a);if(isNaN(l.getTime()))return a;const c=l.getFullYear(),d=String(l.getMonth()+1).padStart(2,"0"),u=String(l.getDate()).padStart(2,"0"),f=String(l.getHours()).padStart(2,"0"),g=String(l.getMinutes()).padStart(2,"0");return`${c}-${d}-${u} ${f}:${g}`}catch{return a}},o=a=>{n(a)},i=`
|
||||
w-full
|
||||
${r}
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(I,{className:i,children:t.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"파일이 없습니다."}):h.jsx(Ia,{className:"divide-y divide-gray-200 dark:divide-gray-700",role:"listbox","aria-label":"레이아웃 파일 목록",children:t.map(a=>{const l=a.id===e,c=`
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(I,{className:i,children:e.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"파일이 없습니다."}):h.jsx(ja,{className:"divide-y divide-gray-200 dark:divide-gray-700",role:"listbox","aria-label":"레이아웃 파일 목록",children:e.map(a=>{const l=a.id===t,c=`
|
||||
flex items-center justify-between p-4 cursor-pointer
|
||||
transition-colors duration-150
|
||||
${l?"bg-blue-100 dark:bg-blue-900 border-l-4 border-blue-500":"hover:bg-gray-100 dark:hover:bg-gray-800"}
|
||||
@@ -105,8 +106,8 @@ var SirsoftAdminBasic=(function(R,h,v,Tn){"use strict";var F5=Object.definePrope
|
||||
`.trim().replace(/\s+/g," "),u=`
|
||||
text-sm
|
||||
${l?"text-blue-700 dark:text-blue-300":"text-gray-500 dark:text-gray-400"}
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(La,{className:c,onClick:()=>o(a.id),role:"option","aria-selected":l,tabIndex:0,onKeyDown:f=>{(f.key==="Enter"||f.key===" ")&&(f.preventDefault(),o(a.id))},children:h.jsxs(I,{className:"flex flex-col gap-1",children:[h.jsx(Q,{className:d,children:a.name}),h.jsx(Q,{className:u,children:s(a.updated_at)})]})},a.id)})})})};function du(t,e){(e==null||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n<e;n++)r[n]=t[n];return r}function Dy(t){if(Array.isArray(t))return t}function Iy(t,e,n){return(e=Hy(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function Ly(t,e){var n=t==null?null:typeof Symbol<"u"&&t[Symbol.iterator]||t["@@iterator"];if(n!=null){var r,s,o,i,a=[],l=!0,c=!1;try{if(o=(n=n.call(t)).next,e!==0)for(;!(l=(r=o.call(n)).done)&&(a.push(r.value),a.length!==e);l=!0);}catch(d){c=!0,s=d}finally{try{if(!l&&n.return!=null&&(i=n.return(),Object(i)!==i))return}finally{if(c)throw s}}return a}}function Py(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function uu(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(t,s).enumerable})),n.push.apply(n,r)}return n}function fu(t){for(var e=1;e<arguments.length;e++){var n=arguments[e]!=null?arguments[e]:{};e%2?uu(Object(n),!0).forEach(function(r){Iy(t,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):uu(Object(n)).forEach(function(r){Object.defineProperty(t,r,Object.getOwnPropertyDescriptor(n,r))})}return t}function $y(t,e){if(t==null)return{};var n,r,s=Fy(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(r=0;r<o.length;r++)n=o[r],e.indexOf(n)===-1&&{}.propertyIsEnumerable.call(t,n)&&(s[n]=t[n])}return s}function Fy(t,e){if(t==null)return{};var n={};for(var r in t)if({}.hasOwnProperty.call(t,r)){if(e.indexOf(r)!==-1)continue;n[r]=t[r]}return n}function zy(t,e){return Dy(t)||Ly(t,e)||Uy(t,e)||Py()}function Vy(t,e){if(typeof t!="object"||!t)return t;var n=t[Symbol.toPrimitive];if(n!==void 0){var r=n.call(t,e);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(t)}function Hy(t){var e=Vy(t,"string");return typeof e=="symbol"?e:e+""}function Uy(t,e){if(t){if(typeof t=="string")return du(t,e);var n={}.toString.call(t).slice(8,-1);return n==="Object"&&t.constructor&&(n=t.constructor.name),n==="Map"||n==="Set"?Array.from(t):n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?du(t,e):void 0}}function By(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function hu(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(t,s).enumerable})),n.push.apply(n,r)}return n}function gu(t){for(var e=1;e<arguments.length;e++){var n=arguments[e]!=null?arguments[e]:{};e%2?hu(Object(n),!0).forEach(function(r){By(t,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):hu(Object(n)).forEach(function(r){Object.defineProperty(t,r,Object.getOwnPropertyDescriptor(n,r))})}return t}function jy(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];return function(r){return e.reduceRight(function(s,o){return o(s)},r)}}function Ms(t){return function e(){for(var n=this,r=arguments.length,s=new Array(r),o=0;o<r;o++)s[o]=arguments[o];return s.length>=t.length?t.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;l<i;l++)a[l]=arguments[l];return e.apply(n,[].concat(s,a))}}}function Wo(t){return{}.toString.call(t).includes("Object")}function Wy(t){return!Object.keys(t).length}function Ns(t){return typeof t=="function"}function Gy(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function Yy(t,e){return Wo(e)||tr("changeType"),Object.keys(e).some(function(n){return!Gy(t,n)})&&tr("changeField"),e}function Xy(t){Ns(t)||tr("selectorType")}function Ky(t){Ns(t)||Wo(t)||tr("handlerType"),Wo(t)&&Object.values(t).some(function(e){return!Ns(e)})&&tr("handlersType")}function qy(t){t||tr("initialIsRequired"),Wo(t)||tr("initialType"),Wy(t)&&tr("initialContent")}function Zy(t,e){throw new Error(t[e]||t.default)}var Qy={initialIsRequired:"initial state is required",initialType:"initial state should be an object",initialContent:"initial state shouldn't be an empty object",handlerType:"handler should be an object or a function",handlersType:"all handlers should be a functions",selectorType:"selector should be a function",changeType:"provided value of changes should be an object",changeField:'it seams you want to change a field in the state which is not specified in the "initial" state',default:"an unknown error accured in `state-local` package"},tr=Ms(Zy)(Qy),Go={changes:Yy,selector:Xy,handler:Ky,initial:qy};function Jy(t){var e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};Go.initial(t),Go.handler(e);var n={current:t},r=Ms(tv)(n,e),s=Ms(ev)(n),o=Ms(Go.changes)(t),i=Ms(Ry)(n);function a(){var c=arguments.length>0&&arguments[0]!==void 0?arguments[0]:function(d){return d};return Go.selector(c),c(n.current)}function l(c){jy(r,s,o,i)(c)}return[a,l]}function Ry(t,e){return Ns(e)?e(t.current):e}function ev(t,e){return t.current=gu(gu({},t.current),e),e}function tv(t,e,n){return Ns(e)?e(t.current):Object.keys(n).forEach(function(r){var s;return(s=e[r])===null||s===void 0?void 0:s.call(e,t.current[r])}),n}var nv={create:Jy},rv={paths:{vs:"https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs"}};function sv(t){return function e(){for(var n=this,r=arguments.length,s=new Array(r),o=0;o<r;o++)s[o]=arguments[o];return s.length>=t.length?t.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;l<i;l++)a[l]=arguments[l];
Line truncated
|
||||
`.trim().replace(/\s+/g," ");return h.jsx(Wa,{className:c,onClick:()=>o(a.id),role:"option","aria-selected":l,tabIndex:0,onKeyDown:f=>{(f.key==="Enter"||f.key===" ")&&(f.preventDefault(),o(a.id))},children:h.jsxs(I,{className:"flex flex-col gap-1",children:[h.jsx(Q,{className:d,children:a.name}),h.jsx(Q,{className:u,children:s(a.updated_at)})]})},a.id)})})})};function ku(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function tv(e){if(Array.isArray(e))return e}function nv(e,t,n){return(t=cv(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function rv(e,t){var n=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(n!=null){var r,s,o,i,a=[],l=!0,c=!1;try{if(o=(n=n.call(e)).next,t!==0)for(;!(l=(r=o.call(n)).done)&&(a.push(r.value),a.length!==t);l=!0);}catch(d){c=!0,s=d}finally{try{if(!l&&n.return!=null&&(i=n.return(),Object(i)!==i))return}finally{if(c)throw s}}return a}}function sv(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function Cu(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(e,s).enumerable})),n.push.apply(n,r)}return n}function Su(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]!=null?arguments[t]:{};t%2?Cu(Object(n),!0).forEach(function(r){nv(e,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Cu(Object(n)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(n,r))})}return e}function ov(e,t){if(e==null)return{};var n,r,s=iv(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(s[n]=e[n])}return s}function iv(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}function av(e,t){return tv(e)||rv(e,t)||dv(e,t)||sv()}function lv(e,t){if(typeof e!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}function cv(e){var t=lv(e,"string");return typeof t=="symbol"?t:t+""}function dv(e,t){if(e){if(typeof e=="string")return ku(e,t);var n={}.toString.call(e).slice(8,-1);return n==="Object"&&e.constructor&&(n=e.constructor.name),n==="Map"||n==="Set"?Array.from(e):n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?ku(e,t):void 0}}function uv(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function _u(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(e,s).enumerable})),n.push.apply(n,r)}return n}function Eu(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]!=null?arguments[t]:{};t%2?_u(Object(n),!0).forEach(function(r){uv(e,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):_u(Object(n)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(n,r))})}return e}function fv(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return function(r){return t.reduceRight(function(s,o){return o(s)},r)}}function Hs(e){return function t(){for(var n=this,r=arguments.length,s=new Array(r),o=0;o<r;o++)s[o]=arguments[o];return s.length>=e.length?e.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;l<i;l++)a[l]=arguments[l];return t.apply(n,[].concat(s,a))}}}function Ro(e){return{}.toString.call(e).includes("Object")}function hv(e){return!Object.keys(e).length}function Us(e){return typeof e=="function"}function gv(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function pv(e,t){return Ro(t)||dr("changeType"),Object.keys(t).some(function(n){return!gv(e,n)})&&dr("changeField"),t}function mv(e){Us(e)||dr("selectorType")}function bv(e){Us(e)||Ro(e)||dr("handlerType"),Ro(e)&&Object.values(e).some(function(t){return!Us(t)})&&dr("handlersType")}function yv(e){e||dr("initialIsRequired"),Ro(e)||dr("initialType"),hv(e)&&dr("initialContent")}function vv(e,t){throw new Error(e[t]||e.default)}var xv={initialIsRequired:"initial state is required",initialType:"initial state should be an object",initialContent:"initial state shouldn't be an empty object",handlerType:"handler should be an object or a function",handlersType:"all handlers should be a functions",selectorType:"selector should be a function",changeType:"provided value of changes should be an object",changeField:'it seams you want to change a field in the state which is not specified in the "initial" state',default:"an unknown error accured in `state-local` package"},dr=Hs(vv)(xv),ei={changes:pv,selector:mv,handler:bv,initial:yv};function wv(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};ei.initial(e),ei.handler(t);var n={current:e},r=Hs(Sv)(n,t),s=Hs(Cv)(n),o=Hs(ei.changes)(e),i=Hs(kv)(n);function a(){var c=arguments.length>0&&arguments[0]!==void 0?arguments[0]:function(d){return d};return ei.selector(c),c(n.current)}function l(c){fv(r,s,o,i)(c)}return[a,l]}function kv(e,t){return Us(t)?t(e.current):t}function Cv(e,t){return e.current=Eu(Eu({},e.current),t),t}function Sv(e,t,n){return Us(t)?t(e.current):Object.keys(n).forEach(function(r){var s;return(s=t[r])===null||s===void 0?void 0:s.call(t,e.current[r])}),n}var _v={create:wv},Ev={paths:{vs:"https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs"}};function Mv(e){return function t(){for(var n=this,r=arguments.length,s=new Array(r),o=0;o<r;o++)s[o]=arguments[o];return s.length>=e.length?e.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;l<i;l++)a[l]=arguments[l];
Line truncated
|
||||
You are using deprecated way of configuration.
|
||||
|
||||
Instead of using
|
||||
@@ -115,10 +116,10 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
||||
monaco.config({ paths: { vs: '...' } })
|
||||
|
||||
For more please check the link https://github.com/suren-atoyan/monaco-loader#config
|
||||
`},mu=sv(lv)(pu),cv={config:iv},dv=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return function(s){return n.reduceRight(function(o,i){return i(o)},s)}};function bu(t,e){return Object.keys(e).forEach(function(n){e[n]instanceof Object&&t[n]&&Object.assign(e[n],bu(t[n],e[n]))}),fu(fu({},t),e)}var uv={type:"cancelation",msg:"operation is manually canceled"};function il(t){var e=!1,n=new Promise(function(r,s){t.then(function(o){return e?s(uv):r(o)}),t.catch(s)});return n.cancel=function(){return e=!0},n}var fv=["monaco"],hv=nv.create({config:rv,isInitialized:!1,resolve:null,reject:null,monaco:null}),yu=zy(hv,2),Os=yu[0],Yo=yu[1];function gv(t){var e=cv.config(t),n=e.monaco,r=$y(e,fv);Yo(function(s){return{config:bu(s.config,r),monaco:n}})}function pv(){var t=Os(function(e){var n=e.monaco,r=e.isInitialized,s=e.resolve;return{monaco:n,isInitialized:r,resolve:s}});if(!t.isInitialized){if(Yo({isInitialized:!0}),t.monaco)return t.resolve(t.monaco),il(al);if(window.monaco&&window.monaco.editor)return vu(window.monaco),t.resolve(window.monaco),il(al);dv(mv,yv)(vv)}return il(al)}function mv(t){return document.body.appendChild(t)}function bv(t){var e=document.createElement("script");return t&&(e.src=t),e}function yv(t){var e=Os(function(r){var s=r.config,o=r.reject;return{config:s,reject:o}}),n=bv("".concat(e.config.paths.vs,"/loader.js"));return n.onload=function(){return t()},n.onerror=e.reject,n}function vv(){var t=Os(function(n){var r=n.config,s=n.resolve,o=n.reject;return{config:r,resolve:s,reject:o}}),e=window.require;e.config(t.config),e(["vs/editor/editor.main"],function(n){var r=n.m||n;vu(r),t.resolve(r)},function(n){t.reject(n)})}function vu(t){Os().monaco||Yo({monaco:t})}function xv(){return Os(function(t){var e=t.monaco;return e})}var al=new Promise(function(t,e){return Yo({resolve:t,reject:e})}),xu={config:gv,init:pv,__getMonacoInstance:xv},wv={wrapper:{display:"flex",position:"relative",textAlign:"initial"},fullWidth:{width:"100%"},hide:{display:"none"}},ll=wv,kv={container:{display:"flex",height:"100%",width:"100%",justifyContent:"center",alignItems:"center"}},Cv=kv;function Sv({children:t}){return v.createElement("div",{style:Cv.container},t)}var _v=Sv,Ev=_v;function Mv({width:t,height:e,isEditorReady:n,loading:r,_ref:s,className:o,wrapperProps:i}){return v.createElement("section",{style:{...ll.wrapper,width:t,height:e},...i},!n&&v.createElement(Ev,null,r),v.createElement("div",{ref:s,style:{...ll.fullWidth,...!n&&ll.hide},className:o}))}var Nv=Mv,wu=v.memo(Nv);function Ov(t){v.useEffect(t,[])}var ku=Ov;function Tv(t,e,n=!0){let r=v.useRef(!0);v.useEffect(r.current||!n?()=>{r.current=!1}:t,e)}var on=Tv;function Ts(){}function Vr(t,e,n,r){return Av(t,r)||Dv(t,e,n,r)}function Av(t,e){return t.editor.getModel(Cu(t,e))}function Dv(t,e,n,r){return t.editor.createModel(e,n,r?Cu(t,r):void 0)}function Cu(t,e){return t.Uri.parse(e)}function Iv({original:t,modified:e,language:n,originalLanguage:r,modifiedLanguage:s,originalModelPath:o,modifiedModelPath:i,keepCurrentOriginalModel:a=!1,keepCurrentModifiedModel:l=!1,theme:c="light",loading:d="Loading...",options:u={},height:f="100%",width:g="100%",className:m,wrapperProps:p={},beforeMount:y=Ts,onMount:b=Ts}){let[_,x]=v.useState(!1),[E,C]=v.useState(!0),k=v.useRef(null),N=v.useRef(null),S=v.useRef(null),w=v.useRef(b),M=v.useRef(y),T=v.useRef(!1);ku(()=>{let A=xu.init();return A.then(D=>(N.current=D)&&C(!1)).catch(D=>D?.type!=="cancelation"&&console.error("Monaco initialization: error:",D)),()=>k.current?$():A.cancel()}),on(()=>{if(k.current&&N.current){let A=k.current.getOriginalEditor(),D=Vr(N.current,t||"",r||n||"text",o||"");D!==A.getModel()&&A.setModel(D)}},[o],_),on(()=>{if(k.current&&N.current){let A=k.current.getModifiedEditor(),D=Vr(N.current,e||"",s||n||"text",i||"");D!==A.getModel()&&A.setModel(D)}},[i],_),on(()=>{let A=k.current.getModifiedEditor();A.getOption(N.current.editor.EditorOption.readOnly)?A.setValue(e||""):e!==A.getValue()&&(A.executeEdits("",[{range:A.getModel().getFullModelRange(),text:e||"",forceMoveMarkers:!0}]),A.pushUndoStop())},[e],_),on(()=>{k.current?.getModel()?.original.setValue(t||"")},[t],_),on(()=>{let{original:A,modified:D}=k.current.getModel();N.current.editor.setModelLanguage(A,r||n||"text"),N.current.editor.setModelLanguage(D,s||n||"text")},[n,r,s],_),on(()=>{N.current?.editor.setTheme(c)},[c],_),on(()=>{k.current?.updateOptions(u)},[u],_);let P=v.useCallback(()=>{if(!N.current)return;M.current(N.current);let A=Vr(N.current,t||"",r||n||"text",o||""),D=Vr(N.current,e||"",s||n||"text",i||"");k.current?.setModel({original:A,modified:D})},[n,e,s,t,r,o,i]),L=v.useCallback(()=>{!T.current&&S.current&&(k.current=N.current.editor.createDiffEditor(S.current,{automaticLayout:!0,...u}),P(),N.current?.editor.setTheme(c),x(!0),T.current=!0)},[u,c,P]);v.useEffect(()=>{_&&w.current(k.current,N.current)},[_]),v.useEffect(()=>{!E&&!_&&L()},[E,_,L]);function $(){let A=k.current?.getModel();a||A?.original?.d
Line truncated
|
||||
`},Nu=Mv(Av)(Mu),Dv={config:Tv},Iv=function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return function(s){return n.reduceRight(function(o,i){return i(o)},s)}};function Tu(e,t){return Object.keys(t).forEach(function(n){t[n]instanceof Object&&e[n]&&Object.assign(t[n],Tu(e[n],t[n]))}),Su(Su({},e),t)}var Lv={type:"cancelation",msg:"operation is manually canceled"};function gl(e){var t=!1,n=new Promise(function(r,s){e.then(function(o){return t?s(Lv):r(o)}),e.catch(s)});return n.cancel=function(){return t=!0},n}var Pv=["monaco"],$v=_v.create({config:Ev,isInitialized:!1,resolve:null,reject:null,monaco:null}),Ou=av($v,2),Bs=Ou[0],ti=Ou[1];function Fv(e){var t=Dv.config(e),n=t.monaco,r=ov(t,Pv);ti(function(s){return{config:Tu(s.config,r),monaco:n}})}function zv(){var e=Bs(function(t){var n=t.monaco,r=t.isInitialized,s=t.resolve;return{monaco:n,isInitialized:r,resolve:s}});if(!e.isInitialized){if(ti({isInitialized:!0}),e.monaco)return e.resolve(e.monaco),gl(pl);if(window.monaco&&window.monaco.editor)return Au(window.monaco),e.resolve(window.monaco),gl(pl);Iv(Vv,Uv)(Bv)}return gl(pl)}function Vv(e){return document.body.appendChild(e)}function Hv(e){var t=document.createElement("script");return e&&(t.src=e),t}function Uv(e){var t=Bs(function(r){var s=r.config,o=r.reject;return{config:s,reject:o}}),n=Hv("".concat(t.config.paths.vs,"/loader.js"));return n.onload=function(){return e()},n.onerror=t.reject,n}function Bv(){var e=Bs(function(n){var r=n.config,s=n.resolve,o=n.reject;return{config:r,resolve:s,reject:o}}),t=window.require;t.config(e.config),t(["vs/editor/editor.main"],function(n){var r=n.m||n;Au(r),e.resolve(r)},function(n){e.reject(n)})}function Au(e){Bs().monaco||ti({monaco:e})}function jv(){return Bs(function(e){var t=e.monaco;return t})}var pl=new Promise(function(e,t){return ti({resolve:e,reject:t})}),Du={config:Fv,init:zv,__getMonacoInstance:jv},Wv={wrapper:{display:"flex",position:"relative",textAlign:"initial"},fullWidth:{width:"100%"},hide:{display:"none"}},ml=Wv,Gv={container:{display:"flex",height:"100%",width:"100%",justifyContent:"center",alignItems:"center"}},Yv=Gv;function Xv({children:e}){return v.createElement("div",{style:Yv.container},e)}var Kv=Xv,qv=Kv;function Zv({width:e,height:t,isEditorReady:n,loading:r,_ref:s,className:o,wrapperProps:i}){return v.createElement("section",{style:{...ml.wrapper,width:e,height:t},...i},!n&&v.createElement(qv,null,r),v.createElement("div",{ref:s,style:{...ml.fullWidth,...!n&&ml.hide},className:o}))}var Qv=Zv,Iu=v.memo(Qv);function Jv(e){v.useEffect(e,[])}var Lu=Jv;function Rv(e,t,n=!0){let r=v.useRef(!0);v.useEffect(r.current||!n?()=>{r.current=!1}:e,t)}var cn=Rv;function js(){}function ns(e,t,n,r){return ex(e,r)||tx(e,t,n,r)}function ex(e,t){return e.editor.getModel(Pu(e,t))}function tx(e,t,n,r){return e.editor.createModel(t,n,r?Pu(e,r):void 0)}function Pu(e,t){return e.Uri.parse(t)}function nx({original:e,modified:t,language:n,originalLanguage:r,modifiedLanguage:s,originalModelPath:o,modifiedModelPath:i,keepCurrentOriginalModel:a=!1,keepCurrentModifiedModel:l=!1,theme:c="light",loading:d="Loading...",options:u={},height:f="100%",width:g="100%",className:m,wrapperProps:p={},beforeMount:y=js,onMount:b=js}){let[_,x]=v.useState(!1),[M,C]=v.useState(!0),k=v.useRef(null),N=v.useRef(null),S=v.useRef(null),w=v.useRef(b),E=v.useRef(y),D=v.useRef(!1);Lu(()=>{let A=Du.init();return A.then(O=>(N.current=O)&&C(!1)).catch(O=>O?.type!=="cancelation"&&console.error("Monaco initialization: error:",O)),()=>k.current?$():A.cancel()}),cn(()=>{if(k.current&&N.current){let A=k.current.getOriginalEditor(),O=ns(N.current,e||"",r||n||"text",o||"");O!==A.getModel()&&A.setModel(O)}},[o],_),cn(()=>{if(k.current&&N.current){let A=k.current.getModifiedEditor(),O=ns(N.current,t||"",s||n||"text",i||"");O!==A.getModel()&&A.setModel(O)}},[i],_),cn(()=>{let A=k.current.getModifiedEditor();A.getOption(N.current.editor.EditorOption.readOnly)?A.setValue(t||""):t!==A.getValue()&&(A.executeEdits("",[{range:A.getModel().getFullModelRange(),text:t||"",forceMoveMarkers:!0}]),A.pushUndoStop())},[t],_),cn(()=>{k.current?.getModel()?.original.setValue(e||"")},[e],_),cn(()=>{let{original:A,modified:O}=k.current.getModel();N.current.editor.setModelLanguage(A,r||n||"text"),N.current.editor.setModelLanguage(O,s||n||"text")},[n,r,s],_),cn(()=>{N.current?.editor.setTheme(c)},[c],_),cn(()=>{k.current?.updateOptions(u)},[u],_);let P=v.useCallback(()=>{if(!N.current)return;E.current(N.current);let A=ns(N.current,e||"",r||n||"text",o||""),O=ns(N.current,t||"",s||n||"text",i||"");k.current?.setModel({original:A,modified:O})},[n,t,s,e,r,o,i]),L=v.useCallback(()=>{!D.current&&S.current&&(k.current=N.current.editor.createDiffEditor(S.current,{automaticLayout:!0,...u}),P(),N.current?.editor.setTheme(c),x(!0),D.current=!0)},[u,c,P]);v.useEffect(()=>{_&&w.current(k.current,N.current)},[_]),v.useEffect(()=>{!M&&!_&&L()},[M,_,L]);function $(){let A=k.current?.getModel();a||A?.original?.d
Line truncated
|
||||
p-3 rounded-lg border cursor-pointer transition-colors
|
||||
${e?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 hover:border-gray-400 dark:border-gray-600 dark:hover:border-gray-500"}
|
||||
`,onClick:r,children:h.jsxs(I,{className:"flex justify-between items-start",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(Q,{className:"font-semibold text-gray-900 dark:text-gray-100",children:["버전 ",t.version]}),h.jsx(Q,{className:"ml-2 text-sm text-gray-500 dark:text-gray-400",children:Bv(t.created_at)})]}),h.jsxs(I,{className:"flex gap-3 text-sm",children:[h.jsxs(Q,{className:"text-green-600 dark:text-green-400",children:["+",t.changes_summary.added]}),h.jsxs(Q,{className:"text-red-600 dark:text-red-400",children:["-",t.changes_summary.removed]})]})]})})});Su.displayName="VersionListItem";const _u=({versions:t,selectedId:e,onSelect:n})=>t.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"버전이 없습니다."}):h.jsx(Ia,{role:"list",className:"space-y-2",children:t.map(r=>h.jsx(Su,{version:r,isSelected:e===r.id,onSelect:n},r.id))}),jv=({layoutId:t,versions:e,onRestore:n})=>{const[r,s]=v.useState(),o=l=>{s(l)},i=()=>{r!==void 0&&n(r)},a=r===void 0;return h.jsxs(I,{className:"space-y-4",role:"region","aria-label":"레이아웃 히스토리 패널",children:[h.jsx(I,{className:"border border-gray-300 dark:border-gray-600 rounded-lg p-4",children:h.jsx(_u,{versions:e,selectedId:r,onSelect:o})}),h.jsx(I,{className:"flex justify-end",children:h.jsx(oe,{variant:"primary",disabled:a,onClick:i,"aria-label":"선택한 버전으로 복원",children:"복원"})})]})},Wv=({emailLabel:t="Email",passwordLabel:e="Password",submitButtonText:n="Sign In",processingText:r="Processing...",emailPlaceholder:s="Email",passwordPlaceholder:o="Password",forgotPasswordText:i="Forgot your password?",forgotPasswordUrl:a="/admin/password/reset",emailRequiredError:l="Please enter your email.",emailInvalidError:c="Please enter a valid email address.",passwordRequiredError:d="Please enter your password.",onSubmit:u,loadingActions:f={},isLoading:g=!1,disabled:m=!1,apiError:p="",className:y="",style:b})=>{const[_,x]=v.useState(""),[E,C]=v.useState(""),[k,N]=v.useState({}),S=p,w=Object.values(f).some($=>$===!0),M=g||w,T=m||M,P=()=>{const $={},A=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;return!_||_.trim()===""?$.email=l:A.test(_)||($.email=c),(!E||E.trim()==="")&&($.password=d),N($),Object.keys($).length===0},L=async $=>{if(!P()){$.preventDefault();return}N({}),u?await u($):$.preventDefault()};return h.jsx(I,{className:`w-full ${y}`,style:b,children:h.jsxs(Pa,{onSubmit:L,className:"space-y-6",noValidate:!0,children:[S&&h.jsx(I,{className:"p-4 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800",children:h.jsx(yt,{className:"text-sm text-red-600 dark:text-red-400",children:S})}),h.jsxs(I,{children:[h.jsx(sn,{htmlFor:"email",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:t}),h.jsx(kt,{id:"email",name:"email",type:"email",value:_,onChange:$=>x($.target.value),placeholder:s,disabled:T,className:`
|
||||
${t?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 hover:border-gray-400 dark:border-gray-600 dark:hover:border-gray-500"}
|
||||
`,onClick:r,children:h.jsxs(I,{className:"flex justify-between items-start",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(Q,{className:"font-semibold text-gray-900 dark:text-gray-100",children:["버전 ",e.version]}),h.jsx(Q,{className:"ml-2 text-sm text-gray-500 dark:text-gray-400",children:ux(e.created_at)})]}),h.jsxs(I,{className:"flex gap-3 text-sm",children:[h.jsxs(Q,{className:"text-green-600 dark:text-green-400",children:["+",e.changes_summary.added]}),h.jsxs(Q,{className:"text-red-600 dark:text-red-400",children:["-",e.changes_summary.removed]})]})]})})});$u.displayName="VersionListItem";const Fu=({versions:e,selectedId:t,onSelect:n})=>e.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"버전이 없습니다."}):h.jsx(ja,{role:"list",className:"space-y-2",children:e.map(r=>h.jsx($u,{version:r,isSelected:t===r.id,onSelect:n},r.id))}),fx=({layoutId:e,versions:t,onRestore:n})=>{const[r,s]=v.useState(),o=l=>{s(l)},i=()=>{r!==void 0&&n(r)},a=r===void 0;return h.jsxs(I,{className:"space-y-4",role:"region","aria-label":"레이아웃 히스토리 패널",children:[h.jsx(I,{className:"border border-gray-300 dark:border-gray-600 rounded-lg p-4",children:h.jsx(Fu,{versions:t,selectedId:r,onSelect:o})}),h.jsx(I,{className:"flex justify-end",children:h.jsx(ae,{variant:"primary",disabled:a,onClick:i,"aria-label":"선택한 버전으로 복원",children:"복원"})})]})},hx=({emailLabel:e="Email",passwordLabel:t="Password",submitButtonText:n="Sign In",processingText:r="Processing...",emailPlaceholder:s="Email",passwordPlaceholder:o="Password",forgotPasswordText:i="Forgot your password?",forgotPasswordUrl:a="/admin/password/reset",emailRequiredError:l="Please enter your email.",emailInvalidError:c="Please enter a valid email address.",passwordRequiredError:d="Please enter your password.",onSubmit:u,loadingActions:f={},isLoading:g=!1,disabled:m=!1,apiError:p="",className:y="",style:b})=>{const[_,x]=v.useState(""),[M,C]=v.useState(""),[k,N]=v.useState({}),S=p,w=Object.values(f).some($=>$===!0),E=g||w,D=m||E,P=()=>{const $={},A=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;return!_||_.trim()===""?$.email=l:A.test(_)||($.email=c),(!M||M.trim()==="")&&($.password=d),N($),Object.keys($).length===0},L=async $=>{if(!P()){$.preventDefault();return}N({}),u?await u($):$.preventDefault()};return h.jsx(I,{className:`w-full ${y}`,style:b,children:h.jsxs(Ga,{onSubmit:L,className:"space-y-6",noValidate:!0,children:[S&&h.jsx(I,{className:"p-4 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800",children:h.jsx(vt,{className:"text-sm text-red-600 dark:text-red-400",children:S})}),h.jsxs(I,{children:[h.jsx(ln,{htmlFor:"email",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:e}),h.jsx(St,{id:"email",name:"email",type:"email",value:_,onChange:$=>x($.target.value),placeholder:s,disabled:D,className:`
|
||||
w-full px-4 py-3 rounded-lg border
|
||||
${k.email?"border-red-500 focus:border-red-500 focus:ring-red-500":"border-gray-300 dark:border-gray-600 focus:border-blue-500 focus:ring-blue-500"}
|
||||
bg-white dark:bg-gray-800
|
||||
@@ -127,7 +128,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
||||
focus:outline-none focus:ring-2
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
transition-colors duration-200
|
||||
`}),k.email&&h.jsx(yt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.email})]}),h.jsxs(I,{children:[h.jsx(sn,{htmlFor:"password",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:e}),h.jsx(kt,{id:"password",name:"password",type:"password",value:E,onChange:$=>C($.target.value),placeholder:o,disabled:T,className:`
|
||||
`}),k.email&&h.jsx(vt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.email})]}),h.jsxs(I,{children:[h.jsx(ln,{htmlFor:"password",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:t}),h.jsx(St,{id:"password",name:"password",type:"password",value:M,onChange:$=>C($.target.value),placeholder:o,disabled:D,className:`
|
||||
w-full px-4 py-3 rounded-lg border
|
||||
${k.password?"border-red-500 focus:border-red-500 focus:ring-red-500":"border-gray-300 dark:border-gray-600 focus:border-blue-500 focus:ring-blue-500"}
|
||||
bg-white dark:bg-gray-800
|
||||
@@ -136,7 +137,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
||||
focus:outline-none focus:ring-2
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
transition-colors duration-200
|
||||
`}),k.password&&h.jsx(yt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.password})]}),a&&h.jsx(I,{className:"flex items-center justify-end",children:h.jsx(Lr,{href:a,className:"text-sm text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300 transition-colors duration-200",children:i})}),h.jsx(oe,{type:"submit",disabled:T,className:`
|
||||
`}),k.password&&h.jsx(vt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.password})]}),a&&h.jsx(I,{className:"flex items-center justify-end",children:h.jsx(Yr,{href:a,className:"text-sm text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300 transition-colors duration-200",children:i})}),h.jsx(ae,{type:"submit",disabled:D,className:`
|
||||
w-full px-4 py-3 rounded-lg font-medium
|
||||
bg-blue-600 hover:bg-blue-700
|
||||
dark:bg-blue-500 dark:hover:bg-blue-600
|
||||
@@ -146,16 +147,16 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
transition-colors duration-200
|
||||
flex items-center justify-center
|
||||
`,children:M?h.jsxs(h.Fragment,{children:[h.jsxs(Un,{className:"animate-spin -ml-1 mr-3 h-5 w-5 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[h.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),h.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),r]}):n})]})})},Eu=t=>t==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":t,cl=t=>{const e=Eu(t);document.documentElement.setAttribute("data-theme",e),e==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")},Gv=()=>{const t=localStorage.getItem("g7_color_scheme");return t&&["auto","light","dark"].includes(t)?t:"auto"},Yv=({onThemeChange:t,className:e="",autoText:n="System",lightText:r="Light",darkText:s="Dark",id:o,editorAttrs:i})=>{const a=Gv(),[l,c]=v.useState(a),[d,u]=v.useState(!1),f=v.useRef(null);v.useEffect(()=>{cl(l)},[]),v.useEffect(()=>{if(l!=="auto")return;const p=window.matchMedia("(prefers-color-scheme: dark)"),y=()=>{cl("auto")};return p.addEventListener("change",y),()=>p.removeEventListener("change",y)},[l]),v.useEffect(()=>{const p=y=>{f.current&&!f.current.contains(y.target)&&u(!1)};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[]);const g=p=>{c(p),localStorage.setItem("g7_color_scheme",p),cl(p),u(!1),t?.(p)},m=()=>Eu(l)==="dark"?ce.Moon:ce.Sun;return h.jsxs(I,{ref:f,className:`relative ${e}`,id:o,...i,children:[h.jsx(oe,{onClick:()=>u(!d),className:"w-9 h-9 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors","aria-label":"Toggle theme",children:h.jsx(re,{name:m(),className:"text-sm text-slate-500 dark:text-slate-400"})}),d&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsxs(I,{className:"py-2",children:[h.jsxs(oe,{onClick:()=>g("auto"),className:`
|
||||
`,children:E?h.jsxs(h.Fragment,{children:[h.jsxs(Zn,{className:"animate-spin -ml-1 mr-3 h-5 w-5 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[h.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),h.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),r]}):n})]})})},zu=e=>e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e,bl=e=>{const t=zu(e);document.documentElement.setAttribute("data-theme",t),t==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")},gx=()=>{const e=localStorage.getItem("g7_color_scheme");return e&&["auto","light","dark"].includes(e)?e:"auto"},px=({onThemeChange:e,className:t="",autoText:n="System",lightText:r="Light",darkText:s="Dark",id:o,editorAttrs:i})=>{const a=gx(),[l,c]=v.useState(a),[d,u]=v.useState(!1),f=v.useRef(null);v.useEffect(()=>{bl(l)},[]),v.useEffect(()=>{if(l!=="auto")return;const p=window.matchMedia("(prefers-color-scheme: dark)"),y=()=>{bl("auto")};return p.addEventListener("change",y),()=>p.removeEventListener("change",y)},[l]),v.useEffect(()=>{const p=y=>{f.current&&!f.current.contains(y.target)&&u(!1)};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[]);const g=p=>{c(p),localStorage.setItem("g7_color_scheme",p),bl(p),u(!1),e?.(p)},m=()=>zu(l)==="dark"?de.Moon:de.Sun;return h.jsxs(I,{ref:f,className:`relative ${t}`,id:o,...i,children:[h.jsx(ae,{onClick:()=>u(!d),className:"w-9 h-9 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors","aria-label":"Toggle theme",children:h.jsx(oe,{name:m(),className:"text-sm text-slate-500 dark:text-slate-400"})}),d&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsxs(I,{className:"py-2",children:[h.jsxs(ae,{onClick:()=>g("auto"),className:`
|
||||
w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors
|
||||
${l==="auto"?"bg-gray-50 dark:bg-gray-700":""}
|
||||
`,children:[h.jsx(re,{name:ce.Settings,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:n}),l==="auto"&&h.jsx(re,{name:ce.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(oe,{onClick:()=>g("light"),className:`
|
||||
`,children:[h.jsx(oe,{name:de.Settings,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:n}),l==="auto"&&h.jsx(oe,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(ae,{onClick:()=>g("light"),className:`
|
||||
w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors
|
||||
${l==="light"?"bg-gray-50 dark:bg-gray-700":""}
|
||||
`,children:[h.jsx(re,{name:ce.Sun,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:r}),l==="light"&&h.jsx(re,{name:ce.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(oe,{onClick:()=>g("dark"),className:`
|
||||
`,children:[h.jsx(oe,{name:de.Sun,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:r}),l==="light"&&h.jsx(oe,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(ae,{onClick:()=>g("dark"),className:`
|
||||
w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors
|
||||
${l==="dark"?"bg-gray-50 dark:bg-gray-700":""}
|
||||
`,children:[h.jsx(re,{name:ce.Moon,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:s}),l==="dark"&&h.jsx(re,{name:ce.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]})]})})]})},Mu=window.G7Core?.createLogger?.("Comp:ColumnSelector")??{log:(...t)=>console.log("[Comp:ColumnSelector]",...t),warn:(...t)=>console.warn("[Comp:ColumnSelector]",...t),error:(...t)=>console.error("[Comp:ColumnSelector]",...t)},Nu=(t,e)=>window.G7Core?.t?.(t,e)??t,Xv=()=>window.G7Core,Kv=({id:t,columns:e,visibleColumns:n,onColumnVisibilityChange:r,triggerLabel:s,triggerIconName:o=ce.Sliders,position:i="right",className:a="",style:l,requiredText:c,menuTitle:d,menuDescription:u,defaultColumns:f,triggerClassName:g,labelClassName:m,dynamicColumns:p,dynamicColumnsInsertAfter:y,editorAttrs:b})=>{const _=s??Nu("common.column_selector"),x=c??Nu("common.required"),E=v.useMemo(()=>{if(!p||!Array.isArray(p)||p.length===0)return e;if(y){const V=e.findIndex(K=>K.field===y);if(V!==-1)return[...e.slice(0,V+1),...p,...e.slice(V+1)]}return[...e,...p]},[e,p,y]),[C,k]=v.useState(!1),N=v.useRef(null),S=v.useRef(null),[w,M]=v.useState(!1),[T,P]=v.useState(i),L=v.useMemo(()=>{try{const V=window.G7Core?.AuthManager?.getInstance();return V?V.getUser()?.uuid:void 0}catch{return}},[]),$=t?`g7_columns_${t}${L!==void 0?`_${L}`:""}`:null,A=()=>{if(!$)return null;try{const V=localStorage.getItem($);if(V){const K=JSON.parse(V);if(Array.isArray(K)&&K.length>0)return K}}catch(V){Mu.warn("Failed to load from localStorage:",V)}return null},D=V=>{if($)try{localStorage.setItem($,JSON.stringify(V))}catch(K){Mu.warn("Failed to save to localStorage:",K)}},O=()=>{const V=A();return V||(n&&n.length>0?n:f&&f.length>0?f:E.filter(K=>K.visible!==!1).map(K=>K.field))},[z,H]=v.useState(O);v.useEffect(()=>{w||(r&&z.length>0&&r(z),M(!0))},[w,r,z]),v.useEffect(()=>{w&&n&&n.length>0&&(A()||H(n))},[n,w]);const j=v.useRef([]),W=v.useRef(null);v.useEffect(()=>{if(w&&p&&p.length>0){const V=p.map(Z=>Z.field);if(W.current===null){const Z=A();if(W.current=Z!==null,Z!==null){j.current=V;return}}const K=j.current,X=V.filter(Z=>!K.includes(Z));if(X.length>0){const Z=n&&n.length>0?n:z,ee=X.filter(ne=>!Z.includes(ne));if(ee.length>0){const ne=[...Z,...ee];H(ne),D(ne),r?.(ne)}}j.current=V}},[p,w]);const U=n&&n.length>0?n:z;v.useEffect(()=>{const V=K=>{N.current&&!N.current.contains(K.target)&&k(!1)};return C&&document.addEventListener("mousedown",V),()=>{document.removeEventListener("mousedown",V)}},[C]),v.useEffect(()=>{if(C&&S.current&&N.current){const V=S.current.getBoundingClientRect(),K=N.current.getBoundingClientRect(),X=window.innerWidth,Z=V.width||256;i==="right"?K.right-Z<0?P("left"):P("right"):K.left+Z>X?P("right"):P("left")}},[C,i]);const B=V=>{if(E.find(Z=>Z.field===V)?.required)return;const X=U.includes(V)?U.filter(Z=>Z!==V):[...U,V];H(X),D(X),r?.(X)},G=T==="left"?"left-0":"right-0";return h.jsxs(I,{ref:N,className:`relative inline-block ${a}`,style:l,id:t,...b,children:[h.jsxs(oe,{onClick:()=>k(!C),className:Xv()?.style?.mergeClasses?.("flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",g)??`flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors ${g??""}`,children:[h.jsx(re,{name:o,className:"w-5 h-5"}),_&&h.jsx(Q,{className:`${m||"hidden sm:inline"} text-sm`,children:_})]}),C&&h.jsxs(I,{ref:S,className:`absolute ${G} z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg`,children:[(d||u)&&h.jsxs(I,{className:"px-4 pt-3 pb-2 border-b border-gray-200 dark:border-gray-700",children:[d&&h.jsx(Q,{className:"block text-sm font-medium text-gray-900 dark:text-white",children:d}),u&&h.jsx(Q,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-1",children:u})]}),h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:E.map(V=>{const K=U.includes(V.field),X=V.required===!0;return h.jsxs(I,{onClick:()=>!X&&B(V.field),className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${X?"cursor-default":"cursor-pointer"}`,children:[h.jsx(vn,{checked:K,disabled:X,readOnly:!0,className:"h-4 w-4 text-blue-600 rounded border-gray-300 dark:border-gray-600 pointer-events-none"}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:V.header}),X&&x&&h.jsx(Q,{className:"ml-auto text-xs text-gray-400 dark:text-gray-500",children:x})]},V.field)})})]})]})},qv=window.G7Core?.createLogger?.("Comp:PageTransition")??{log:(...t)=>console.log("[Comp:PageTransition]",...t),warn:(...t)=>console.warn("[Comp:PageTransition]",...t),error:(...t)=>console.error("[Comp:PageTransition]",...t)},Zv=({className:t="",style:e})=>{const[n,r]=v.useState(!1);return v.useEffect(()=>{c
Line truncated
|
||||
`,children:[h.jsx(oe,{name:de.Moon,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:s}),l==="dark"&&h.jsx(oe,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]})]})})]})},Vu=window.G7Core?.createLogger?.("Comp:ColumnSelector")??{log:(...e)=>console.log("[Comp:ColumnSelector]",...e),warn:(...e)=>console.warn("[Comp:ColumnSelector]",...e),error:(...e)=>console.error("[Comp:ColumnSelector]",...e)},Hu=(e,t)=>window.G7Core?.t?.(e,t)??e,mx=()=>window.G7Core,bx=({id:e,columns:t,visibleColumns:n,onColumnVisibilityChange:r,triggerLabel:s,triggerIconName:o=de.Sliders,position:i="right",className:a="",style:l,requiredText:c,menuTitle:d,menuDescription:u,defaultColumns:f,triggerClassName:g,labelClassName:m,dynamicColumns:p,dynamicColumnsInsertAfter:y,editorAttrs:b})=>{const _=s??Hu("common.column_selector"),x=c??Hu("common.required"),M=v.useMemo(()=>{if(!p||!Array.isArray(p)||p.length===0)return t;if(y){const V=t.findIndex(X=>X.field===y);if(V!==-1)return[...t.slice(0,V+1),...p,...t.slice(V+1)]}return[...t,...p]},[t,p,y]),[C,k]=v.useState(!1),N=v.useRef(null),S=v.useRef(null),[w,E]=v.useState(!1),[D,P]=v.useState(i),L=v.useMemo(()=>{try{const V=window.G7Core?.AuthManager?.getInstance();return V?V.getUser()?.uuid:void 0}catch{return}},[]),$=e?`g7_columns_${e}${L!==void 0?`_${L}`:""}`:null,A=()=>{if(!$)return null;try{const V=localStorage.getItem($);if(V){const X=JSON.parse(V);if(Array.isArray(X)&&X.length>0)return X}}catch(V){Vu.warn("Failed to load from localStorage:",V)}return null},O=V=>{if($)try{localStorage.setItem($,JSON.stringify(V))}catch(X){Vu.warn("Failed to save to localStorage:",X)}},T=()=>{const V=A();return V||(n&&n.length>0?n:f&&f.length>0?f:M.filter(X=>X.visible!==!1).map(X=>X.field))},[z,H]=v.useState(T);v.useEffect(()=>{w||(r&&z.length>0&&r(z),E(!0))},[w,r,z]),v.useEffect(()=>{w&&n&&n.length>0&&(A()||H(n))},[n,w]);const j=v.useRef([]),G=v.useRef(null);v.useEffect(()=>{if(w&&p&&p.length>0){const V=p.map(q=>q.field);if(G.current===null){const q=A();if(G.current=q!==null,q!==null){j.current=V;return}}const X=j.current,K=V.filter(q=>!X.includes(q));if(K.length>0){const q=n&&n.length>0?n:z,ne=K.filter(ee=>!q.includes(ee));if(ne.length>0){const ee=[...q,...ne];H(ee),O(ee),r?.(ee)}}j.current=V}},[p,w]);const U=n&&n.length>0?n:z;v.useEffect(()=>{const V=X=>{N.current&&!N.current.contains(X.target)&&k(!1)};return C&&document.addEventListener("mousedown",V),()=>{document.removeEventListener("mousedown",V)}},[C]),v.useEffect(()=>{if(C&&S.current&&N.current){const V=S.current.getBoundingClientRect(),X=N.current.getBoundingClientRect(),K=window.innerWidth,q=V.width||256;i==="right"?X.right-q<0?P("left"):P("right"):X.left+q>K?P("right"):P("left")}},[C,i]);const W=V=>{if(M.find(q=>q.field===V)?.required)return;const K=U.includes(V)?U.filter(q=>q!==V):[...U,V];H(K),O(K),r?.(K)},Y=D==="left"?"left-0":"right-0";return h.jsxs(I,{ref:N,className:`relative inline-block ${a}`,style:l,id:e,...b,children:[h.jsxs(ae,{onClick:()=>k(!C),className:mx()?.style?.mergeClasses?.("flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",g)??`flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors ${g??""}`,children:[h.jsx(oe,{name:o,className:"w-5 h-5"}),_&&h.jsx(Q,{className:`${m||"hidden sm:inline"} text-sm`,children:_})]}),C&&h.jsxs(I,{ref:S,className:`absolute ${Y} z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg`,children:[(d||u)&&h.jsxs(I,{className:"px-4 pt-3 pb-2 border-b border-gray-200 dark:border-gray-700",children:[d&&h.jsx(Q,{className:"block text-sm font-medium text-gray-900 dark:text-white",children:d}),u&&h.jsx(Q,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-1",children:u})]}),h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:M.map(V=>{const X=U.includes(V.field),K=V.required===!0;return h.jsxs(I,{onClick:()=>!K&&W(V.field),className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${K?"cursor-default":"cursor-pointer"}`,children:[h.jsx(En,{checked:X,disabled:K,readOnly:!0,className:"h-4 w-4 text-blue-600 rounded border-gray-300 dark:border-gray-600 pointer-events-none"}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:V.header}),K&&x&&h.jsx(Q,{className:"ml-auto text-xs text-gray-400 dark:text-gray-500",children:x})]},V.field)})})]})]})},yx=window.G7Core?.createLogger?.("Comp:PageTransition")??{log:(...e)=>console.log("[Comp:PageTransition]",...e),warn:(...e)=>console.warn("[Comp:PageTransition]",...e),error:(...e)=>console.error("[Comp:PageTransition]",...e)},vx=({className:e="",style:t})=>{const[n,r]=v.useState(!1);return v.useEffect(()=>{c
Line truncated
|
||||
@keyframes loading {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
@@ -167,12 +168,12 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
`})]}):null},Qv=(t,e)=>window.G7Core?.t?.(t,e)??t,Jv=({options:t})=>{const e=document.documentElement.classList.contains("dark"),n=e?"rgb(17,24,39)":"rgb(249,250,251)",r=e?"#6b7280":"#9ca3af",s=e?"#9ca3af":"#6b7280";return h.jsxs("div",{style:{position:"absolute",inset:0,zIndex:2147483647,overflow:"hidden",background:n,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",paddingTop:"15%",gap:"12px"},children:[h.jsx("div",{style:{width:"32px",height:"32px",border:`3px solid ${r}`,borderTopColor:"transparent",borderRadius:"50%",animation:"g7-spin 0.8s linear infinite"}}),h.jsx("span",{style:{color:s,fontSize:"14px"},children:t?.text||Qv("nav.loading")})]})};function gr(t){"@babel/helpers - typeof";return gr=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},gr(t)}function Rv(t,e){if(gr(t)!="object"||!t)return t;var n=t[Symbol.toPrimitive];if(n!==void 0){var r=n.call(t,e);if(gr(r)!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(t)}function Ou(t){var e=Rv(t,"string");return gr(e)=="symbol"?e:e+""}function As(t,e,n){return(e=Ou(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function Tu(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(t,s).enumerable})),n.push.apply(n,r)}return n}function Me(t){for(var e=1;e<arguments.length;e++){var n=arguments[e]!=null?arguments[e]:{};e%2?Tu(Object(n),!0).forEach(function(r){As(t,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):Tu(Object(n)).forEach(function(r){Object.defineProperty(t,r,Object.getOwnPropertyDescriptor(n,r))})}return t}function ex(t){if(Array.isArray(t))return t}function tx(t,e){var n=t==null?null:typeof Symbol<"u"&&t[Symbol.iterator]||t["@@iterator"];if(n!=null){var r,s,o,i,a=[],l=!0,c=!1;try{if(o=(n=n.call(t)).next,e===0){if(Object(n)!==n)return;l=!1}else for(;!(l=(r=o.call(n)).done)&&(a.push(r.value),a.length!==e);l=!0);}catch(d){c=!0,s=d}finally{try{if(!l&&n.return!=null&&(i=n.return(),Object(i)!==i))return}finally{if(c)throw s}}return a}}function dl(t,e){(e==null||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n<e;n++)r[n]=t[n];return r}function Au(t,e){if(t){if(typeof t=="string")return dl(t,e);var n={}.toString.call(t).slice(8,-1);return n==="Object"&&t.constructor&&(n=t.constructor.name),n==="Map"||n==="Set"?Array.from(t):n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?dl(t,e):void 0}}function nx(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function jn(t,e){return ex(t)||tx(t,e)||Au(t,e)||nx()}function rx(t,e){if(t==null)return{};var n={};for(var r in t)if({}.hasOwnProperty.call(t,r)){if(e.indexOf(r)!==-1)continue;n[r]=t[r]}return n}function An(t,e){if(t==null)return{};var n,r,s=rx(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(r=0;r<o.length;r++)n=o[r],e.indexOf(n)===-1&&{}.propertyIsEnumerable.call(t,n)&&(s[n]=t[n])}return s}var sx=["defaultInputValue","defaultMenuIsOpen","defaultValue","inputValue","menuIsOpen","onChange","onInputChange","onMenuClose","onMenuOpen","value"];function Du(t){var e=t.defaultInputValue,n=e===void 0?"":e,r=t.defaultMenuIsOpen,s=r===void 0?!1:r,o=t.defaultValue,i=o===void 0?null:o,a=t.inputValue,l=t.menuIsOpen,c=t.onChange,d=t.onInputChange,u=t.onMenuClose,f=t.onMenuOpen,g=t.value,m=An(t,sx),p=v.useState(a!==void 0?a:n),y=jn(p,2),b=y[0],_=y[1],x=v.useState(l!==void 0?l:s),E=jn(x,2),C=E[0],k=E[1],N=v.useState(g!==void 0?g:i),S=jn(N,2),w=S[0],M=S[1],T=v.useCallback(function(z,H){typeof c=="function"&&c(z,H),M(z)},[c]),P=v.useCallback(function(z,H){var j;typeof d=="function"&&(j=d(z,H)),_(j!==void 0?j:z)},[d]),L=v.useCallback(function(){typeof f=="function"&&f(),k(!0)},[f]),$=v.useCallback(function(){typeof u=="function"&&u(),k(!1)},[u]),A=a!==void 0?a:b,D=l!==void 0?l:C,O=g!==void 0?g:w;return Me(Me({},m),{},{inputValue:A,menuIsOpen:D,onChange:T,onInputChange:P,onMenuClose:$,onMenuOpen:L,value:O})}function Ae(){return Ae=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)({}).hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t},Ae.apply(null,arguments)}function ox(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function Iu(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,Ou(r.key),r)}}function ix(t,e,n){return e&&Iu(t.prototype,e),n&&Iu(t,n),Object.defineProperty(t,"prototype",{writable:!1}),t}function ul(t,e){return ul=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(n,r){return n.__proto__=r,n},ul(t,e)}function ax(t,e){if(typeof e!="function"&&e!==null)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&ul(t,e)}function Ko(t){return Ko=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Ko(t)}function Lu(){try{var t=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(Lu=function(){return!!t})()}function lx(t){if(t===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function cx(t,e){if(e&&(gr(e)=="object"||typeof e=="function"))return e;if(e!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return lx(t)}function dx(t){var e=Lu();return function(){var n,r=Ko(t);if(e){var s=Ko(this).constructor;n=Reflect.construct(r,arguments,s)}else n=r.apply(this,arguments);return cx(this,n)}}function ux(t){if(Array.isArray(t))return dl(t)}function fx(t){if(typeof Symbol<"u"&&t[Symbol.iterator]!=null||t["@@iterator"]!=null)return Array.from(t)}function hx(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function Hr(t){return ux(t)||fx(t)||Au(t)||hx()}function gx(t){if(t.sheet)return t.sheet;for(var e=0;e<document.styleSheets.length;e++)if(document.styleSheets[e].ownerNode===t)return document.styleSheets[e]}function px(t){var e=document.createElement("style");return e.setAttribute("data-emotion",t.key),t.nonce!==void 0&&e.setAttribute("nonce",t.nonce),e.appendChild(document.createTextNode("")),e.setAttribute("data-s",""),e}var mx=(function(){function t(n){var r=this;this._insertTag=function(s){var o;r.tags.length===0?r.insertionPoint?o=r.insertionPoint.nextSibling:r.prepend?o=r.container.firstChild:o=r.before:o=r.tags[r.tags.length-1].nextSibling,r.container.insertBefore(s,o),r.tags.push(s)},this.isSpeedy=n.speedy===void 0?!0:n.speedy,this.tags=[],this.ctr=0,this.nonce=n.nonce,this.key=n.key,this.container=n.container,this.prepend=n.prepend,this.insertionPoint=n.insertionPoint,this.before=null}var e=t.prototype;return e.hydrate=function(r){r.forEach(this._insertTag)},e.insert=function(r){this.ctr%(this.isSpeedy?65e3:1)===0&&this._insertTag(px(this));var s=this.tags[this.tags.length-1];if(this.isSpeedy){var o=gx(s);try{o.insertRule(r,o.cssRules.length)}catch{}}else s.appendChild(document.createTextNode(r));this.ctr++},e.flush=function(){this.tags.forEach(function(r){var s;return(s=r.parentNode)==null?void 0:s.removeChild(r)}),this.tags=[],this.ctr=0},t})(),Ft="-ms-",qo="-moz-",et="-webkit-",Pu="comm",fl="rule",hl="decl",bx="@import",$u="@keyframes",yx="@layer",vx=Math.abs,Zo=String.fromCharCode,xx=Object.assign;function wx(t,e){return Pt(t,0)^45?(((e<<2^Pt(t,0))<<2^Pt(t,1))<<2^Pt(t,2))<<2^Pt(t,3):0}function Fu(t){return t.trim()}function kx(t,e){return(t=e.exec(t))?t[0]:t}function tt(t,e,n){return t.replace(e,n)}function gl(t,e){return t.indexOf(e)}function Pt(t,e){return t.charCodeAt(e)|0}function Ds(t,e,n){return t.slice(e,n)}function Dn(t){return t.length}function pl(t){return t.length}function Qo(t,e){return e.push(t),t}function Cx(t,e){return t.map(e).join("")}var Jo=1,Ur=1,zu=0,Qt=0,Ct=0,Br="";function Ro(t,e,n,r,s,o,i){return{value:t,root:e,parent:n,type:r,props:s,children:o,line:Jo,column:Ur,length:i,return:""}}function Is(t,e){return xx(Ro("",null,null,"",null,null,0),t,{length:-t.length},e)}function Sx(){return Ct}function _x(){return Ct=Qt>0?Pt(Br,--Qt):0,Ur--,Ct===10&&(Ur=1,Jo--),Ct}function an(){return Ct=Qt<zu?Pt(Br,Qt++):0,Ur++,Ct===10&&(Ur=1,Jo++),Ct}function In(){return Pt(Br,Qt)}function ei(){return Qt}function Ls(t,e){return Ds(Br,t,e)}function Ps(t){switch(t){case 0:case 9:case 10:case 13:case 32:return 5;case 33:case 43:case 44:case 47:case 62:case 64:case 126:case 59:case 123:case 125:return 4;case 58:return 3;case 34:case 39:case 40:case 91:return 2;case 41:case 93:return 1}return 0}function Vu(t){return Jo=Ur=1,zu=Dn(Br=t),Qt=0,[]}function Hu(t){return Br="",t}function ti(t){return Fu(Ls(Qt-1,ml(t===91?t+2:t===40?t+1:t)))}function Ex(t){for(;(Ct=In())&&Ct<33;)an();return Ps(t)>2||Ps(Ct)>3?"":" "}function Mx(t,e){for(;--e&&an()&&!(Ct<48||Ct>102||Ct>57&&Ct<65||Ct>70&&Ct<97););return Ls(t,ei()+(e<6&&In()==32&&an()==32))}function ml(t){for(;an();)switch(Ct){case t:return Qt;case 34:case 39:t!==34&&t!==39&&ml(Ct);break;case 40:t===41&&ml(t);break;case 92:an();break}return Qt}function Nx(t,e){for(;an()&&t+Ct!==57;)if(t+Ct===84&&In()===47)break;return"/*"+Ls(e,Qt-1)+"*"+Zo(t===47?t:an())}function Ox(t){for(;!Ps(In());)an();return Ls(t,Qt)}function Tx(t){return Hu(ni("",null,null,null,[""],t=Vu(t),0,[0],t))}function ni(t,e,n,r,s,o,i,a,l){for(var c=0,d=0,u=i,f=0,g=0,m=0,p=1,y=1,b=1,_=0,x="",E=s,C=o,k=r,N=x;y;)switch(m=_,_=an()){case 40:if(m!=108&&Pt(N,u-1)==58){gl(N+=tt(ti(_),"&","&\f"),"&\f")!=-1&&(b=-1);break}case 34:case 39:case 91:N+=ti(_);break;case 9:case 10:case 13:case 32:N+=Ex(m);break;case 92:N+=Mx(ei()-1,7);continue;case 47:switch(In()){case 42:case 47:Qo(Ax(Nx(an(),ei()),e,n),l);break;default:N+="/"}break;case 123*p:a[c++]=Dn(N)*b;case 125*p:case 59:case 0:switch(_){case 0:case 125:y=0;case 59+d:b==-1&&(N=tt(N,/\f/g,"")),g>0&&Dn(N)-u&&Qo(g>32?Bu(N+";",r,n,u-1):Bu(tt(N," ","")+";",r,n,u-2),l);break;case 59:N+=";";default:if(Qo(k=Uu(N,e,n,c,d,s,a,x,E=[],C=[],u),o),_===123)if(d===0)ni(N,e,k,k,E,o,u,a,C);else switch(f===99&&Pt(N,3)===110?100:f){case 100:case 108:case 109:case 115:ni(t,k,k,r&&Qo(Uu(t,k,k,0,0,s,a,x,s,E=[],u),C),s,C,u,a,r?E:C);break;default:ni(N,k,k,k,[""],C,0,a,C)}}c=d=g=0,p=b=1,x=N="",u=i;break;case 58:u=1+Dn(N),g=m;default:if(p<1){if(_==123)--p;else if(_==125&&p++==0&&_x()==125)continue}switch(N+=Zo(_),_*p){case 38:b=d>0?1:(N+="\f",-1);break;case 44:a[c++]=(Dn(N)-1)*b,b=1;break;case 64:In()===45&&(N+=ti(an())),f=In(),d=u=Dn(x=N+=Ox(ei())),_++;break;case 45:m===45&&Dn(N)==2&&(p=0)}}return o}function Uu(t,e,n,r,s,o,i,a,l,c,d){for(var u=s-1,f=s===0?o:[""],g=pl(f),m=0,p=0,y=0;m<r;++m)for(var b=0,_=Ds(t,u+1,u=vx(p=i[m])),x=t;b<g;++b)(x=Fu(p>0?f[b]+" "+_:tt(_,/&\f/g,f[b])))&&(l[y++]=x);retu
Line truncated
|
||||
`})]}):null},xx=(e,t)=>window.G7Core?.t?.(e,t)??e,wx=({options:e})=>{const t=document.documentElement.classList.contains("dark"),n=t?"rgb(17,24,39)":"rgb(249,250,251)",r=t?"#6b7280":"#9ca3af",s=t?"#9ca3af":"#6b7280";return h.jsxs("div",{style:{position:"absolute",inset:0,zIndex:2147483647,overflow:"hidden",background:n,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",paddingTop:"15%",gap:"12px"},children:[h.jsx("div",{style:{width:"32px",height:"32px",border:`3px solid ${r}`,borderTopColor:"transparent",borderRadius:"50%",animation:"g7-spin 0.8s linear infinite"}}),h.jsx("span",{style:{color:s,fontSize:"14px"},children:e?.text||xx("nav.loading")})]})};function Cr(e){"@babel/helpers - typeof";return Cr=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Cr(e)}function kx(e,t){if(Cr(e)!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(Cr(r)!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}function Uu(e){var t=kx(e,"string");return Cr(t)=="symbol"?t:t+""}function Ws(e,t,n){return(t=Uu(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function Bu(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(e,s).enumerable})),n.push.apply(n,r)}return n}function Ee(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]!=null?arguments[t]:{};t%2?Bu(Object(n),!0).forEach(function(r){Ws(e,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Bu(Object(n)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(n,r))})}return e}function Cx(e){if(Array.isArray(e))return e}function Sx(e,t){var n=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(n!=null){var r,s,o,i,a=[],l=!0,c=!1;try{if(o=(n=n.call(e)).next,t===0){if(Object(n)!==n)return;l=!1}else for(;!(l=(r=o.call(n)).done)&&(a.push(r.value),a.length!==t);l=!0);}catch(d){c=!0,s=d}finally{try{if(!l&&n.return!=null&&(i=n.return(),Object(i)!==i))return}finally{if(c)throw s}}return a}}function yl(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function ju(e,t){if(e){if(typeof e=="string")return yl(e,t);var n={}.toString.call(e).slice(8,-1);return n==="Object"&&e.constructor&&(n=e.constructor.name),n==="Map"||n==="Set"?Array.from(e):n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?yl(e,t):void 0}}function _x(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function Qn(e,t){return Cx(e)||Sx(e,t)||ju(e,t)||_x()}function Ex(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}function Vn(e,t){if(e==null)return{};var n,r,s=Ex(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(s[n]=e[n])}return s}var Mx=["defaultInputValue","defaultMenuIsOpen","defaultValue","inputValue","menuIsOpen","onChange","onInputChange","onMenuClose","onMenuOpen","value"];function Wu(e){var t=e.defaultInputValue,n=t===void 0?"":t,r=e.defaultMenuIsOpen,s=r===void 0?!1:r,o=e.defaultValue,i=o===void 0?null:o,a=e.inputValue,l=e.menuIsOpen,c=e.onChange,d=e.onInputChange,u=e.onMenuClose,f=e.onMenuOpen,g=e.value,m=Vn(e,Mx),p=v.useState(a!==void 0?a:n),y=Qn(p,2),b=y[0],_=y[1],x=v.useState(l!==void 0?l:s),M=Qn(x,2),C=M[0],k=M[1],N=v.useState(g!==void 0?g:i),S=Qn(N,2),w=S[0],E=S[1],D=v.useCallback(function(z,H){typeof c=="function"&&c(z,H),E(z)},[c]),P=v.useCallback(function(z,H){var j;typeof d=="function"&&(j=d(z,H)),_(j!==void 0?j:z)},[d]),L=v.useCallback(function(){typeof f=="function"&&f(),k(!0)},[f]),$=v.useCallback(function(){typeof u=="function"&&u(),k(!1)},[u]),A=a!==void 0?a:b,O=l!==void 0?l:C,T=g!==void 0?g:w;return Ee(Ee({},m),{},{inputValue:A,menuIsOpen:O,onChange:D,onInputChange:P,onMenuClose:$,onMenuOpen:L,value:T})}function Oe(){return Oe=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)({}).hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},Oe.apply(null,arguments)}function Nx(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Gu(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,Uu(r.key),r)}}function Tx(e,t,n){return t&&Gu(e.prototype,t),n&&Gu(e,n),Object.defineProperty(e,"prototype",{writable:!1}),e}function vl(e,t){return vl=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(n,r){return n.__proto__=r,n},vl(e,t)}function Ox(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&vl(e,t)}function ri(e){return ri=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},ri(e)}function Yu(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(Yu=function(){return!!e})()}function Ax(e){if(e===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Dx(e,t){if(t&&(Cr(t)=="object"||typeof t=="function"))return t;if(t!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return Ax(e)}function Ix(e){var t=Yu();return function(){var n,r=ri(e);if(t){var s=ri(this).constructor;n=Reflect.construct(r,arguments,s)}else n=r.apply(this,arguments);return Dx(this,n)}}function Lx(e){if(Array.isArray(e))return yl(e)}function Px(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function $x(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function rs(e){return Lx(e)||Px(e)||ju(e)||$x()}function Fx(e){if(e.sheet)return e.sheet;for(var t=0;t<document.styleSheets.length;t++)if(document.styleSheets[t].ownerNode===e)return document.styleSheets[t]}function zx(e){var t=document.createElement("style");return t.setAttribute("data-emotion",e.key),e.nonce!==void 0&&t.setAttribute("nonce",e.nonce),t.appendChild(document.createTextNode("")),t.setAttribute("data-s",""),t}var Vx=(function(){function e(n){var r=this;this._insertTag=function(s){var o;r.tags.length===0?r.insertionPoint?o=r.insertionPoint.nextSibling:r.prepend?o=r.container.firstChild:o=r.before:o=r.tags[r.tags.length-1].nextSibling,r.container.insertBefore(s,o),r.tags.push(s)},this.isSpeedy=n.speedy===void 0?!0:n.speedy,this.tags=[],this.ctr=0,this.nonce=n.nonce,this.key=n.key,this.container=n.container,this.prepend=n.prepend,this.insertionPoint=n.insertionPoint,this.before=null}var t=e.prototype;return t.hydrate=function(r){r.forEach(this._insertTag)},t.insert=function(r){this.ctr%(this.isSpeedy?65e3:1)===0&&this._insertTag(zx(this));var s=this.tags[this.tags.length-1];if(this.isSpeedy){var o=Fx(s);try{o.insertRule(r,o.cssRules.length)}catch{}}else s.appendChild(document.createTextNode(r));this.ctr++},t.flush=function(){this.tags.forEach(function(r){var s;return(s=r.parentNode)==null?void 0:s.removeChild(r)}),this.tags=[],this.ctr=0},e})(),jt="-ms-",si="-moz-",nt="-webkit-",Xu="comm",xl="rule",wl="decl",Hx="@import",Ku="@keyframes",Ux="@layer",Bx=Math.abs,oi=String.fromCharCode,jx=Object.assign;function Wx(e,t){return Vt(e,0)^45?(((t<<2^Vt(e,0))<<2^Vt(e,1))<<2^Vt(e,2))<<2^Vt(e,3):0}function qu(e){return e.trim()}function Gx(e,t){return(e=t.exec(e))?e[0]:e}function rt(e,t,n){return e.replace(t,n)}function kl(e,t){return e.indexOf(t)}function Vt(e,t){return e.charCodeAt(t)|0}function Gs(e,t,n){return e.slice(t,n)}function Hn(e){return e.length}function Cl(e){return e.length}function ii(e,t){return t.push(e),e}function Yx(e,t){return e.map(t).join("")}var ai=1,ss=1,Zu=0,tn=0,Et=0,os="";function li(e,t,n,r,s,o,i){return{value:e,root:t,parent:n,type:r,props:s,children:o,line:ai,column:ss,length:i,return:""}}function Ys(e,t){return jx(li("",null,null,"",null,null,0),e,{length:-e.length},t)}function Xx(){return Et}function Kx(){return Et=tn>0?Vt(os,--tn):0,ss--,Et===10&&(ss=1,ai--),Et}function dn(){return Et=tn<Zu?Vt(os,tn++):0,ss++,Et===10&&(ss=1,ai++),Et}function Un(){return Vt(os,tn)}function ci(){return tn}function Xs(e,t){return Gs(os,e,t)}function Ks(e){switch(e){case 0:case 9:case 10:case 13:case 32:return 5;case 33:case 43:case 44:case 47:case 62:case 64:case 126:case 59:case 123:case 125:return 4;case 58:return 3;case 34:case 39:case 40:case 91:return 2;case 41:case 93:return 1}return 0}function Qu(e){return ai=ss=1,Zu=Hn(os=e),tn=0,[]}function Ju(e){return os="",e}function di(e){return qu(Xs(tn-1,Sl(e===91?e+2:e===40?e+1:e)))}function qx(e){for(;(Et=Un())&&Et<33;)dn();return Ks(e)>2||Ks(Et)>3?"":" "}function Zx(e,t){for(;--t&&dn()&&!(Et<48||Et>102||Et>57&&Et<65||Et>70&&Et<97););return Xs(e,ci()+(t<6&&Un()==32&&dn()==32))}function Sl(e){for(;dn();)switch(Et){case e:return tn;case 34:case 39:e!==34&&e!==39&&Sl(Et);break;case 40:e===41&&Sl(e);break;case 92:dn();break}return tn}function Qx(e,t){for(;dn()&&e+Et!==57;)if(e+Et===84&&Un()===47)break;return"/*"+Xs(t,tn-1)+"*"+oi(e===47?e:dn())}function Jx(e){for(;!Ks(Un());)dn();return Xs(e,tn)}function Rx(e){return Ju(ui("",null,null,null,[""],e=Qu(e),0,[0],e))}function ui(e,t,n,r,s,o,i,a,l){for(var c=0,d=0,u=i,f=0,g=0,m=0,p=1,y=1,b=1,_=0,x="",M=s,C=o,k=r,N=x;y;)switch(m=_,_=dn()){case 40:if(m!=108&&Vt(N,u-1)==58){kl(N+=rt(di(_),"&","&\f"),"&\f")!=-1&&(b=-1);break}case 34:case 39:case 91:N+=di(_);break;case 9:case 10:case 13:case 32:N+=qx(m);break;case 92:N+=Zx(ci()-1,7);continue;case 47:switch(Un()){case 42:case 47:ii(ew(Qx(dn(),ci()),t,n),l);break;default:N+="/"}break;case 123*p:a[c++]=Hn(N)*b;case 125*p:case 59:case 0:switch(_){case 0:case 125:y=0;case 59+d:b==-1&&(N=rt(N,/\f/g,"")),g>0&&Hn(N)-u&&ii(g>32?ef(N+";",r,n,u-1):ef(rt(N," ","")+";",r,n,u-2),l);break;case 59:N+=";";default:if(ii(k=Ru(N,t,n,c,d,s,a,x,M=[],C=[],u),o),_===123)if(d===0)ui(N,t,k,k,M,o,u,a,C);else switch(f===99&&Vt(N,3)===110?100:f){case 100:case 108:case 109:case 115:ui(e,k,k,r&&ii(Ru(e,k,k,0,0,s,a,x,s,M=[],u),C),s,C,u,a,r?M:C);break;default:ui(N,k,k,k,[""],C,0,a,C)}}c=d=g=0,p=b=1,x=N="",u=i;break;case 58:u=1+Hn(N),g=m;default:if(p<1){if(_==123)--p;else if(_==125&&p++==0&&Kx()==125)continue}switch(N+=oi(_),_*p){case 38:b=d>0?1:(N+="\f",-1);break;case 44:a[c++]=(Hn(N)-1)*b,b=1;break;case 64:Un()===45&&(N+=di(dn())),f=Un(),d=u=Hn(x=N+=Jx(ci())),_++;break;case 45:m===45&&Hn(N)==2&&(p=0)}}return o}function Ru(e,t,n,r,s,o,i,a,l,c,d){for(var u=s-1,f=s===0?o:[""],g=Cl(f),m=0,p=0,y=0;m<r;++m)for(var b=0,_=Gs(e,u+1,u=Bx(p=i[m])),x=e;b<g;++b)(x=qu(p>0?f[b]+" "+_:rt(_,/&\f/g,f[b])))&&(l[y++]=x);retu
Line truncated
|
||||
0%, 80%, 100% { opacity: 0; }
|
||||
40% { opacity: 1; }
|
||||
`]))),m1=function(e,n){var r=e.isFocused,s=e.size,o=e.theme,i=o.colors,a=o.spacing.baseUnit;return Me({label:"loadingIndicator",display:"flex",transition:"color 150ms",alignSelf:"center",fontSize:s,lineHeight:1,marginRight:s,textAlign:"center",verticalAlign:"middle"},n?{}:{color:r?i.neutral60:i.neutral20,padding:a*2})},Tl=function(e){var n=e.delay,r=e.offset;return _e("span",{css:kl({animation:"".concat(p1," 1s ease-in-out ").concat(n,"ms infinite;"),backgroundColor:"currentColor",borderRadius:"1em",display:"inline-block",marginLeft:r?"1em":void 0,height:"1em",verticalAlign:"top",width:"1em"},"","")})},b1=function(e){var n=e.innerProps,r=e.isRtl,s=e.size,o=s===void 0?4:s,i=An(e,a1);return _e("div",Ae({},vt(Me(Me({},i),{},{innerProps:n,isRtl:r,size:o}),"loadingIndicator",{indicator:!0,"loading-indicator":!0}),n),_e(Tl,{delay:0,offset:r}),_e(Tl,{delay:160,offset:!0}),_e(Tl,{delay:320,offset:!r}))},y1=function(e,n){var r=e.isDisabled,s=e.isFocused,o=e.theme,i=o.colors,a=o.borderRadius,l=o.spacing;return Me({label:"control",alignItems:"center",cursor:"default",display:"flex",flexWrap:"wrap",justifyContent:"space-between",minHeight:l.controlHeight,outline:"0 !important",position:"relative",transition:"all 100ms"},n?{}:{backgroundColor:r?i.neutral5:i.neutral0,borderColor:r?i.neutral10:s?i.primary:i.neutral20,borderRadius:a,borderStyle:"solid",borderWidth:1,boxShadow:s?"0 0 0 1px ".concat(i.primary):void 0,"&:hover":{borderColor:s?i.primary:i.neutral30}})},v1=function(e){var n=e.children,r=e.isDisabled,s=e.isFocused,o=e.innerRef,i=e.innerProps,a=e.menuIsOpen;return _e("div",Ae({ref:o},vt(e,"control",{control:!0,"control--is-disabled":r,"control--is-focused":s,"control--menu-is-open":a}),i,{"aria-disabled":r||void 0}),n)},x1=v1,w1=["data"],k1=function(e,n){var r=e.theme.spacing;return n?{}:{paddingBottom:r.baseUnit*2,paddingTop:r.baseUnit*2}},C1=function(e){var n=e.children,r=e.cx,s=e.getStyles,o=e.getClassNames,i=e.Heading,a=e.headingProps,l=e.innerProps,c=e.label,d=e.theme,u=e.selectProps;return _e("div",Ae({},vt(e,"group",{group:!0}),l),_e(i,Ae({},a,{selectProps:u,theme:d,getStyles:s,getClassNames:o,cx:r}),c),_e("div",null,n))},S1=function(e,n){var r=e.theme,s=r.colors,o=r.spacing;return Me({label:"group",cursor:"default",display:"block"},n?{}:{color:s.neutral40,fontSize:"75%",fontWeight:500,marginBottom:"0.25em",paddingLeft:o.baseUnit*3,paddingRight:o.baseUnit*3,textTransform:"uppercase"})},_1=function(e){var n=uf(e);n.data;var r=An(n,w1);return _e("div",Ae({},vt(e,"groupHeading",{"group-heading":!0}),r))},E1=C1,M1=["innerRef","isDisabled","isHidden","inputClassName"],N1=function(e,n){var r=e.isDisabled,s=e.value,o=e.theme,i=o.spacing,a=o.colors;return Me(Me({visibility:r?"hidden":"visible",transform:s?"translateZ(0)":""},O1),n?{}:{margin:i.baseUnit/2,paddingBottom:i.baseUnit/2,paddingTop:i.baseUnit/2,color:a.neutral80})},Cf={gridArea:"1 / 2",font:"inherit",minWidth:"2px",border:0,margin:0,outline:0,padding:0},O1={flex:"1 1 auto",display:"inline-grid",gridArea:"1 / 1 / 2 / 3",gridTemplateColumns:"0 min-content","&:after":Me({content:'attr(data-value) " "',visibility:"hidden",whiteSpace:"pre"},Cf)},T1=function(e){return Me({label:"input",color:"inherit",background:0,opacity:e?0:1,width:"100%"},Cf)},A1=function(e){var n=e.cx,r=e.value,s=uf(e),o=s.innerRef,i=s.isDisabled,a=s.isHidden,l=s.inputClassName,c=An(s,M1);return _e("div",Ae({},vt(e,"input",{"input-container":!0}),{"data-value":r||""}),_e("input",Ae({className:n({input:!0},l),ref:o,style:T1(a),disabled:i},c)))},D1=A1,I1=function(e,n){var r=e.theme,s=r.spacing,o=r.borderRadius,i=r.colors;return Me({label:"multiValue",display:"flex",minWidth:0},n?{}:{backgroundColor:i.neutral10,borderRadius:o/2,margin:s.baseUnit/2})},L1=function(e,n){var r=e.theme,s=r.borderRadius,o=r.colors,i=e.cropWithEllipsis;return Me({overflow:"hidden",textOverflow:i||i===void 0?"ellipsis":void 0,whiteSpace:"nowrap"},n?{}:{borderRadius:s/2,color:o.neutral80,fontSize:"85%",padding:3,paddingLeft:6})},P1=function(e,n){var r=e.theme,s=r.spacing,o=r.borderRadius,i=r.colors,a=e.isFocused;return Me({alignItems:"center",display:"flex"},n?{}:{borderRadius:o/2,backgroundColor:a?i.dangerLight:void 0,paddingLeft:s.baseUnit,paddingRight:s.baseUnit,":hover":{backgroundColor:i.dangerLight,color:i.danger}})},Sf=function(e){var n=e.children,r=e.innerProps;return _e("div",r,n)},$1=Sf,F1=Sf;function z1(t){var e=t.children,n=t.innerProps;return _e("div",Ae({role:"button"},n),e||_e(Ol,{size:14}))}var V1=function(e){var n=e.children,r=e.components,s=e.data,o=e.innerProps,i=e.isDisabled,a=e.removeProps,l=e.selectProps,c=r.Container,d=r.Label,u=r.Remove;return _e(c,{data:s,innerProps:Me(Me({},vt(e,"multiValue",{"multi-value":!0,"multi-value--is-disabled":i})),o),selectProps:l},_e(d,{data:s,innerProps:Me({},vt(e,"multiValueLabel",{"multi-value__label":!0})),selectProps:l},n),_e(u,{data:s,innerProps:Me(Me({},vt(e,"multiValueRemove",{"multi-value__remove":!0})),{},{"aria-label":"Remove ".concat(n||"option")},a),s
Line truncated
|
||||
`]))),F1=function(t,n){var r=t.isFocused,s=t.size,o=t.theme,i=o.colors,a=o.spacing.baseUnit;return Ee({label:"loadingIndicator",display:"flex",transition:"color 150ms",alignSelf:"center",fontSize:s,lineHeight:1,marginRight:s,textAlign:"center",verticalAlign:"middle"},n?{}:{color:r?i.neutral60:i.neutral20,padding:a*2})},$l=function(t){var n=t.delay,r=t.offset;return Se("span",{css:Ol({animation:"".concat($1," 1s ease-in-out ").concat(n,"ms infinite;"),backgroundColor:"currentColor",borderRadius:"1em",display:"inline-block",marginLeft:r?"1em":void 0,height:"1em",verticalAlign:"top",width:"1em"},"","")})},z1=function(t){var n=t.innerProps,r=t.isRtl,s=t.size,o=s===void 0?4:s,i=Vn(t,N1);return Se("div",Oe({},wt(Ee(Ee({},i),{},{innerProps:n,isRtl:r,size:o}),"loadingIndicator",{indicator:!0,"loading-indicator":!0}),n),Se($l,{delay:0,offset:r}),Se($l,{delay:160,offset:!0}),Se($l,{delay:320,offset:!r}))},V1=function(t,n){var r=t.isDisabled,s=t.isFocused,o=t.theme,i=o.colors,a=o.borderRadius,l=o.spacing;return Ee({label:"control",alignItems:"center",cursor:"default",display:"flex",flexWrap:"wrap",justifyContent:"space-between",minHeight:l.controlHeight,outline:"0 !important",position:"relative",transition:"all 100ms"},n?{}:{backgroundColor:r?i.neutral5:i.neutral0,borderColor:r?i.neutral10:s?i.primary:i.neutral20,borderRadius:a,borderStyle:"solid",borderWidth:1,boxShadow:s?"0 0 0 1px ".concat(i.primary):void 0,"&:hover":{borderColor:s?i.primary:i.neutral30}})},H1=function(t){var n=t.children,r=t.isDisabled,s=t.isFocused,o=t.innerRef,i=t.innerProps,a=t.menuIsOpen;return Se("div",Oe({ref:o},wt(t,"control",{control:!0,"control--is-disabled":r,"control--is-focused":s,"control--menu-is-open":a}),i,{"aria-disabled":r||void 0}),n)},U1=H1,B1=["data"],j1=function(t,n){var r=t.theme.spacing;return n?{}:{paddingBottom:r.baseUnit*2,paddingTop:r.baseUnit*2}},W1=function(t){var n=t.children,r=t.cx,s=t.getStyles,o=t.getClassNames,i=t.Heading,a=t.headingProps,l=t.innerProps,c=t.label,d=t.theme,u=t.selectProps;return Se("div",Oe({},wt(t,"group",{group:!0}),l),Se(i,Oe({},a,{selectProps:u,theme:d,getStyles:s,getClassNames:o,cx:r}),c),Se("div",null,n))},G1=function(t,n){var r=t.theme,s=r.colors,o=r.spacing;return Ee({label:"group",cursor:"default",display:"block"},n?{}:{color:s.neutral40,fontSize:"75%",fontWeight:500,marginBottom:"0.25em",paddingLeft:o.baseUnit*3,paddingRight:o.baseUnit*3,textTransform:"uppercase"})},Y1=function(t){var n=Cf(t);n.data;var r=Vn(n,B1);return Se("div",Oe({},wt(t,"groupHeading",{"group-heading":!0}),r))},X1=W1,K1=["innerRef","isDisabled","isHidden","inputClassName"],q1=function(t,n){var r=t.isDisabled,s=t.value,o=t.theme,i=o.spacing,a=o.colors;return Ee(Ee({visibility:r?"hidden":"visible",transform:s?"translateZ(0)":""},Z1),n?{}:{margin:i.baseUnit/2,paddingBottom:i.baseUnit/2,paddingTop:i.baseUnit/2,color:a.neutral80})},Pf={gridArea:"1 / 2",font:"inherit",minWidth:"2px",border:0,margin:0,outline:0,padding:0},Z1={flex:"1 1 auto",display:"inline-grid",gridArea:"1 / 1 / 2 / 3",gridTemplateColumns:"0 min-content","&:after":Ee({content:'attr(data-value) " "',visibility:"hidden",whiteSpace:"pre"},Pf)},Q1=function(t){return Ee({label:"input",color:"inherit",background:0,opacity:t?0:1,width:"100%"},Pf)},J1=function(t){var n=t.cx,r=t.value,s=Cf(t),o=s.innerRef,i=s.isDisabled,a=s.isHidden,l=s.inputClassName,c=Vn(s,K1);return Se("div",Oe({},wt(t,"input",{"input-container":!0}),{"data-value":r||""}),Se("input",Oe({className:n({input:!0},l),ref:o,style:Q1(a),disabled:i},c)))},R1=J1,ek=function(t,n){var r=t.theme,s=r.spacing,o=r.borderRadius,i=r.colors;return Ee({label:"multiValue",display:"flex",minWidth:0},n?{}:{backgroundColor:i.neutral10,borderRadius:o/2,margin:s.baseUnit/2})},tk=function(t,n){var r=t.theme,s=r.borderRadius,o=r.colors,i=t.cropWithEllipsis;return Ee({overflow:"hidden",textOverflow:i||i===void 0?"ellipsis":void 0,whiteSpace:"nowrap"},n?{}:{borderRadius:s/2,color:o.neutral80,fontSize:"85%",padding:3,paddingLeft:6})},nk=function(t,n){var r=t.theme,s=r.spacing,o=r.borderRadius,i=r.colors,a=t.isFocused;return Ee({alignItems:"center",display:"flex"},n?{}:{borderRadius:o/2,backgroundColor:a?i.dangerLight:void 0,paddingLeft:s.baseUnit,paddingRight:s.baseUnit,":hover":{backgroundColor:i.dangerLight,color:i.danger}})},$f=function(t){var n=t.children,r=t.innerProps;return Se("div",r,n)},rk=$f,sk=$f;function ok(e){var t=e.children,n=e.innerProps;return Se("div",Oe({role:"button"},n),t||Se(Pl,{size:14}))}var ik=function(t){var n=t.children,r=t.components,s=t.data,o=t.innerProps,i=t.isDisabled,a=t.removeProps,l=t.selectProps,c=r.Container,d=r.Label,u=r.Remove;return Se(c,{data:s,innerProps:Ee(Ee({},wt(t,"multiValue",{"multi-value":!0,"multi-value--is-disabled":i})),o),selectProps:l},Se(d,{data:s,innerProps:Ee({},wt(t,"multiValueLabel",{"multi-value__label":!0})),selectProps:l},n),Se(u,{data:s,innerProps:Ee(Ee({},wt(t,"multiValueRemove",{"multi-value__remove":!0})),{},{"aria-label":"Remove ".concat(n||"option")},a),s
Line truncated
|
||||
px-4 py-2.5 bg-gray-100 dark:bg-gray-700
|
||||
border-0 rounded-xl
|
||||
text-gray-700 dark:text-gray-200 font-medium
|
||||
@@ -181,27 +182,27 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
||||
text-left cursor-pointer
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
min-w-[200px]
|
||||
`,"aria-haspopup":"listbox","aria-expanded":m,children:[h.jsxs(I,{className:"flex items-center gap-2",children:[c,h.jsx(Q,{className:"truncate",children:N})]}),h.jsx(Un,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${m?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),m&&h.jsxs(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden min-w-[280px]",role:"listbox",children:[h.jsx(I,{className:"p-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx("input",{id:E,type:"text",value:y,onChange:w,placeholder:s,className:"w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent focus:outline-none"})}),h.jsxs(I,{className:"max-h-80 overflow-auto",children:[P&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:a}),L&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:i}),$&&h.jsx(I,{className:"py-2",children:k.map(A=>{const D=T(A.value);return h.jsxs(oe,{type:"button",onClick:()=>!A.disabled&&M(A.value),disabled:A.disabled,className:`
|
||||
`,"aria-haspopup":"listbox","aria-expanded":m,children:[h.jsxs(I,{className:"flex items-center gap-2",children:[c,h.jsx(Q,{className:"truncate",children:N})]}),h.jsx(Zn,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${m?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),m&&h.jsxs(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden min-w-[280px]",role:"listbox",children:[h.jsx(I,{className:"p-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx("input",{id:M,type:"text",value:y,onChange:w,placeholder:s,className:"w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent focus:outline-none"})}),h.jsxs(I,{className:"max-h-80 overflow-auto",children:[P&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:a}),L&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:i}),$&&h.jsx(I,{className:"py-2",children:k.map(A=>{const O=D(A.value);return h.jsxs(ae,{type:"button",onClick:()=>!A.disabled&&E(A.value),disabled:A.disabled,className:`
|
||||
w-full px-4 py-3 text-left
|
||||
flex items-start gap-3
|
||||
hover:bg-gray-100 dark:hover:bg-gray-700
|
||||
transition-colors
|
||||
${A.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer"}
|
||||
`,role:"option","aria-selected":D,children:[h.jsx(I,{className:"w-5 h-5 flex-shrink-0 mt-0.5",children:D&&h.jsx(Un,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsx(I,{className:`font-medium ${D?"text-blue-600 dark:text-blue-400":"text-gray-900 dark:text-white"}`,children:A.label}),A.description&&h.jsx(I,{className:"text-sm text-gray-500 dark:text-gray-400 mt-0.5",children:A.description})]})]},A.value)})})]})]})]})},Vs={container:{base:"tag-select",disabled:"opacity-50 cursor-not-allowed"},tag:"tag",tagRemove:"tag-remove",placeholder:"tag-select-placeholder"},Xk=({options:t=[],value:e=[],onChange:n,placeholder:r="No items selected",disabled:s=!1,className:o="",id:i,editorAttrs:a})=>{const l=v.useCallback(f=>t.find(m=>String(m.value)===String(f))?.label||String(f),[t]),c=v.useCallback(f=>{if(s)return;const g=e.filter(m=>String(m)!==String(f));n?.(g)},[e,n,s]),d=v.useMemo(()=>e.map(f=>({value:f,label:l(f)})),[e,l]),u=`${Vs.container.base} ${s?Vs.container.disabled:""} ${o}`.trim();return h.jsx(I,{className:u,id:i,...a,children:d.length===0?h.jsx(Q,{className:Vs.placeholder,children:r}):d.map(f=>h.jsxs(Q,{className:Vs.tag,children:[f.label,!s&&h.jsx(Q,{className:Vs.tagRemove,onClick:()=>c(f.value),role:"button","aria-label":`Remove ${f.label}`,children:h.jsx(Un,{className:"w-3 h-3",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]},f.value))})};function Kk(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];return v.useMemo(()=>r=>{e.forEach(s=>s(r))},e)}const bi=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Yr(t){const e=Object.prototype.toString.call(t);return e==="[object Window]"||e==="[object global]"}function Ul(t){return"nodeType"in t}function Kt(t){var e,n;return t?Yr(t)?t:Ul(t)&&(e=(n=t.ownerDocument)==null?void 0:n.defaultView)!=null?e:window:window}function Bl(t){const{Document:e}=Kt(t);return t instanceof e}function Hs(t){return Yr(t)?!1:t instanceof Kt(t).HTMLElement}function rh(t){return t instanceof Kt(t).SVGElement}function Xr(t){return t?Yr(t)?t.document:Ul(t)?Bl(t)?t:Hs(t)||rh(t)?t.ownerDocument:document:document:document}const wn=bi?v.useLayoutEffect:v.useEffect;function yi(t){const e=v.useRef(t);return wn(()=>{e.current=t}),v.useCallback(function(){for(var n=arguments.length,r=new Array(n),s=0;s<n;s++)r[s]=arguments[s];return e.current==null?void 0:e.current(...r)},[])}function qk(){const t=v.useRef(null),e=v.useCallback((r,s)=>{t.current=setInterval(r,s)},[]),n=v.useCallback(()=>{t.current!==null&&(clearInterval(t.current),t.current=null)},[]);return[e,n]}function Us(t,e){e===void 0&&(e=[t]);const n=v.useRef(t);return wn(()=>{n.current!==t&&(n.current=t)},e),n}function Bs(t,e){const n=v.useRef();return v.useMemo(()=>{const r=t(n.current);return n.current=r,r},[...e])}function vi(t){const e=yi(t),n=v.useRef(null),r=v.useCallback(s=>{s!==n.current&&e?.(s,n.current),n.current=s},[]);return[n,r]}function xi(t){const e=v.useRef();return v.useEffect(()=>{e.current=t},[t]),e.current}let jl={};function js(t,e){return v.useMemo(()=>{if(e)return e;const n=jl[t]==null?0:jl[t]+1;return jl[t]=n,t+"-"+n},[t,e])}function sh(t){return function(e){for(var n=arguments.length,r=new Array(n>1?n-1:0),s=1;s<n;s++)r[s-1]=arguments[s];return r.reduce((o,i)=>{const a=Object.entries(i);for(const[l,c]of a){const d=o[l];d!=null&&(o[l]=d+t*c)}return o},{...e})}}const Kr=sh(1),Ws=sh(-1);function Zk(t){return"clientX"in t&&"clientY"in t}function wi(t){if(!t)return!1;const{KeyboardEvent:e}=Kt(t.target);return e&&t instanceof e}function Qk(t){if(!t)return!1;const{TouchEvent:e}=Kt(t.target);return e&&t instanceof e}function ki(t){if(Qk(t)){if(t.touches&&t.touches.length){const{clientX:e,clientY:n}=t.touches[0];return{x:e,y:n}}else if(t.changedTouches&&t.changedTouches.length){const{clientX:e,clientY:n}=t.changedTouches[0];return{x:e,y:n}}}return Zk(t)?{x:t.clientX,y:t.clientY}:null}const Pn=Object.freeze({Translate:{toString(t){if(!t)return;const{x:e,y:n}=t;return"translate3d("+(e?Math.round(e):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(t){if(!t)return;const{scaleX:e,scaleY:n}=t;return"scaleX("+e+") scaleY("+n+")"}},Transform:{toString(t){if(t)return[Pn.Translate.toString(t),Pn.Scale.toString(t)].join(" ")}},Transition:{toString(t){let{property:e,duration:n,easing:r}=t;return e+" "+n+"ms "+r}}}),oh="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function Jk(t){return t.matches(oh)?t:t.querySelector(oh)}const Rk={display:"none"};function eC(t){let{id:e,value:n}=t;return v.createElement("div",{id:e,s
Line truncated
|
||||
`,role:"option","aria-selected":O,children:[h.jsx(I,{className:"w-5 h-5 flex-shrink-0 mt-0.5",children:O&&h.jsx(Zn,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsx(I,{className:`font-medium ${O?"text-blue-600 dark:text-blue-400":"text-gray-900 dark:text-white"}`,children:A.label}),A.description&&h.jsx(I,{className:"text-sm text-gray-500 dark:text-gray-400 mt-0.5",children:A.description})]})]},A.value)})})]})]})]})},Js={container:{base:"tag-select",disabled:"opacity-50 cursor-not-allowed"},tag:"tag",tagRemove:"tag-remove",placeholder:"tag-select-placeholder"},gC=({options:e=[],value:t=[],onChange:n,placeholder:r="No items selected",disabled:s=!1,className:o="",id:i,editorAttrs:a})=>{const l=v.useCallback(f=>e.find(m=>String(m.value)===String(f))?.label||String(f),[e]),c=v.useCallback(f=>{if(s)return;const g=t.filter(m=>String(m)!==String(f));n?.(g)},[t,n,s]),d=v.useMemo(()=>t.map(f=>({value:f,label:l(f)})),[t,l]),u=`${Js.container.base} ${s?Js.container.disabled:""} ${o}`.trim();return h.jsx(I,{className:u,id:i,...a,children:d.length===0?h.jsx(Q,{className:Js.placeholder,children:r}):d.map(f=>h.jsxs(Q,{className:Js.tag,children:[f.label,!s&&h.jsx(Q,{className:Js.tagRemove,onClick:()=>c(f.value),role:"button","aria-label":`Remove ${f.label}`,children:h.jsx(Zn,{className:"w-3 h-3",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]},f.value))})};function pC(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return v.useMemo(()=>r=>{t.forEach(s=>s(r))},t)}const _i=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function cs(e){const t=Object.prototype.toString.call(e);return t==="[object Window]"||t==="[object global]"}function Xl(e){return"nodeType"in e}function Jt(e){var t,n;return e?cs(e)?e:Xl(e)&&(t=(n=e.ownerDocument)==null?void 0:n.defaultView)!=null?t:window:window}function Kl(e){const{Document:t}=Jt(e);return e instanceof t}function Rs(e){return cs(e)?!1:e instanceof Jt(e).HTMLElement}function ph(e){return e instanceof Jt(e).SVGElement}function ds(e){return e?cs(e)?e.document:Xl(e)?Kl(e)?e:Rs(e)||ph(e)?e.ownerDocument:document:document:document}const Mn=_i?v.useLayoutEffect:v.useEffect;function Ei(e){const t=v.useRef(e);return Mn(()=>{t.current=e}),v.useCallback(function(){for(var n=arguments.length,r=new Array(n),s=0;s<n;s++)r[s]=arguments[s];return t.current==null?void 0:t.current(...r)},[])}function mC(){const e=v.useRef(null),t=v.useCallback((r,s)=>{e.current=setInterval(r,s)},[]),n=v.useCallback(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[]);return[t,n]}function eo(e,t){t===void 0&&(t=[e]);const n=v.useRef(e);return Mn(()=>{n.current!==e&&(n.current=e)},t),n}function to(e,t){const n=v.useRef();return v.useMemo(()=>{const r=e(n.current);return n.current=r,r},[...t])}function Mi(e){const t=Ei(e),n=v.useRef(null),r=v.useCallback(s=>{s!==n.current&&t?.(s,n.current),n.current=s},[]);return[n,r]}function Ni(e){const t=v.useRef();return v.useEffect(()=>{t.current=e},[e]),t.current}let ql={};function no(e,t){return v.useMemo(()=>{if(t)return t;const n=ql[e]==null?0:ql[e]+1;return ql[e]=n,e+"-"+n},[e,t])}function mh(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),s=1;s<n;s++)r[s-1]=arguments[s];return r.reduce((o,i)=>{const a=Object.entries(i);for(const[l,c]of a){const d=o[l];d!=null&&(o[l]=d+e*c)}return o},{...t})}}const us=mh(1),ro=mh(-1);function bC(e){return"clientX"in e&&"clientY"in e}function Ti(e){if(!e)return!1;const{KeyboardEvent:t}=Jt(e.target);return t&&e instanceof t}function yC(e){if(!e)return!1;const{TouchEvent:t}=Jt(e.target);return t&&e instanceof t}function Oi(e){if(yC(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}else if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return bC(e)?{x:e.clientX,y:e.clientY}:null}const jn=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return"translate3d("+(t?Math.round(t):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return"scaleX("+t+") scaleY("+n+")"}},Transform:{toString(e){if(e)return[jn.Translate.toString(e),jn.Scale.toString(e)].join(" ")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+" "+n+"ms "+r}}}),bh="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function vC(e){return e.matches(bh)?e:e.querySelector(bh)}const xC={display:"none"};function wC(e){let{id:t,value:n}=e;return v.createElement("div",{id:t,s
Line truncated
|
||||
To pick up a draggable item, press the space bar.
|
||||
While dragging, use the arrow keys to move the item.
|
||||
Press space again to drop the item in its new position, or press escape to cancel.
|
||||
`},iC={onDragStart(t){let{active:e}=t;return"Picked up draggable item "+e.id+"."},onDragOver(t){let{active:e,over:n}=t;return n?"Draggable item "+e.id+" was moved over droppable area "+n.id+".":"Draggable item "+e.id+" is no longer over a droppable area."},onDragEnd(t){let{active:e,over:n}=t;return n?"Draggable item "+e.id+" was dropped over droppable area "+n.id:"Draggable item "+e.id+" was dropped."},onDragCancel(t){let{active:e}=t;return"Dragging was cancelled. Draggable item "+e.id+" was dropped."}};function aC(t){let{announcements:e=iC,container:n,hiddenTextDescribedById:r,screenReaderInstructions:s=oC}=t;const{announce:o,announcement:i}=nC(),a=js("DndLiveRegion"),[l,c]=v.useState(!1);if(v.useEffect(()=>{c(!0)},[]),rC(v.useMemo(()=>({onDragStart(u){let{active:f}=u;o(e.onDragStart({active:f}))},onDragMove(u){let{active:f,over:g}=u;e.onDragMove&&o(e.onDragMove({active:f,over:g}))},onDragOver(u){let{active:f,over:g}=u;o(e.onDragOver({active:f,over:g}))},onDragEnd(u){let{active:f,over:g}=u;o(e.onDragEnd({active:f,over:g}))},onDragCancel(u){let{active:f,over:g}=u;o(e.onDragCancel({active:f,over:g}))}}),[o,e])),!l)return null;const d=v.createElement(v.Fragment,null,v.createElement(eC,{id:r,value:s.draggable}),v.createElement(tC,{id:a,announcement:i}));return n?Tn.createPortal(d,n):d}var _t;(function(t){t.DragStart="dragStart",t.DragMove="dragMove",t.DragEnd="dragEnd",t.DragCancel="dragCancel",t.DragOver="dragOver",t.RegisterDroppable="registerDroppable",t.SetDroppableDisabled="setDroppableDisabled",t.UnregisterDroppable="unregisterDroppable"})(_t||(_t={}));function Ci(){}function qr(t,e){return v.useMemo(()=>({sensor:t,options:e??{}}),[t,e])}function Wl(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];return v.useMemo(()=>[...e].filter(r=>r!=null),[...e])}const kn=Object.freeze({x:0,y:0});function ah(t,e){return Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))}function lC(t,e){const n=ki(t);if(!n)return"0 0";const r={x:(n.x-e.left)/e.width*100,y:(n.y-e.top)/e.height*100};return r.x+"% "+r.y+"%"}function lh(t,e){let{data:{value:n}}=t,{data:{value:r}}=e;return n-r}function cC(t,e){let{data:{value:n}}=t,{data:{value:r}}=e;return r-n}function ch(t){let{left:e,top:n,height:r,width:s}=t;return[{x:e,y:n},{x:e+s,y:n},{x:e,y:n+r},{x:e+s,y:n+r}]}function dh(t,e){if(!t||t.length===0)return null;const[n]=t;return n[e]}function uh(t,e,n){return e===void 0&&(e=t.left),n===void 0&&(n=t.top),{x:e+t.width*.5,y:n+t.height*.5}}const Gl=t=>{let{collisionRect:e,droppableRects:n,droppableContainers:r}=t;const s=uh(e,e.left,e.top),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=ah(uh(l),s);o.push({id:a,data:{droppableContainer:i,value:c}})}}return o.sort(lh)},dC=t=>{let{collisionRect:e,droppableRects:n,droppableContainers:r}=t;const s=ch(e),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=ch(l),d=s.reduce((f,g,m)=>f+ah(c[m],g),0),u=Number((d/4).toFixed(4));o.push({id:a,data:{droppableContainer:i,value:u}})}}return o.sort(lh)};function uC(t,e){const n=Math.max(e.top,t.top),r=Math.max(e.left,t.left),s=Math.min(e.left+e.width,t.left+t.width),o=Math.min(e.top+e.height,t.top+t.height),i=s-r,a=o-n;if(r<s&&n<o){const l=e.width*e.height,c=t.width*t.height,d=i*a,u=d/(l+c-d);return Number(u.toFixed(4))}return 0}const fC=t=>{let{collisionRect:e,droppableRects:n,droppableContainers:r}=t;const s=[];for(const o of r){const{id:i}=o,a=n.get(i);if(a){const l=uC(a,e);l>0&&s.push({id:i,data:{droppableContainer:o,value:l}})}}return s.sort(cC)};function hC(t,e,n){return{...t,scaleX:e&&n?e.width/n.width:1,scaleY:e&&n?e.height/n.height:1}}function fh(t,e){return t&&e?{x:t.left-e.left,y:t.top-e.top}:kn}function gC(t){return function(n){for(var r=arguments.length,s=new Array(r>1?r-1:0),o=1;o<r;o++)s[o-1]=arguments[o];return s.reduce((i,a)=>({...i,top:i.top+t*a.y,bottom:i.bottom+t*a.y,left:i.left+t*a.x,right:i.right+t*a.x}),{...n})}}const pC=gC(1);function hh(t){if(t.startsWith("matrix3d(")){const e=t.slice(9,-1).split(/, /);return{x:+e[12],y:+e[13],scaleX:+e[0],scaleY:+e[5]}}else if(t.startsWith("matrix(")){const e=t.slice(7,-1).split(/, /);return{x:+e[4],y:+e[5],scaleX:+e[0],scaleY:+e[3]}}return null}function mC(t,e,n){const r=hh(e);if(!r)return t;const{scaleX:s,scaleY:o,x:i,y:a}=r,l=t.left-i-(1-s)*parseFloat(n),c=t.top-a-(1-o)*parseFloat(n.slice(n.indexOf(" ")+1)),d=s?t.width/s:t.width,u=o?t.height/o:t.height;return{width:d,height:u,top:c,right:l+d,bottom:c+u,left:l}}const bC={ignoreTransform:!1};function Zr(t,e){e===void 0&&(e=bC);let n=t.getBoundingClientRect();if(e.ignoreTransform){const{transform:c,transformOrigin:d}=Kt(t).getComputedStyle(t);c&&(n=mC(n,c,d))}const{top:r,left:s,width:o,height:i,bottom:a,right:l}=n;return{top:r,left:s,width:o,height:i,bottom:a,right:l}}function gh(t){return Zr(t,{ignoreTransform:!0})}function yC(t){const e=t.innerWidth,n=t.innerHeight;return{top:0,left:0,right:e,bottom:n,width:e,height:n}}function vC(t,e){return e===void 0&&(e=Kt(t).getComputedStyle(t)),e.posit
Line truncated
|
||||
`},MC={onDragStart(e){let{active:t}=e;return"Picked up draggable item "+t.id+"."},onDragOver(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was moved over droppable area "+n.id+".":"Draggable item "+t.id+" is no longer over a droppable area."},onDragEnd(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was dropped over droppable area "+n.id:"Draggable item "+t.id+" was dropped."},onDragCancel(e){let{active:t}=e;return"Dragging was cancelled. Draggable item "+t.id+" was dropped."}};function NC(e){let{announcements:t=MC,container:n,hiddenTextDescribedById:r,screenReaderInstructions:s=EC}=e;const{announce:o,announcement:i}=CC(),a=no("DndLiveRegion"),[l,c]=v.useState(!1);if(v.useEffect(()=>{c(!0)},[]),SC(v.useMemo(()=>({onDragStart(u){let{active:f}=u;o(t.onDragStart({active:f}))},onDragMove(u){let{active:f,over:g}=u;t.onDragMove&&o(t.onDragMove({active:f,over:g}))},onDragOver(u){let{active:f,over:g}=u;o(t.onDragOver({active:f,over:g}))},onDragEnd(u){let{active:f,over:g}=u;o(t.onDragEnd({active:f,over:g}))},onDragCancel(u){let{active:f,over:g}=u;o(t.onDragCancel({active:f,over:g}))}}),[o,t])),!l)return null;const d=v.createElement(v.Fragment,null,v.createElement(wC,{id:r,value:s.draggable}),v.createElement(kC,{id:a,announcement:i}));return n?$n.createPortal(d,n):d}var Nt;(function(e){e.DragStart="dragStart",e.DragMove="dragMove",e.DragEnd="dragEnd",e.DragCancel="dragCancel",e.DragOver="dragOver",e.RegisterDroppable="registerDroppable",e.SetDroppableDisabled="setDroppableDisabled",e.UnregisterDroppable="unregisterDroppable"})(Nt||(Nt={}));function Ai(){}function fs(e,t){return v.useMemo(()=>({sensor:e,options:t??{}}),[e,t])}function Zl(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return v.useMemo(()=>[...t].filter(r=>r!=null),[...t])}const Nn=Object.freeze({x:0,y:0});function vh(e,t){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function TC(e,t){const n=Oi(e);if(!n)return"0 0";const r={x:(n.x-t.left)/t.width*100,y:(n.y-t.top)/t.height*100};return r.x+"% "+r.y+"%"}function xh(e,t){let{data:{value:n}}=e,{data:{value:r}}=t;return n-r}function OC(e,t){let{data:{value:n}}=e,{data:{value:r}}=t;return r-n}function wh(e){let{left:t,top:n,height:r,width:s}=e;return[{x:t,y:n},{x:t+s,y:n},{x:t,y:n+r},{x:t+s,y:n+r}]}function kh(e,t){if(!e||e.length===0)return null;const[n]=e;return n[t]}function Ch(e,t,n){return t===void 0&&(t=e.left),n===void 0&&(n=e.top),{x:t+e.width*.5,y:n+e.height*.5}}const Ql=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const s=Ch(t,t.left,t.top),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=vh(Ch(l),s);o.push({id:a,data:{droppableContainer:i,value:c}})}}return o.sort(xh)},AC=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const s=wh(t),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=wh(l),d=s.reduce((f,g,m)=>f+vh(c[m],g),0),u=Number((d/4).toFixed(4));o.push({id:a,data:{droppableContainer:i,value:u}})}}return o.sort(xh)};function DC(e,t){const n=Math.max(t.top,e.top),r=Math.max(t.left,e.left),s=Math.min(t.left+t.width,e.left+e.width),o=Math.min(t.top+t.height,e.top+e.height),i=s-r,a=o-n;if(r<s&&n<o){const l=t.width*t.height,c=e.width*e.height,d=i*a,u=d/(l+c-d);return Number(u.toFixed(4))}return 0}const IC=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const s=[];for(const o of r){const{id:i}=o,a=n.get(i);if(a){const l=DC(a,t);l>0&&s.push({id:i,data:{droppableContainer:o,value:l}})}}return s.sort(OC)};function LC(e,t,n){return{...e,scaleX:t&&n?t.width/n.width:1,scaleY:t&&n?t.height/n.height:1}}function Sh(e,t){return e&&t?{x:e.left-t.left,y:e.top-t.top}:Nn}function PC(e){return function(n){for(var r=arguments.length,s=new Array(r>1?r-1:0),o=1;o<r;o++)s[o-1]=arguments[o];return s.reduce((i,a)=>({...i,top:i.top+e*a.y,bottom:i.bottom+e*a.y,left:i.left+e*a.x,right:i.right+e*a.x}),{...n})}}const $C=PC(1);function _h(e){if(e.startsWith("matrix3d(")){const t=e.slice(9,-1).split(/, /);return{x:+t[12],y:+t[13],scaleX:+t[0],scaleY:+t[5]}}else if(e.startsWith("matrix(")){const t=e.slice(7,-1).split(/, /);return{x:+t[4],y:+t[5],scaleX:+t[0],scaleY:+t[3]}}return null}function FC(e,t,n){const r=_h(t);if(!r)return e;const{scaleX:s,scaleY:o,x:i,y:a}=r,l=e.left-i-(1-s)*parseFloat(n),c=e.top-a-(1-o)*parseFloat(n.slice(n.indexOf(" ")+1)),d=s?e.width/s:e.width,u=o?e.height/o:e.height;return{width:d,height:u,top:c,right:l+d,bottom:c+u,left:l}}const zC={ignoreTransform:!1};function hs(e,t){t===void 0&&(t=zC);let n=e.getBoundingClientRect();if(t.ignoreTransform){const{transform:c,transformOrigin:d}=Jt(e).getComputedStyle(e);c&&(n=FC(n,c,d))}const{top:r,left:s,width:o,height:i,bottom:a,right:l}=n;return{top:r,left:s,width:o,height:i,bottom:a,right:l}}function Eh(e){return hs(e,{ignoreTransform:!0})}function VC(e){const t=e.innerWidth,n=e.innerHeight;return{top:0,left:0,right:t,bottom:n,width:t,height:n}}function HC(e,t){return t===void 0&&(t=Jt(e).getComputedStyle(e)),t.posit
Line truncated
|
||||
w-full flex items-center justify-between px-3 py-2
|
||||
bg-white dark:bg-gray-800
|
||||
border border-gray-300 dark:border-gray-600 rounded-lg
|
||||
text-left text-sm
|
||||
${a?"opacity-50 cursor-not-allowed":"hover:border-gray-400 dark:hover:border-gray-500"}
|
||||
focus:outline-none focus:ring-2 focus:ring-blue-500
|
||||
`,children:[h.jsx(I,{className:"flex items-center gap-2",children:y?h.jsxs(h.Fragment,{children:[h.jsx(re,{name:y.faIcon,className:"w-4 h-4 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"text-gray-900 dark:text-white",children:y.label})]}):h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:r})}),h.jsx(re,{name:"chevron-down",className:`w-4 h-4 text-gray-400 dark:text-gray-500 transition-transform ${u?"rotate-180":""}`})]}),l&&h.jsx("input",{type:"hidden",name:l,value:t||""}),u&&h.jsxs(I,{className:"absolute z-50 w-full mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg max-h-72 overflow-hidden",children:[h.jsx(I,{className:"p-2 border-b border-gray-200 dark:border-gray-700",children:h.jsx(kt,{type:"text",value:g,onChange:x=>m(x.target.value),placeholder:s,className:"w-full px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white",autoFocus:!0})}),h.jsx(I,{className:"max-h-56 overflow-y-auto",children:b.length>0?b.map(x=>h.jsxs(oe,{type:"button",onClick:()=>_(x),className:`
|
||||
`,children:[h.jsx(I,{className:"flex items-center gap-2",children:y?h.jsxs(h.Fragment,{children:[h.jsx(oe,{name:y.faIcon,className:"w-4 h-4 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"text-gray-900 dark:text-white",children:y.label})]}):h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:r})}),h.jsx(oe,{name:"chevron-down",className:`w-4 h-4 text-gray-400 dark:text-gray-500 transition-transform ${u?"rotate-180":""}`})]}),l&&h.jsx("input",{type:"hidden",name:l,value:e||""}),u&&h.jsxs(I,{className:"absolute z-50 w-full mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg max-h-72 overflow-hidden",children:[h.jsx(I,{className:"p-2 border-b border-gray-200 dark:border-gray-700",children:h.jsx(St,{type:"text",value:g,onChange:x=>m(x.target.value),placeholder:s,className:"w-full px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white",autoFocus:!0})}),h.jsx(I,{className:"max-h-56 overflow-y-auto",children:b.length>0?b.map(x=>h.jsxs(ae,{type:"button",onClick:()=>_(x),className:`
|
||||
w-full flex items-center gap-3 px-3 py-2 text-left text-sm
|
||||
${y?.faIcon===x.faIcon?"bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700"}
|
||||
`,children:[h.jsx(re,{name:x.faIcon,className:"w-4 h-4"}),h.jsx(Q,{children:x.label}),y?.faIcon===x.faIcon&&h.jsx(re,{name:"check",className:"w-4 h-4 ml-auto text-blue-600"})]},x.value)):h.jsx(I,{className:"px-3 py-4 text-sm text-gray-500 dark:text-gray-400 text-center",children:o})})]})]})},sc=({defaultOpen:t=!1,isOpen:e,onToggle:n,className:r="",style:s,children:o,disabled:i=!1,id:a,editorAttrs:l})=>{const[c,d]=v.useState(t),u=e!==void 0,f=u?e:c,g=v.useRef(null),[m,p]=v.useState(t?void 0:0),y=()=>{if(i)return;const _=!f;u||d(_),n?.(_)};v.useEffect(()=>{if(g.current)if(f){p(g.current.scrollHeight);const _=setTimeout(()=>{p(void 0)},300);return()=>clearTimeout(_)}else p(g.current.scrollHeight),requestAnimationFrame(()=>{p(0)})},[f]);const b={};if(v.Children.forEach(o,_=>{if(v.isValidElement(_)){const x=_.props?.slot;x==="trigger"?b.trigger=_:x==="content"&&(b.content=_)}}),!b.trigger&&!b.content){const _=v.Children.toArray(o);_.length>=2?(b.trigger=_[0],b.content=_.slice(1)):_.length===1&&(b.content=_[0])}return h.jsxs(I,{className:`border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden ${r}`,style:s,id:a,...l,children:[h.jsxs(oe,{type:"button",onClick:y,disabled:i,className:`w-full flex items-center justify-between p-3 text-left bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${i?"opacity-50 cursor-not-allowed":""} ${f?"border-b border-gray-200 dark:border-gray-700":""}`,"aria-expanded":f,children:[h.jsx(I,{className:"flex-1",children:b.trigger}),!b.trigger&&h.jsx("svg",{className:`w-5 h-5 text-gray-500 dark:text-gray-400 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),h.jsx(I,{ref:g,className:"overflow-hidden transition-all duration-300 ease-in-out",style:{height:m!==void 0?`${m}px`:"auto"},children:h.jsx(I,{className:"bg-white dark:bg-gray-800",children:b.content})})]})},Rs=window.G7Core,Jh=Rs?.useControllableState,Rh=(t,e)=>window.G7Core?.t?.(t,e)??t,zS={"":"common.scope_type_all_desc",self:"common.scope_type_self_desc",role:"common.scope_type_role_desc"},VS=({options:t,value:e,onChange:n,disabled:r=!1})=>{const[s,o]=v.useState(!1),[i,a]=v.useState({}),l=v.useRef(null),c=t.find(u=>u.value===e)??t[0];v.useEffect(()=>{if(!s)return;const u=g=>{l.current&&!l.current.contains(g.target)&&o(!1)},f=()=>{if(!l.current)return;const g=l.current.getBoundingClientRect();a({position:"fixed",top:g.bottom+4,right:window.innerWidth-g.right,zIndex:9999})};return document.addEventListener("mousedown",u),window.addEventListener("scroll",f,!0),window.addEventListener("resize",f),()=>{document.removeEventListener("mousedown",u),window.removeEventListener("scroll",f,!0),window.removeEventListener("resize",f)}},[s]);const d=v.useCallback(()=>{if(r||!l.current)return;const u=l.current.getBoundingClientRect();a({position:"fixed",top:u.bottom+4,right:window.innerWidth-u.right,zIndex:9999}),o(f=>!f)},[r]);return h.jsxs(I,{ref:l,className:"relative flex-shrink-0",children:[h.jsxs(I,{role:"button",tabIndex:0,onClick:u=>{u.stopPropagation(),u.preventDefault(),d()},onMouseDown:u=>u.stopPropagation(),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&(u.stopPropagation(),u.preventDefault(),d()),u.key==="Escape"&&o(!1)},className:`flex items-center gap-1 text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300 cursor-pointer select-none ${r?"opacity-50 cursor-not-allowed":"hover:border-blue-400 dark:hover:border-blue-500"}`,children:[h.jsx(Q,{children:c?.label??""}),h.jsx(re,{name:ce.ChevronDown,className:"w-3 h-3 text-gray-400 dark:text-gray-500"})]}),s&&h.jsx(I,{style:i,className:"w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1",children:t.map(u=>{const f=u.value??"",g=zS[f],m=g?Rh(g):"",p=u.value===e;return h.jsxs(I,{role:"option","aria-selected":p,onClick:y=>{y.stopPropagation(),y.preventDefault(),n(u.value),o(!1)},onMouseDown:y=>y.stopPropagation(),className:`px-3 py-2 cursor-pointer transition-colors ${p?"bg-blue-50 dark:bg-blue-900/30":"hover:bg-gray-50 dark:hover:bg-gray-700/50"}`,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[h.jsx(Q,{className:`text-xs font-medium ${p?"text-blue-700 dark:text-blue-300":"text-gray-900 dark:text-white"}`,children:u.label}),p&&h.jsx(re,{name:ce.Check,className:"w-3.5 h-3.5 text-blue-600 dark:text-blue-400"})]}),m&&h.jsx(Q,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-0.5",children:m})]},f||"__null__")})})]})},HS=(t,e)=>{if(t.length!==e.length)return!1;const n=[...t].sort((s,o)=>s.id-o.id),r=[...e].sort((s,o)=>s.id-o.id);return n.every((s,o)=>s.id===r[o].id&&s.scope_type===r[o].scope_type)},US=({data:t=[],value:e=[],onChange:n,scopeOptions:r=[],className:s="",disabled:o=!1,desktopColumns:i=2,inst
Line truncated
|
||||
`,children:[h.jsx(oe,{name:x.faIcon,className:"w-4 h-4"}),h.jsx(Q,{children:x.label}),y?.faIcon===x.faIcon&&h.jsx(oe,{name:"check",className:"w-4 h-4 ml-auto text-blue-600"})]},x.value)):h.jsx(I,{className:"px-3 py-4 text-sm text-gray-500 dark:text-gray-400 text-center",children:o})})]})]})},dc=({defaultOpen:e=!1,isOpen:t,onToggle:n,className:r="",style:s,children:o,disabled:i=!1,id:a,editorAttrs:l})=>{const[c,d]=v.useState(e),u=t!==void 0,f=u?t:c,g=v.useRef(null),[m,p]=v.useState(e?void 0:0),y=()=>{if(i)return;const _=!f;u||d(_),n?.(_)};v.useEffect(()=>{if(g.current)if(f){p(g.current.scrollHeight);const _=setTimeout(()=>{p(void 0)},300);return()=>clearTimeout(_)}else p(g.current.scrollHeight),requestAnimationFrame(()=>{p(0)})},[f]);const b={};if(v.Children.forEach(o,_=>{if(v.isValidElement(_)){const x=_.props?.slot;x==="trigger"?b.trigger=_:x==="content"&&(b.content=_)}}),!b.trigger&&!b.content){const _=v.Children.toArray(o);_.length>=2?(b.trigger=_[0],b.content=_.slice(1)):_.length===1&&(b.content=_[0])}return h.jsxs(I,{className:`border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden ${r}`,style:s,id:a,...l,children:[h.jsxs(ae,{type:"button",onClick:y,disabled:i,className:`w-full flex items-center justify-between p-3 text-left bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${i?"opacity-50 cursor-not-allowed":""} ${f?"border-b border-gray-200 dark:border-gray-700":""}`,"aria-expanded":f,children:[h.jsx(I,{className:"flex-1",children:b.trigger}),!b.trigger&&h.jsx("svg",{className:`w-5 h-5 text-gray-500 dark:text-gray-400 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),h.jsx(I,{ref:g,className:"overflow-hidden transition-all duration-300 ease-in-out",style:{height:m!==void 0?`${m}px`:"auto"},children:h.jsx(I,{className:"bg-white dark:bg-gray-800",children:b.content})})]})},ho=window.G7Core,dg=ho?.useControllableState,ug=(e,t)=>window.G7Core?.t?.(e,t)??e,o_={"":"common.scope_type_all_desc",self:"common.scope_type_self_desc",role:"common.scope_type_role_desc"},i_=({options:e,value:t,onChange:n,disabled:r=!1})=>{const[s,o]=v.useState(!1),[i,a]=v.useState({}),l=v.useRef(null),c=e.find(u=>u.value===t)??e[0];v.useEffect(()=>{if(!s)return;const u=g=>{l.current&&!l.current.contains(g.target)&&o(!1)},f=()=>{if(!l.current)return;const g=l.current.getBoundingClientRect();a({position:"fixed",top:g.bottom+4,right:window.innerWidth-g.right,zIndex:9999})};return document.addEventListener("mousedown",u),window.addEventListener("scroll",f,!0),window.addEventListener("resize",f),()=>{document.removeEventListener("mousedown",u),window.removeEventListener("scroll",f,!0),window.removeEventListener("resize",f)}},[s]);const d=v.useCallback(()=>{if(r||!l.current)return;const u=l.current.getBoundingClientRect();a({position:"fixed",top:u.bottom+4,right:window.innerWidth-u.right,zIndex:9999}),o(f=>!f)},[r]);return h.jsxs(I,{ref:l,className:"relative flex-shrink-0",children:[h.jsxs(I,{role:"button",tabIndex:0,onClick:u=>{u.stopPropagation(),u.preventDefault(),d()},onMouseDown:u=>u.stopPropagation(),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&(u.stopPropagation(),u.preventDefault(),d()),u.key==="Escape"&&o(!1)},className:`flex items-center gap-1 text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300 cursor-pointer select-none ${r?"opacity-50 cursor-not-allowed":"hover:border-blue-400 dark:hover:border-blue-500"}`,children:[h.jsx(Q,{children:c?.label??""}),h.jsx(oe,{name:de.ChevronDown,className:"w-3 h-3 text-gray-400 dark:text-gray-500"})]}),s&&h.jsx(I,{style:i,className:"w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1",children:e.map(u=>{const f=u.value??"",g=o_[f],m=g?ug(g):"",p=u.value===t;return h.jsxs(I,{role:"option","aria-selected":p,onClick:y=>{y.stopPropagation(),y.preventDefault(),n(u.value),o(!1)},onMouseDown:y=>y.stopPropagation(),className:`px-3 py-2 cursor-pointer transition-colors ${p?"bg-blue-50 dark:bg-blue-900/30":"hover:bg-gray-50 dark:hover:bg-gray-700/50"}`,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[h.jsx(Q,{className:`text-xs font-medium ${p?"text-blue-700 dark:text-blue-300":"text-gray-900 dark:text-white"}`,children:u.label}),p&&h.jsx(oe,{name:de.Check,className:"w-3.5 h-3.5 text-blue-600 dark:text-blue-400"})]}),m&&h.jsx(Q,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-0.5",children:m})]},f||"__null__")})})]})},a_=(e,t)=>{if(e.length!==t.length)return!1;const n=[...e].sort((s,o)=>s.id-o.id),r=[...t].sort((s,o)=>s.id-o.id);return n.every((s,o)=>s.id===r[o].id&&s.scope_type===r[o].scope_type)},l_=({data:e=[],value:t=[],onChange:n,scopeOptions:r=[],className:s="",disabled:o=!1,desktopColumns:i=2,inst
Line truncated
|
||||
let scriptImported = false
|
||||
self.addEventListener('message', async (e) => {
|
||||
const { file, id, imageCompressionLibUrl, options } = e.data
|
||||
@@ -220,11 +221,11 @@ self.addEventListener('message', async (e) => {
|
||||
self.postMessage({ error: e.message + '\\n' + e.stack, id })
|
||||
}
|
||||
})
|
||||
`;let Lc;function iE(t,e){return new Promise(((n,r)=>{Lc||(Lc=(function(i){const a=[];return a.push(i),URL.createObjectURL(new Blob(a))})(oE));const s=new Worker(Lc);s.addEventListener("message",(function(i){if(e.signal&&e.signal.aborted)s.terminate();else if(i.data.progress===void 0){if(i.data.error)return r(new Error(i.data.error)),void s.terminate();n(i.data.file),s.terminate()}else e.onProgress(i.data.progress)})),s.addEventListener("error",r),e.signal&&e.signal.addEventListener("abort",(()=>{r(e.signal.reason),s.terminate()})),s.postMessage({file:t,imageCompressionLibUrl:e.libURL,options:{...e,onProgress:void 0,signal:void 0}})}))}function Ht(t,e){return new Promise((function(n,r){let s,o,i,a,l,c;if(s={...e},i=0,{onProgress:a}=s,s.maxSizeMB=s.maxSizeMB||Number.POSITIVE_INFINITY,l=typeof s.useWebWorker!="boolean"||s.useWebWorker,delete s.useWebWorker,s.onProgress=g=>{i=g,typeof a=="function"&&a(i)},!(t instanceof Blob||t instanceof sE))return r(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(t.type))return r(new Error("The file given is not an image"));if(c=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,!l||typeof Worker!="function"||c)return Sp(t,s).then(function(g){try{return o=g,f.call(this)}catch(m){return r(m)}}.bind(this),r);var d=function(){try{return f.call(this)}catch(g){return r(g)}}.bind(this),u=function(g){try{return Sp(t,s).then((function(m){try{return o=m,d()}catch(p){return r(p)}}),r)}catch(m){return r(m)}};try{return s.libURL=s.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",iE(t,s).then((function(g){try{return o=g,d()}catch{return u()}}),u)}catch{u()}function f(){try{o.name=t.name,o.lastModified=t.lastModified}catch{}try{s.preserveExif&&t.type==="image/jpeg"&&(!s.fileType||s.fileType&&s.fileType===t.type)&&(o=hp(t,o))}catch{}return n(o)}}))}Ht.getDataUrlFromFile=bp,Ht.getFilefromDataUrl=Ic,Ht.loadImage=yp,Ht.drawImageInCanvas=xp,Ht.drawFileInCanvas=Ki,Ht.canvasToFile=qi,Ht.getExifOrientation=wp,Ht.handleMaxWidthOrHeight=kp,Ht.followExifOrientation=Cp,Ht.cleanupCanvasMemory=$n,Ht.isAutoOrientationInBrowser=is,Ht.approximateBelowMaximumCanvasSizeOfBrowser=vp,Ht.copyExifWithoutOrientation=hp,Ht.getBrowserName=kr,Ht.version="2.0.2";const aE=window.G7Core,Pc=t=>{const e=["B","KB","MB","GB"];let n=0,r=t;for(;r>=1024&&n<e.length-1;)r/=1024,n++;return`${r.toFixed(2)} ${e[n]}`},lE=t=>{if(t&&typeof t=="object"){const e=t;if(e.response?.data?.message)return e.response.data.message;if(e.response?.data?.errors){const n=Object.values(e.response.data.errors)[0];if(Array.isArray(n)&&n[0])return n[0]}if(e.message)return e.message}return t instanceof Error?t.message:"업로드 실패"},cE=t=>t?t.startsWith("image/")?"fa-file-image":t.startsWith("video/")?"fa-file-video":t.startsWith("audio/")?"fa-file-audio":t==="application/pdf"?"fa-file-pdf":t.includes("word")||t.includes("document")?"fa-file-word":t.includes("excel")||t.includes("spreadsheet")?"fa-file-excel":t.includes("zip")||t.includes("compressed")?"fa-file-zipper":"fa-file":"fa-file",It=(t,e)=>aE?.t?.(t,e)??t,_p=window.G7Core?.createLogger?.("Comp:FileUploader")??{log:(...t)=>console.log("[Comp:FileUploader]",...t),warn:(...t)=>console.warn("[Comp:FileUploader]",...t),error:(...t)=>console.error("[Comp:FileUploader]",...t)},Cr=window.G7Core;function dE(t){const{attachmentableType:e,attachmentableId:n,collection:r,maxFiles:s,maxSize:o,maxConcurrentUploads:i,accept:a,imageCompression:l,roleIds:c,autoUpload:d,initialFiles:u,endpoints:f,onFilesChange:g,onUploadComplete:m,onUploadError:p,onRemove:y,onReorder:b,uploadTriggerEvent:_,confirmBeforeRemove:x,uploadParams:E}=t,[C,k]=v.useState(u),[N,S]=v.useState([]),[w,M]=v.useState(!1),T=v.useRef(null),P=v.useRef(0),L=v.useRef(new Set),$=v.useRef(new Map),A=v.useRef(null),[D,O]=v.useState(!1),[z,H]=v.useState(null),[j,W]=v.useState(!1),U=v.useRef(!1),[B,G]=v.useState([]),[V,K]=v.useState(!1),[X,Z]=v.useState(0),ee=v.useRef(0),[ne,he]=v.useState(new Map),J=v.useRef(new Map),le=v.useRef(g);le.current=g,v.useEffect(()=>{le.current?.(N)},[N]),v.useEffect(()=>{if(U.current)return;const te=u.filter(pe=>!L.current.has(pe.id)&&(!pe.hash||!L.current.has(pe.hash))),ge=new Set(te.map(pe=>pe.id)),me=Array.from($.current.values()).filter(pe=>!ge.has(pe.id)&&!L.current.has(pe.id));k([...te,...me])},[u]);const ue=v.useCallback(async te=>{const ge={maxSizeMB:l?.maxSizeMB??1,maxWidthOrHeight:l?.maxWidthOrHeight??1920,useWebWorker:!0};return await Ht(te,ge)},[l]),ae=v.useCallback(async te=>{const ge=C.length+N.length,me=Math.max(0,s-ge),pe=Array.from(te).slice(0,me);if(te.length>me){const be=Array.from(te).slice(me),Ue=It("attachment.upload_limit_exceeded",{max:s});p?.(Ue,be[0]??te[0])}for(const be of pe){if(be.size>o*1024*1024){p?.(`[${be.name}] 파일 크기(${Pc(be.size)})가 최대 허용 크기(${o}MB)를 초과합니다.`,be);continue}if(a){const Ce="."+be.name.split(".").pop()?.toLowerCase();if(!a.split(",").m
Line truncated
|
||||
`;let Hc;function ME(e,t){return new Promise(((n,r)=>{Hc||(Hc=(function(i){const a=[];return a.push(i),URL.createObjectURL(new Blob(a))})(EE));const s=new Worker(Hc);s.addEventListener("message",(function(i){if(t.signal&&t.signal.aborted)s.terminate();else if(i.data.progress===void 0){if(i.data.error)return r(new Error(i.data.error)),void s.terminate();n(i.data.file),s.terminate()}else t.onProgress(i.data.progress)})),s.addEventListener("error",r),t.signal&&t.signal.addEventListener("abort",(()=>{r(t.signal.reason),s.terminate()})),s.postMessage({file:e,imageCompressionLibUrl:t.libURL,options:{...t,onProgress:void 0,signal:void 0}})}))}function Yt(e,t){return new Promise((function(n,r){let s,o,i,a,l,c;if(s={...t},i=0,{onProgress:a}=s,s.maxSizeMB=s.maxSizeMB||Number.POSITIVE_INFINITY,l=typeof s.useWebWorker!="boolean"||s.useWebWorker,delete s.useWebWorker,s.onProgress=g=>{i=g,typeof a=="function"&&a(i)},!(e instanceof Blob||e instanceof _E))return r(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return r(new Error("The file given is not an image"));if(c=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,!l||typeof Worker!="function"||c)return $p(e,s).then(function(g){try{return o=g,f.call(this)}catch(m){return r(m)}}.bind(this),r);var d=function(){try{return f.call(this)}catch(g){return r(g)}}.bind(this),u=function(g){try{return $p(e,s).then((function(m){try{return o=m,d()}catch(p){return r(p)}}),r)}catch(m){return r(m)}};try{return s.libURL=s.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",ME(e,s).then((function(g){try{return o=g,d()}catch{return u()}}),u)}catch{u()}function f(){try{o.name=e.name,o.lastModified=e.lastModified}catch{}try{s.preserveExif&&e.type==="image/jpeg"&&(!s.fileType||s.fileType&&s.fileType===e.type)&&(o=_p(e,o))}catch{}return n(o)}}))}Yt.getDataUrlFromFile=Tp,Yt.getFilefromDataUrl=Vc,Yt.loadImage=Op,Yt.drawImageInCanvas=Dp,Yt.drawFileInCanvas=na,Yt.canvasToFile=ra,Yt.getExifOrientation=Ip,Yt.handleMaxWidthOrHeight=Lp,Yt.followExifOrientation=Pp,Yt.cleanupCanvasMemory=Wn,Yt.isAutoOrientationInBrowser=Cs,Yt.approximateBelowMaximumCanvasSizeOfBrowser=Ap,Yt.copyExifWithoutOrientation=_p,Yt.getBrowserName=Dr,Yt.version="2.0.2";const NE=window.G7Core,Uc=e=>{const t=["B","KB","MB","GB"];let n=0,r=e;for(;r>=1024&&n<t.length-1;)r/=1024,n++;return`${r.toFixed(2)} ${t[n]}`},TE=e=>{if(e&&typeof e=="object"){const t=e;if(t.response?.data?.message)return t.response.data.message;if(t.response?.data?.errors){const n=Object.values(t.response.data.errors)[0];if(Array.isArray(n)&&n[0])return n[0]}if(t.message)return t.message}return e instanceof Error?e.message:"업로드 실패"},OE=e=>e?e.startsWith("image/")?"fa-file-image":e.startsWith("video/")?"fa-file-video":e.startsWith("audio/")?"fa-file-audio":e==="application/pdf"?"fa-file-pdf":e.includes("word")||e.includes("document")?"fa-file-word":e.includes("excel")||e.includes("spreadsheet")?"fa-file-excel":e.includes("zip")||e.includes("compressed")?"fa-file-zipper":"fa-file":"fa-file",$t=(e,t)=>NE?.t?.(e,t)??e,Fp=window.G7Core?.createLogger?.("Comp:FileUploader")??{log:(...e)=>console.log("[Comp:FileUploader]",...e),warn:(...e)=>console.warn("[Comp:FileUploader]",...e),error:(...e)=>console.error("[Comp:FileUploader]",...e)},Ir=window.G7Core;function AE(e){const{attachmentableType:t,attachmentableId:n,collection:r,maxFiles:s,maxSize:o,maxConcurrentUploads:i,accept:a,imageCompression:l,roleIds:c,autoUpload:d,initialFiles:u,endpoints:f,onFilesChange:g,onUploadComplete:m,onUploadError:p,onRemove:y,onReorder:b,uploadTriggerEvent:_,confirmBeforeRemove:x,uploadParams:M}=e,[C,k]=v.useState(u),[N,S]=v.useState([]),[w,E]=v.useState(!1),D=v.useRef(null),P=v.useRef(0),L=v.useRef(new Set),$=v.useRef(new Map),A=v.useRef(null),[O,T]=v.useState(!1),[z,H]=v.useState(null),[j,G]=v.useState(!1),U=v.useRef(!1),[W,Y]=v.useState([]),[V,X]=v.useState(!1),[K,q]=v.useState(0),ne=v.useRef(0),[ee,ue]=v.useState(new Map),J=v.useRef(new Map),ce=v.useRef(g);ce.current=g,v.useEffect(()=>{ce.current?.(N)},[N]),v.useEffect(()=>{if(U.current)return;const re=u.filter(we=>!L.current.has(we.id)&&(!we.hash||!L.current.has(we.hash))),fe=new Set(re.map(we=>we.id)),ye=Array.from($.current.values()).filter(we=>!fe.has(we.id)&&!L.current.has(we.id));k([...re,...ye])},[u]);const me=v.useCallback(async re=>{const fe={maxSizeMB:l?.maxSizeMB??1,maxWidthOrHeight:l?.maxWidthOrHeight??1920,useWebWorker:!0};return await Yt(re,fe)},[l]),le=v.useCallback(async re=>{const fe=C.length+N.length,ye=Math.max(0,s-fe),we=Array.from(re).slice(0,ye);if(re.length>ye){const xe=Array.from(re).slice(ye),He=$t("attachment.upload_limit_exceeded",{max:s});p?.(He,xe[0]??re[0])}for(const xe of we){if(xe.size>o*1024*1024){p?.(`[${xe.name}] 파일 크기(${Uc(xe.size)})가 최대 허용 크기(${o}MB)를 초과합니다.`,xe);continue}if(a){const _e="."+xe.name.split(".").pop()?.toLowerCase();if(!a.split(",").m
Line truncated
|
||||
relative border-2 border-dashed rounded-lg transition-colors
|
||||
${l?"opacity-60 cursor-not-allowed":""}
|
||||
${t&&!l?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 dark:border-gray-600"}
|
||||
`,onDragOver:u=>{u.preventDefault(),d&&r(!0)},onDragLeave:()=>r(!1),onDrop:u=>{u.preventDefault(),r(!1),d&&n(u.dataTransfer.files)},children:[h.jsx("input",{ref:s,type:"file",multiple:!0,accept:o,onChange:u=>u.target.files&&!l&&n(u.target.files),className:"hidden",disabled:l}),!c&&h.jsxs(I,{className:`p-8 text-center transition-colors ${l?"cursor-not-allowed":"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50"}`,onClick:()=>!l&&s.current?.click(),children:[h.jsx(mt,{className:"fa-solid fa-cloud-arrow-up text-4xl text-gray-400 dark:text-gray-500 mb-4"}),h.jsx(yt,{className:"text-sm text-gray-600 dark:text-gray-400",children:It("attachment.drop_or_click")}),h.jsxs(yt,{className:"text-xs text-gray-500 dark:text-gray-500 mt-2",children:[It("attachment.upload_limit",{maxFiles:i,maxSize:a}),o&&` (${o})`]})]}),c]})},fE=window.G7Core,hE=window.G7Core?.createLogger?.("Comp:SortableThumbnail")??{log:(...t)=>console.log("[Comp:SortableThumbnail]",...t),warn:(...t)=>console.warn("[Comp:SortableThumbnail]",...t),error:(...t)=>console.error("[Comp:SortableThumbnail]",...t)},gE=({file:t,onRemove:e,onRetry:n,onImageClick:r,onDownload:s,isPrimary:o,onPrimaryClick:i,disabled:a})=>{const{attributes:l,listeners:c,setNodeRef:d,transform:u,transition:f,isDragging:g}=Ii({id:"hash"in t?t.hash:t.id}),m={transform:Pn.Transform.toString(u),transition:f,opacity:g?.5:1},p="status"in t&&t.status!==void 0,y=p?t.status:"done",b=y==="uploading",_=y==="compressing",x=y==="error",E=p?t.progress:100,C=p?t.preview:void 0,k=p?t.mime_type?.startsWith("image/")??!1:t.is_image,[N,S]=v.useState(void 0),w=!p&&k?t.download_url:void 0;v.useEffect(()=>{if(!w){S(void 0);return}let T=!0,P;return(async()=>{try{const $=await fE.api.get(w,{responseType:"blob"});T&&$&&(P=URL.createObjectURL($),S(P))}catch($){hE.error("Failed to load authenticated image:",$)}})(),()=>{T=!1,P&&URL.revokeObjectURL(P)}},[w]);const M=N;return h.jsxs(I,{ref:d,style:m,className:"relative group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden",children:[h.jsxs(I,{className:`aspect-square w-full flex items-center justify-center bg-gray-50 dark:bg-gray-900 relative ${k||!p?"cursor-pointer":""}`,onClick:()=>{b||_||(k&&r?r():!p&&s&&s())},children:[C||M?h.jsx(yn,{src:C||M,alt:t.original_filename,className:"w-full h-full object-cover"}):h.jsx(mt,{className:`fa-solid ${cE(t.mime_type)} text-4xl text-gray-400 dark:text-gray-500`}),(b||_)&&h.jsxs(I,{className:"absolute inset-0 bg-black/50 flex flex-col items-center justify-center",children:[h.jsx(I,{className:"w-3/4 h-2 bg-gray-700 rounded-full overflow-hidden",children:h.jsx(I,{className:"h-full bg-blue-500 transition-all",style:{width:`${E}%`}})}),h.jsxs(Q,{className:"text-white text-xs mt-1",children:[E,"%"]})]}),x&&h.jsx(I,{className:"absolute inset-0 bg-red-500/50 flex items-center justify-center",children:h.jsx(mt,{className:"fa-solid fa-exclamation-triangle text-2xl text-white"})}),i&&h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),i()},className:`absolute top-1.5 left-1.5 z-10 p-1 rounded-full transition-colors ${o?"bg-yellow-500 text-white":"bg-gray-800/50 text-gray-300 hover:bg-yellow-500 hover:text-white opacity-0 group-hover:opacity-100"}`,title:It(o?"attachment.primary_image":"attachment.set_as_primary"),children:h.jsx(mt,{className:"fa-solid fa-star text-xs"})}),h.jsxs(I,{className:`absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-start justify-end p-1 gap-1 opacity-0 group-hover:opacity-100 ${a?"hidden":""}`,children:[h.jsx(oe,{type:"button",...l,...c,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-gray-600 dark:text-gray-300 hover:bg-white cursor-grab active:cursor-grabbing",title:It("attachment.drag_to_reorder"),onClick:T=>T.stopPropagation(),children:h.jsx(mt,{className:"fa-solid fa-grip-vertical text-sm"})}),!p&&s&&h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),s()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-blue-500 hover:bg-white",title:It("common.download"),children:h.jsx(mt,{className:"fa-solid fa-download text-sm"})}),x&&n&&h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),n()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-orange-500 hover:bg-white",title:It("common.retry"),children:h.jsx(mt,{className:"fa-solid fa-arrow-rotate-right text-sm"})}),h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),e()},disabled:b||_,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-red-500 hover:bg-white disabled:opacity-50",title:It("common.delete"),children:h.jsx(mt,{className:"fa-solid fa-times text-sm"})})]})]}),h.jsxs(I,{className:`p-2 ${!p&&s?"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700":""}`,onClick:()=>{!p&&s&&s()},children:[h.jsx(yt,{className:"text-xs font-medium text-gray-900 dark:text-white truncate",title:t.original_filename,children:t.original_filename}),h.jsx(yt,{className:"text-xs text-gray-500 dark:text-gray-400",children:t.siz
Line truncated
|
||||
${e&&!l?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 dark:border-gray-600"}
|
||||
`,onDragOver:u=>{u.preventDefault(),d&&r(!0)},onDragLeave:()=>r(!1),onDrop:u=>{u.preventDefault(),r(!1),d&&n(u.dataTransfer.files)},children:[h.jsx("input",{ref:s,type:"file",multiple:!0,accept:o,onChange:u=>u.target.files&&!l&&n(u.target.files),className:"hidden",disabled:l}),!c&&h.jsxs(I,{className:`p-8 text-center transition-colors ${l?"cursor-not-allowed":"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50"}`,onClick:()=>!l&&s.current?.click(),children:[h.jsx(bt,{className:"fa-solid fa-cloud-arrow-up text-4xl text-gray-400 dark:text-gray-500 mb-4"}),h.jsx(vt,{className:"text-sm text-gray-600 dark:text-gray-400",children:$t("attachment.drop_or_click")}),h.jsxs(vt,{className:"text-xs text-gray-500 dark:text-gray-500 mt-2",children:[$t("attachment.upload_limit",{maxFiles:i,maxSize:a}),o&&` (${o})`]})]}),c]})},IE=window.G7Core,LE=window.G7Core?.createLogger?.("Comp:SortableThumbnail")??{log:(...e)=>console.log("[Comp:SortableThumbnail]",...e),warn:(...e)=>console.warn("[Comp:SortableThumbnail]",...e),error:(...e)=>console.error("[Comp:SortableThumbnail]",...e)},PE=({file:e,onRemove:t,onRetry:n,onImageClick:r,onDownload:s,isPrimary:o,onPrimaryClick:i,disabled:a})=>{const{attributes:l,listeners:c,setNodeRef:d,transform:u,transition:f,isDragging:g}=Ui({id:"hash"in e?e.hash:e.id}),m={transform:jn.Transform.toString(u),transition:f,opacity:g?.5:1},p="status"in e&&e.status!==void 0,y=p?e.status:"done",b=y==="uploading",_=y==="compressing",x=y==="error",M=p?e.progress:100,C=p?e.preview:void 0,k=p?e.mime_type?.startsWith("image/")??!1:e.is_image,[N,S]=v.useState(void 0),w=!p&&k?e.download_url:void 0;v.useEffect(()=>{if(!w){S(void 0);return}let D=!0,P;return(async()=>{try{const $=await IE.api.get(w,{responseType:"blob"});D&&$&&(P=URL.createObjectURL($),S(P))}catch($){LE.error("Failed to load authenticated image:",$)}})(),()=>{D=!1,P&&URL.revokeObjectURL(P)}},[w]);const E=N;return h.jsxs(I,{ref:d,style:m,className:"relative group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden",children:[h.jsxs(I,{className:`aspect-square w-full flex items-center justify-center bg-gray-50 dark:bg-gray-900 relative ${k||!p?"cursor-pointer":""}`,onClick:()=>{b||_||(k&&r?r():!p&&s&&s())},children:[C||E?h.jsx(_n,{src:C||E,alt:e.original_filename,className:"w-full h-full object-cover"}):h.jsx(bt,{className:`fa-solid ${OE(e.mime_type)} text-4xl text-gray-400 dark:text-gray-500`}),(b||_)&&h.jsxs(I,{className:"absolute inset-0 bg-black/50 flex flex-col items-center justify-center",children:[h.jsx(I,{className:"w-3/4 h-2 bg-gray-700 rounded-full overflow-hidden",children:h.jsx(I,{className:"h-full bg-blue-500 transition-all",style:{width:`${M}%`}})}),h.jsxs(Q,{className:"text-white text-xs mt-1",children:[M,"%"]})]}),x&&h.jsx(I,{className:"absolute inset-0 bg-red-500/50 flex items-center justify-center",children:h.jsx(bt,{className:"fa-solid fa-exclamation-triangle text-2xl text-white"})}),i&&h.jsx(ae,{type:"button",onClick:D=>{D.stopPropagation(),i()},className:`absolute top-1.5 left-1.5 z-10 p-1 rounded-full transition-colors ${o?"bg-yellow-500 text-white":"bg-gray-800/50 text-gray-300 hover:bg-yellow-500 hover:text-white opacity-0 group-hover:opacity-100"}`,title:$t(o?"attachment.primary_image":"attachment.set_as_primary"),children:h.jsx(bt,{className:"fa-solid fa-star text-xs"})}),h.jsxs(I,{className:`absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-start justify-end p-1 gap-1 opacity-0 group-hover:opacity-100 ${a?"hidden":""}`,children:[h.jsx(ae,{type:"button",...l,...c,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-gray-600 dark:text-gray-300 hover:bg-white cursor-grab active:cursor-grabbing",title:$t("attachment.drag_to_reorder"),onClick:D=>D.stopPropagation(),children:h.jsx(bt,{className:"fa-solid fa-grip-vertical text-sm"})}),!p&&s&&h.jsx(ae,{type:"button",onClick:D=>{D.stopPropagation(),s()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-blue-500 hover:bg-white",title:$t("common.download"),children:h.jsx(bt,{className:"fa-solid fa-download text-sm"})}),x&&n&&h.jsx(ae,{type:"button",onClick:D=>{D.stopPropagation(),n()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-orange-500 hover:bg-white",title:$t("common.retry"),children:h.jsx(bt,{className:"fa-solid fa-arrow-rotate-right text-sm"})}),h.jsx(ae,{type:"button",onClick:D=>{D.stopPropagation(),t()},disabled:b||_,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-red-500 hover:bg-white disabled:opacity-50",title:$t("common.delete"),children:h.jsx(bt,{className:"fa-solid fa-times text-sm"})})]})]}),h.jsxs(I,{className:`p-2 ${!p&&s?"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700":""}`,onClick:()=>{!p&&s&&s()},children:[h.jsx(vt,{className:"text-xs font-medium text-gray-900 dark:text-white truncate",title:e.original_filename,children:e.original_filename}),h.jsx(vt,{className:"text-xs text-gray-500 dark:text-gray-400",children:e.siz
Line truncated
|
||||
${y.radio}
|
||||
text-blue-600
|
||||
bg-gray-100 dark:bg-gray-700
|
||||
@@ -232,15 +233,15 @@ self.addEventListener('message', async (e) => {
|
||||
focus:ring-blue-500 dark:focus:ring-blue-600
|
||||
focus:ring-2
|
||||
dark:ring-offset-gray-800
|
||||
${E?"cursor-not-allowed":"cursor-pointer"}
|
||||
${M?"cursor-not-allowed":"cursor-pointer"}
|
||||
`}),h.jsx(Q,{className:`
|
||||
${y.label}
|
||||
font-medium
|
||||
text-gray-900 dark:text-gray-300
|
||||
`,children:_.label})]},_.value)})}),c&&h.jsx(Q,{className:"mt-1 text-xs text-red-600 dark:text-red-400",children:c})]})},as=()=>window.G7Core,Zi=(t,e)=>as()?.t?.(t,e)??t,bE=()=>as()?.locale?.supported?.()??["ko","en"],yE=()=>as()?.locale?.current?.()??"ko",Mp=t=>{const e=Zi(`common.language_${t}`);return e===`common.language_${t}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[t]||t.toUpperCase():e},vE=({content:t="",value:e,onChange:n,multilingual:r=!1,isHtml:s=!1,onIsHtmlChange:o,rows:i=15,placeholder:a="",label:l,showHtmlModeToggle:c=!0,contentClassName:d="",purifyConfig:u,className:f="",name:g="content",htmlFieldName:m="content_mode",readOnly:p=!1,id:y,editorAttrs:b})=>{const _=v.useMemo(()=>bE(),[]),x=v.useMemo(()=>yE(),[]),[E,C]=v.useState(x),[k,N]=v.useState(s),[S,w]=v.useState(t),[M,T]=v.useState(e??{}),[P,L]=v.useState(!1);v.useEffect(()=>{N(s)},[s]),v.useEffect(()=>{r||w(t)},[t,r]),v.useEffect(()=>{r&&e&&T(e)},[e,r]);const $=v.useCallback(W=>{const U=W.target.value;if(w(U),n){const B=as()?.createChangeEvent?.({value:U,name:g,type:"textarea"})??{target:{name:g,value:U}};n(B)}},[n,g]),A=v.useCallback((W,U)=>{const B={...M,[W]:U};if(T(B),n){const G=as()?.createChangeEvent?.({value:B,name:g,type:"multilingual"})??{target:{name:g,value:B}};n(G)}},[n,g,M]),D=v.useCallback(W=>{const U=W.target.checked;if(N(U),U||L(!1),o){const B=as()?.createChangeEvent?.({checked:U,name:m,type:"checkbox"})??{target:{name:m,checked:U}};o(B)}},[o,m]),O=v.useCallback(()=>{L(W=>!W)},[]),z=r?M[E]??"":S,H=v.useCallback(W=>!!M[W]?.trim(),[M]),j=()=>h.jsx(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:_.map(W=>{const U=W===x,B=E===W;return h.jsxs(oe,{type:"button",className:`
|
||||
`,children:_.label})]},_.value)})}),c&&h.jsx(Q,{className:"mt-1 text-xs text-red-600 dark:text-red-400",children:c})]})},Ss=()=>window.G7Core,sa=(e,t)=>Ss()?.t?.(e,t)??e,zE=()=>Ss()?.locale?.supported?.()??["ko","en"],VE=()=>Ss()?.locale?.current?.()??"ko",Vp=e=>{const t=sa(`common.language_${e}`);return t===`common.language_${e}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[e]||e.toUpperCase():t},HE=({content:e="",value:t,onChange:n,multilingual:r=!1,isHtml:s=!1,onIsHtmlChange:o,rows:i=15,placeholder:a="",label:l,showHtmlModeToggle:c=!0,contentClassName:d="",purifyConfig:u,className:f="",name:g="content",htmlFieldName:m="content_mode",readOnly:p=!1,id:y,editorAttrs:b})=>{const _=v.useMemo(()=>zE(),[]),x=v.useMemo(()=>VE(),[]),[M,C]=v.useState(x),[k,N]=v.useState(s),[S,w]=v.useState(e),[E,D]=v.useState(t??{}),[P,L]=v.useState(!1);v.useEffect(()=>{N(s)},[s]),v.useEffect(()=>{r||w(e)},[e,r]),v.useEffect(()=>{r&&t&&D(t)},[t,r]);const $=v.useCallback(G=>{const U=G.target.value;if(w(U),n){const W=Ss()?.createChangeEvent?.({value:U,name:g,type:"textarea"})??{target:{name:g,value:U}};n(W)}},[n,g]),A=v.useCallback((G,U)=>{const W={...E,[G]:U};if(D(W),n){const Y=Ss()?.createChangeEvent?.({value:W,name:g,type:"multilingual"})??{target:{name:g,value:W}};n(Y)}},[n,g,E]),O=v.useCallback(G=>{const U=G.target.checked;if(N(U),U||L(!1),o){const W=Ss()?.createChangeEvent?.({checked:U,name:m,type:"checkbox"})??{target:{name:m,checked:U}};o(W)}},[o,m]),T=v.useCallback(()=>{L(G=>!G)},[]),z=r?E[M]??"":S,H=v.useCallback(G=>!!E[G]?.trim(),[E]),j=()=>h.jsx(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:_.map(G=>{const U=G===x,W=M===G;return h.jsxs(ae,{type:"button",className:`
|
||||
inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold transition-all
|
||||
${B?U?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":U?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"}
|
||||
`,onClick:()=>C(W),children:[h.jsx(re,{name:"globe",className:"w-3 h-3"}),h.jsx(Q,{children:Mp(W)}),U&&h.jsx(Q,{className:"text-red-500",children:"*"}),H(W)&&!B&&h.jsx(re,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"})]},W)})});return h.jsxs(I,{className:`space-y-2 ${f}`,id:y,...b,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[l&&h.jsx(sn,{className:"block text-sm font-medium text-gray-500 dark:text-gray-400",children:l}),h.jsxs(I,{className:"flex items-center gap-3",children:[k&&!p&&h.jsx(oe,{type:"button",onClick:O,className:`px-3 py-1.5 text-xs font-bold rounded-lg focus:outline-none focus:ring-2 ${P?"text-gray-700 dark:text-gray-200 bg-gray-200 dark:bg-gray-600 border border-gray-300 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-500 focus:ring-gray-400 dark:focus:ring-gray-500":"text-blue-600 dark:text-blue-400 bg-white dark:bg-gray-700 border border-blue-300 dark:border-blue-600 hover:bg-blue-50 dark:hover:bg-gray-600 focus:ring-blue-500 dark:focus:ring-blue-600"}`,children:Zi(P?"common.preview_off":"common.preview")}),c&&h.jsxs(sn,{className:"flex items-center gap-2 p-2 cursor-pointer",children:[h.jsx(kt,{type:"checkbox",name:m,checked:k,onChange:D,disabled:p,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(I,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:Zi("common.html_mode")})]})]})]}),r&&j(),!P&&(r?_.map(W=>h.jsx(I,{className:E===W?"block":"hidden",children:h.jsx(Qn,{name:`${g}[${W}]`,value:M[W]??"",onChange:U=>A(W,U.target.value),placeholder:`${a} (${Mp(W)})`,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})},W)):h.jsx(Qn,{name:g,value:S,onChange:$,placeholder:a,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})),P&&h.jsx(I,{className:"p-4 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 min-h-[400px]",children:h.jsx(Za,{content:z,isHtml:!0,className:d||"prose dark:prose-invert max-w-none text-gray-900 dark:text-gray-100",purifyConfig:u})})]})},Np="g7_dismissed_warnings";function Op(){try{const t=sessionStorage.getItem(Np);return t?JSON.parse(t):[]}catch{return[]}}function xE(t){try{const e=Op();e.includes(t)||(e.push(t),sessionStorage.setItem(Np,JSON.stringify(e)))}catch{}}function wE(t){switch(t){case"error":return"error";case"warning":return"warning";default:return"info"}}const kE=({warnings:t,className:e=""})=>{const[n,r]=v.useState([]);v.useEffect(()=>{r(Op())},[]);const s=v.useCallback(i=>{xE(i),r(a=>[...a,i])},[]);if(!t||t.length===0)return null;const o=t.filter(i=>!n.includes(i.id));return o.length===0?null:h.jsx(I,{className:`space-y-2 ${e}`.trim(),children:o.map(i=>h.jsx(cu,{type:wE(i.level),message:i.message,dismissible:!0,onDismiss:()=>s(i.id)},i.id))})},Tp=window.G7Core?.createLogger?.("Comp:FilterVisibility")??{log:(...t)=>console.log("[Comp:FilterVisibility]",...t),warn:(...t)=>console.warn("[Comp:FilterVisibility]",...t),error:(...t)=>console.error("[Comp:FilterVisibility]",...t)},CE=({id:t,visibleFilters:e,defaultFilters:n=[],onFilterVisibilityChange:r})=>{const[s,o]=v.useState(!1),i=v.useRef(!0),a=v.useMemo(()=>{try{const f=window.G7Core?.AuthManager?.getInstance();return f?f.getUser()?.uuid:void 0}catch{return}},[]),l=v.useMemo(()=>`g7_filters_${t}${a!==void 0?`_${a}`:""}`,[t,a]),c=v.useCallback(()=>{try{const f=localStorage.getItem(l);if(f){const g=JSON.parse(f);if(Array.isArray(g))return g}}catch(f){Tp.warn("Failed to load from localStorage:",f)}return null},[l]),d=v.useCallback(f=>{try{localStorage.setItem(l,JSON.stringify(f))}catch(g){Tp.warn("Failed to save to localStorage:",g)}},[l]),u=v.useCallback(()=>{const f=c();return f||(e&&e.length>0?e:n)},[c,e,n]);return v.useEffect(()=>{if(!s){const f=u();r&&f.length>0&&r(f),o(!0)}},[s,r,u]),v.useEffect(()=>{if(s&&e&&e.length>0){if(i.current){i.current=!1;return}d(e)}},[s,e,d]),null},SE=window.G7Core?.createLogger?.("Comp:SlotContainer")??{log:(...t)=>console.log(
Line truncated
|
||||
${W?U?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":U?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"}
|
||||
`,onClick:()=>C(G),children:[h.jsx(oe,{name:"globe",className:"w-3 h-3"}),h.jsx(Q,{children:Vp(G)}),U&&h.jsx(Q,{className:"text-red-500",children:"*"}),H(G)&&!W&&h.jsx(oe,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"})]},G)})});return h.jsxs(I,{className:`space-y-2 ${f}`,id:y,...b,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[l&&h.jsx(ln,{className:"block text-sm font-medium text-gray-500 dark:text-gray-400",children:l}),h.jsxs(I,{className:"flex items-center gap-3",children:[k&&!p&&h.jsx(ae,{type:"button",onClick:T,className:`px-3 py-1.5 text-xs font-bold rounded-lg focus:outline-none focus:ring-2 ${P?"text-gray-700 dark:text-gray-200 bg-gray-200 dark:bg-gray-600 border border-gray-300 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-500 focus:ring-gray-400 dark:focus:ring-gray-500":"text-blue-600 dark:text-blue-400 bg-white dark:bg-gray-700 border border-blue-300 dark:border-blue-600 hover:bg-blue-50 dark:hover:bg-gray-600 focus:ring-blue-500 dark:focus:ring-blue-600"}`,children:sa(P?"common.preview_off":"common.preview")}),c&&h.jsxs(ln,{className:"flex items-center gap-2 p-2 cursor-pointer",children:[h.jsx(St,{type:"checkbox",name:m,checked:k,onChange:O,disabled:p,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(I,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:sa("common.html_mode")})]})]})]}),r&&j(),!P&&(r?_.map(G=>h.jsx(I,{className:M===G?"block":"hidden",children:h.jsx(ir,{name:`${g}[${G}]`,value:E[G]??"",onChange:U=>A(G,U.target.value),placeholder:`${a} (${Vp(G)})`,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})},G)):h.jsx(ir,{name:g,value:S,onChange:$,placeholder:a,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})),P&&h.jsx(I,{className:"p-4 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 min-h-[400px]",children:h.jsx(ol,{content:z,isHtml:!0,className:d||"prose dark:prose-invert max-w-none text-gray-900 dark:text-gray-100",purifyConfig:u})})]})},Hp="g7_dismissed_warnings";function Up(){try{const e=sessionStorage.getItem(Hp);return e?JSON.parse(e):[]}catch{return[]}}function UE(e){try{const t=Up();t.includes(e)||(t.push(e),sessionStorage.setItem(Hp,JSON.stringify(t)))}catch{}}function BE(e){switch(e){case"error":return"error";case"warning":return"warning";default:return"info"}}const jE=({warnings:e,className:t=""})=>{const[n,r]=v.useState([]);v.useEffect(()=>{r(Up())},[]);const s=v.useCallback(i=>{UE(i),r(a=>[...a,i])},[]);if(!e||e.length===0)return null;const o=e.filter(i=>!n.includes(i.id));return o.length===0?null:h.jsx(I,{className:`space-y-2 ${t}`.trim(),children:o.map(i=>h.jsx(wu,{type:BE(i.level),message:i.message,dismissible:!0,onDismiss:()=>s(i.id)},i.id))})},Bp=window.G7Core?.createLogger?.("Comp:FilterVisibility")??{log:(...e)=>console.log("[Comp:FilterVisibility]",...e),warn:(...e)=>console.warn("[Comp:FilterVisibility]",...e),error:(...e)=>console.error("[Comp:FilterVisibility]",...e)},WE=({id:e,visibleFilters:t,defaultFilters:n=[],onFilterVisibilityChange:r})=>{const[s,o]=v.useState(!1),i=v.useRef(!0),a=v.useMemo(()=>{try{const f=window.G7Core?.AuthManager?.getInstance();return f?f.getUser()?.uuid:void 0}catch{return}},[]),l=v.useMemo(()=>`g7_filters_${e}${a!==void 0?`_${a}`:""}`,[e,a]),c=v.useCallback(()=>{try{const f=localStorage.getItem(l);if(f){const g=JSON.parse(f);if(Array.isArray(g))return g}}catch(f){Bp.warn("Failed to load from localStorage:",f)}return null},[l]),d=v.useCallback(f=>{try{localStorage.setItem(l,JSON.stringify(f))}catch(g){Bp.warn("Failed to save to localStorage:",g)}},[l]),u=v.useCallback(()=>{const f=c();return f||(t&&t.length>0?t:n)},[c,t,n]);return v.useEffect(()=>{if(!s){const f=u();r&&f.length>0&&r(f),o(!0)}},[s,r,u]),v.useEffect(()=>{if(s&&t&&t.length>0){if(i.current){i.current=!1;return}d(t)}},[s,t,d]),null},GE=window.G7Core?.createLogger?.("Comp:SlotContainer")??{log:(...e)=>console.log(
Line truncated
|
||||
w-full px-4 py-2.5
|
||||
bg-gray-100 dark:bg-gray-700
|
||||
border-0 rounded-xl
|
||||
@@ -249,13 +250,13 @@ self.addEventListener('message', async (e) => {
|
||||
flex items-center justify-between gap-2
|
||||
text-left cursor-pointer
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
`,"aria-haspopup":"listbox","aria-expanded":f,children:[h.jsx(I,{className:"flex-1 min-w-0",children:E()}),h.jsx(Un,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),f&&h.jsx(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden",role:"listbox",children:h.jsx(I,{className:"overflow-auto",style:{maxHeight:i},children:t.length===0?h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:Ap("common.no_options")}):t.map((k,N)=>{const S=String(k.value)===String(e);return h.jsx(oe,{type:"button",onClick:()=>!k.disabled&&_(k.value),disabled:k.disabled,className:`
|
||||
`,"aria-haspopup":"listbox","aria-expanded":f,children:[h.jsx(I,{className:"flex-1 min-w-0",children:M()}),h.jsx(Zn,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),f&&h.jsx(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden",role:"listbox",children:h.jsx(I,{className:"overflow-auto",style:{maxHeight:i},children:e.length===0?h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:jp("common.no_options")}):e.map((k,N)=>{const S=String(k.value)===String(t);return h.jsx(ae,{type:"button",onClick:()=>!k.disabled&&_(k.value),disabled:k.disabled,className:`
|
||||
w-full px-4 py-3 text-left text-sm
|
||||
hover:bg-gray-100 dark:hover:bg-gray-700
|
||||
transition-colors
|
||||
${S?"bg-blue-50 dark:bg-blue-900/20":""}
|
||||
${k.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer"}
|
||||
`,role:"option","aria-selected":S,"data-item-value":k.value,"data-item-index":N,"data-item-selected":S,children:c?.item?Dp(c.item,{item:k,index:N,isSelected:S},`item-${N}`):a||C(k,S)},k.value)})})})]})};function NE(t){const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)}:null}const OE=({value:t,checked:e=!1,icon:n,label:r,variant:s="blue",customColor:o,className:i="",style:a,onChange:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(!1),g="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium border cursor-pointer transition-all";if(o){const b=NE(o),_=document.documentElement.classList.contains("dark");let x;return e?x={backgroundColor:o,color:"#ffffff",borderColor:o,opacity:u?.85:1}:x=b?{backgroundColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${u?_?.25:.12:_?.15:.06})`,color:o,borderColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${_?.3:.2})`}:{},h.jsxs(sn,{className:`${g} ${i}`,style:{...x,...a},onMouseEnter:()=>f(!0),onMouseLeave:()=>f(!1),id:c,...d,children:[h.jsx(kt,{type:"checkbox",value:t,checked:e,onChange:E=>{l?.(E)},className:"hidden"}),n&&h.jsx(re,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})}const y=e?{green:"bg-green-600 dark:bg-green-600 text-white dark:text-white border-green-700 dark:border-green-500 hover:bg-green-700 dark:hover:bg-green-500",orange:"bg-orange-600 dark:bg-orange-600 text-white dark:text-white border-orange-700 dark:border-orange-500 hover:bg-orange-700 dark:hover:bg-orange-500",yellow:"bg-yellow-600 dark:bg-yellow-600 text-white dark:text-white border-yellow-700 dark:border-yellow-500 hover:bg-yellow-700 dark:hover:bg-yellow-500",gray:"bg-gray-600 dark:bg-gray-500 text-white dark:text-white border-gray-700 dark:border-gray-400 hover:bg-gray-700 dark:hover:bg-gray-400",blue:"bg-blue-600 dark:bg-blue-600 text-white dark:text-white border-blue-700 dark:border-blue-500 hover:bg-blue-700 dark:hover:bg-blue-500"}[s]:{green:"bg-green-50 dark:bg-green-900/20 text-green-600 dark:text-green-400 border-green-200 dark:border-green-800 hover:bg-green-100 dark:hover:bg-green-900/30",orange:"bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400 border-orange-200 dark:border-orange-800 hover:bg-orange-100 dark:hover:bg-orange-900/30",yellow:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-600 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800 hover:bg-yellow-100 dark:hover:bg-yellow-900/30",gray:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border-gray-200 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-600",blue:"bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800 hover:bg-blue-100 dark:hover:bg-blue-900/30"}[s];return h.jsxs(sn,{className:`${g} ${y} ${i}`,style:a,id:c,...d,children:[h.jsx(kt,{type:"checkbox",value:t,checked:e,onChange:b=>{l?.(b)},className:"hidden"}),n&&h.jsx(re,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})},TE=({label:t,icon:e,iconPosition:n="left",position:r="right",className:s="",style:o,children:i})=>{const[a,l]=v.useState(!1),[c,d]=v.useState({top:0,left:0}),u=v.useRef(null),f=v.useRef(null),g=v.useCallback(()=>{if(!u.current)return;const p=u.current.getBoundingClientRect(),y=224;let b=r==="right"?p.right-y:p.left;const _=p.bottom+8;b<8&&(b=8),b+y>window.innerWidth-8&&(b=window.innerWidth-y-8),d({top:_,left:b})},[r]);v.useEffect(()=>{a&&g()},[a,g]),v.useEffect(()=>{if(!a)return;const p=()=>{l(!1)};return window.addEventListener("scroll",p,!0),window.addEventListener("resize",p),()=>{window.removeEventListener("scroll",p,!0),window.removeEventListener("resize",p)}},[a]),v.useEffect(()=>{const p=y=>{const b=y.target,_=u.current?.contains(b),x=f.current?.contains(b);!_&&!x&&l(!1)};return a&&document.addEventListener("mousedown",p),()=>{document.removeEventListener("mousedown",p)}},[a]);const m=a?Tn.createPortal(h.jsx("div",{ref:f,className:"fixed z-[9999] w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden",style:{top:c.top,left:c.left},onClick:()=>l(!1),children:i}),document.body):null;return h.jsxs(I,{className:`relative inline-block ${s}`,style:o,children:[h.jsxs(oe,{ref:u,onClick:()=>l(!a),className:"flex items-center gap-2",children:[n==="left"&&e&&h.jsx(re,{name:e,className:"w-4 h-4"}),t&&h.jsx(Q,{className:"text-sm font-medium",children:t}),n==="right"&&e&&h.jsx(re,{name:e,className:"w-4 h-4"})]}),m]})},AE=({label:t,icon:e,variant:n="default",disabled:r=!1,divider:s=!1,className:o="",style:i,onClick:a})=>{if(s)return h.jsx(I,{className:"h-px bg-gray-200 dark:bg-gray-700 my-1",style:i});const l=n==="danger"?"text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700",c=r?"opacity-50 cursor-not-allowed":"cursor-pointer",d=()=>{!r&&a&&a()};return h.jsxs(I,{onClick:d,className:`flex items-center gap-3 px-4 py-3Line truncated
|
||||
`)>-1?t.split(`
|
||||
`):t}function bT(t,e){const{element:n,datasetIndex:r,index:s}=e,o=t.getDatasetMeta(r).controller,{label:i,value:a}=o.getLabelAndValue(s);return{chart:t,label:i,parsed:o.getParsed(s),raw:t.data.datasets[r].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:r,element:n}}function bm(t,e){const n=t.chart.ctx,{body:r,footer:s,title:o}=t,{boxWidth:i,boxHeight:a}=e,l=Bt(e.bodyFont),c=Bt(e.titleFont),d=Bt(e.footerFont),u=o.length,f=s.length,g=r.length,m=pn(e.padding);let p=m.height,y=0,b=r.reduce((E,C)=>E+C.before.length+C.lines.length+C.after.length,0);if(b+=t.beforeBody.length+t.afterBody.length,u&&(p+=u*c.lineHeight+(u-1)*e.titleSpacing+e.titleMarginBottom),b){const E=e.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=g*E+(b-g)*l.lineHeight+(b-1)*e.bodySpacing}f&&(p+=e.footerMarginTop+f*d.lineHeight+(f-1)*e.footerSpacing);let _=0;const x=function(E){y=Math.max(y,n.measureText(E).width+_)};return n.save(),n.font=c.string,at(t.title,x),n.font=l.string,at(t.beforeBody.concat(t.afterBody),x),_=e.displayColors?i+2+e.boxPadding:0,at(r,E=>{at(E.before,x),at(E.lines,x),at(E.after,x)}),_=0,n.font=d.string,at(t.footer,x),n.restore(),y+=m.width,{width:y,height:p}}function yT(t,e){const{y:n,height:r}=e;return n<r/2?"top":n>t.height-r/2?"bottom":"center"}function vT(t,e,n,r){const{x:s,width:o}=r,i=n.caretSize+n.caretPadding;if(t==="left"&&s+o+i>e.width||t==="right"&&s-o-i<0)return!0}function xT(t,e,n,r){const{x:s,width:o}=n,{width:i,chartArea:{left:a,right:l}}=t;let c="center";return r==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=i-o/2&&(c="right"),vT(c,t,e,n)&&(c="center"),c}function ym(t,e,n){const r=n.yAlign||e.yAlign||yT(t,n);return{xAlign:n.xAlign||e.xAlign||xT(t,e,n,r),yAlign:r}}function wT(t,e){let{x:n,width:r}=t;return e==="right"?n-=r:e==="center"&&(n-=r/2),n}function kT(t,e,n){let{y:r,height:s}=t;return e==="top"?r+=n:e==="bottom"?r-=s+n:r-=s/2,r}function vm(t,e,n,r){const{caretSize:s,caretPadding:o,cornerRadius:i}=t,{xAlign:a,yAlign:l}=n,c=s+o,{topLeft:d,topRight:u,bottomLeft:f,bottomRight:g}=cs(i);let m=wT(e,a);const p=kT(e,l,c);return l==="center"?a==="left"?m+=c:a==="right"&&(m-=c):a==="left"?m-=Math.max(d,f)+s:a==="right"&&(m+=Math.max(u,g)+s),{x:en(m,0,r.width-e.width),y:en(p,0,r.height-e.height)}}function Ca(t,e,n){const r=pn(n.padding);return e==="center"?t.x+t.width/2:e==="right"?t.x+t.width-r.right:t.x+r.left}function xm(t){return Hn([],qn(t))}function CT(t,e,n){return ds(t,{tooltip:e,tooltipItems:n,type:"tooltip"})}function wm(t,e){const n=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return n?t.override(n):t}const km={beforeTitle:Yn,title(t){if(t.length>0){const e=t[0],n=e.chart.data.labels,r=n?n.length:0;if(this&&this.options&&this.options.mode==="dataset")return e.dataset.label||"";if(e.label)return e.label;if(r>0&&e.dataIndex<r)return n[e.dataIndex]}return""},afterTitle:Yn,beforeBody:Yn,beforeLabel:Yn,label(t){if(this&&this.options&&this.options.mode==="dataset")return t.label+": "+t.formattedValue||t.formattedValue;let e=t.dataset.label||"";e&&(e+=": ");const n=t.formattedValue;return lt(n)||(e+=n),e},labelColor(t){const n=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{borderColor:n.borderColor,backgroundColor:n.backgroundColor,borderWidth:n.borderWidth,borderDash:n.borderDash,borderDashOffset:n.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(t){const n=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{pointStyle:n.pointStyle,rotation:n.rotation}},afterLabel:Yn,afterBody:Yn,beforeFooter:Yn,footer:Yn,afterFooter:Yn};function tn(t,e,n,r){const s=t[e].call(n,r);return typeof s>"u"?km[e].call(n,r):s}class bd extends Dr{constructor(e){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=e.chart,this.options=e.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(e){this.options=e,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const e=this._cachedAnimations;if(e)return e;const n=this.chart,r=this.options.setContext(this.getContext()),s=r.enabled&&n.options.animation&&r.animations,o=new E0(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=CT(this.chart.getContext(),this,this._tooltipItems))}getTitle(e,n){const{callbacks:r}=n,s=tn(r,"beforeTitle",this,e),o=tn(r,"title",this,e),i=tn(r,"afterTitle",this,e);let a=[];return a=Hn(a,qn(s)),a=Hn(a,qn(o)),a=Hn(a,qn(i)),a}getBeforeBody(e,n){return xm(tn(n.call
Line truncated
|
||||
`,role:"option","aria-selected":S,"data-item-value":k.value,"data-item-index":N,"data-item-selected":S,children:c?.item?Wp(c.item,{item:k,index:N,isSelected:S},`item-${N}`):a||C(k,S)},k.value)})})})]})};function qE(e){const t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return t?{r:parseInt(t[1],16),g:parseInt(t[2],16),b:parseInt(t[3],16)}:null}const ZE=({value:e,checked:t=!1,icon:n,label:r,variant:s="blue",customColor:o,className:i="",style:a,onChange:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(!1),g="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium border cursor-pointer transition-all";if(o){const b=qE(o),_=document.documentElement.classList.contains("dark");let x;return t?x={backgroundColor:o,color:"#ffffff",borderColor:o,opacity:u?.85:1}:x=b?{backgroundColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${u?_?.25:.12:_?.15:.06})`,color:o,borderColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${_?.3:.2})`}:{},h.jsxs(ln,{className:`${g} ${i}`,style:{...x,...a},onMouseEnter:()=>f(!0),onMouseLeave:()=>f(!1),id:c,...d,children:[h.jsx(St,{type:"checkbox",value:e,checked:t,onChange:M=>{l?.(M)},className:"hidden"}),n&&h.jsx(oe,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})}const y=t?{green:"bg-green-600 dark:bg-green-600 text-white dark:text-white border-green-700 dark:border-green-500 hover:bg-green-700 dark:hover:bg-green-500",orange:"bg-orange-600 dark:bg-orange-600 text-white dark:text-white border-orange-700 dark:border-orange-500 hover:bg-orange-700 dark:hover:bg-orange-500",yellow:"bg-yellow-600 dark:bg-yellow-600 text-white dark:text-white border-yellow-700 dark:border-yellow-500 hover:bg-yellow-700 dark:hover:bg-yellow-500",gray:"bg-gray-600 dark:bg-gray-500 text-white dark:text-white border-gray-700 dark:border-gray-400 hover:bg-gray-700 dark:hover:bg-gray-400",blue:"bg-blue-600 dark:bg-blue-600 text-white dark:text-white border-blue-700 dark:border-blue-500 hover:bg-blue-700 dark:hover:bg-blue-500"}[s]:{green:"bg-green-50 dark:bg-green-900/20 text-green-600 dark:text-green-400 border-green-200 dark:border-green-800 hover:bg-green-100 dark:hover:bg-green-900/30",orange:"bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400 border-orange-200 dark:border-orange-800 hover:bg-orange-100 dark:hover:bg-orange-900/30",yellow:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-600 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800 hover:bg-yellow-100 dark:hover:bg-yellow-900/30",gray:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border-gray-200 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-600",blue:"bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800 hover:bg-blue-100 dark:hover:bg-blue-900/30"}[s];return h.jsxs(ln,{className:`${g} ${y} ${i}`,style:a,id:c,...d,children:[h.jsx(St,{type:"checkbox",value:e,checked:t,onChange:b=>{l?.(b)},className:"hidden"}),n&&h.jsx(oe,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})},QE=({label:e,icon:t,iconPosition:n="left",position:r="right",className:s="",style:o,children:i})=>{const[a,l]=v.useState(!1),[c,d]=v.useState({top:0,left:0}),u=v.useRef(null),f=v.useRef(null),g=v.useCallback(()=>{if(!u.current)return;const p=u.current.getBoundingClientRect(),y=224;let b=r==="right"?p.right-y:p.left;const _=p.bottom+8;b<8&&(b=8),b+y>window.innerWidth-8&&(b=window.innerWidth-y-8),d({top:_,left:b})},[r]);v.useEffect(()=>{a&&g()},[a,g]),v.useEffect(()=>{if(!a)return;const p=()=>{l(!1)};return window.addEventListener("scroll",p,!0),window.addEventListener("resize",p),()=>{window.removeEventListener("scroll",p,!0),window.removeEventListener("resize",p)}},[a]),v.useEffect(()=>{const p=y=>{const b=y.target,_=u.current?.contains(b),x=f.current?.contains(b);!_&&!x&&l(!1)};return a&&document.addEventListener("mousedown",p),()=>{document.removeEventListener("mousedown",p)}},[a]);const m=a?$n.createPortal(h.jsx("div",{ref:f,className:"fixed z-[9999] w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden",style:{top:c.top,left:c.left},onClick:()=>l(!1),children:i}),document.body):null;return h.jsxs(I,{className:`relative inline-block ${s}`,style:o,children:[h.jsxs(ae,{ref:u,onClick:()=>l(!a),className:"flex items-center gap-2",children:[n==="left"&&t&&h.jsx(oe,{name:t,className:"w-4 h-4"}),e&&h.jsx(Q,{className:"text-sm font-medium",children:e}),n==="right"&&t&&h.jsx(oe,{name:t,className:"w-4 h-4"})]}),m]})},JE=({label:e,icon:t,variant:n="default",disabled:r=!1,divider:s=!1,className:o="",style:i,onClick:a})=>{if(s)return h.jsx(I,{className:"h-px bg-gray-200 dark:bg-gray-700 my-1",style:i});const l=n==="danger"?"text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700",c=r?"opacity-50 cursor-not-allowed":"cursor-pointer",d=()=>{!r&&a&&a()};return h.jsxs(I,{onClick:d,className:`flex items-center gap-3 px-4 py-3Line truncated
|
||||
`)>-1?e.split(`
|
||||
`):e}function zO(e,t){const{element:n,datasetIndex:r,index:s}=t,o=e.getDatasetMeta(r).controller,{label:i,value:a}=o.getLabelAndValue(s);return{chart:e,label:i,parsed:o.getParsed(s),raw:e.data.datasets[r].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:r,element:n}}function Tm(e,t){const n=e.chart.ctx,{body:r,footer:s,title:o}=e,{boxWidth:i,boxHeight:a}=t,l=Kt(t.bodyFont),c=Kt(t.titleFont),d=Kt(t.footerFont),u=o.length,f=s.length,g=r.length,m=vn(t.padding);let p=m.height,y=0,b=r.reduce((M,C)=>M+C.before.length+C.lines.length+C.after.length,0);if(b+=e.beforeBody.length+e.afterBody.length,u&&(p+=u*c.lineHeight+(u-1)*t.titleSpacing+t.titleMarginBottom),b){const M=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=g*M+(b-g)*l.lineHeight+(b-1)*t.bodySpacing}f&&(p+=t.footerMarginTop+f*d.lineHeight+(f-1)*t.footerSpacing);let _=0;const x=function(M){y=Math.max(y,n.measureText(M).width+_)};return n.save(),n.font=c.string,lt(e.title,x),n.font=l.string,lt(e.beforeBody.concat(e.afterBody),x),_=t.displayColors?i+2+t.boxPadding:0,lt(r,M=>{lt(M.before,x),lt(M.lines,x),lt(M.after,x)}),_=0,n.font=d.string,lt(e.footer,x),n.restore(),y+=m.width,{width:y,height:p}}function VO(e,t){const{y:n,height:r}=t;return n<r/2?"top":n>e.height-r/2?"bottom":"center"}function HO(e,t,n,r){const{x:s,width:o}=r,i=n.caretSize+n.caretPadding;if(e==="left"&&s+o+i>t.width||e==="right"&&s-o-i<0)return!0}function UO(e,t,n,r){const{x:s,width:o}=n,{width:i,chartArea:{left:a,right:l}}=e;let c="center";return r==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=i-o/2&&(c="right"),HO(c,e,t,n)&&(c="center"),c}function Om(e,t,n){const r=n.yAlign||t.yAlign||VO(e,n);return{xAlign:n.xAlign||t.xAlign||UO(e,t,n,r),yAlign:r}}function BO(e,t){let{x:n,width:r}=e;return t==="right"?n-=r:t==="center"&&(n-=r/2),n}function jO(e,t,n){let{y:r,height:s}=e;return t==="top"?r+=n:t==="bottom"?r-=s+n:r-=s/2,r}function Am(e,t,n,r){const{caretSize:s,caretPadding:o,cornerRadius:i}=e,{xAlign:a,yAlign:l}=n,c=s+o,{topLeft:d,topRight:u,bottomLeft:f,bottomRight:g}=Es(i);let m=BO(t,a);const p=jO(t,l,c);return l==="center"?a==="left"?m+=c:a==="right"&&(m-=c):a==="left"?m-=Math.max(d,f)+s:a==="right"&&(m+=Math.max(u,g)+s),{x:sn(m,0,r.width-t.width),y:sn(p,0,r.height-t.height)}}function Aa(e,t,n){const r=vn(n.padding);return t==="center"?e.x+e.width/2:t==="right"?e.x+e.width-r.right:e.x+r.left}function Dm(e){return Kn([],nr(e))}function WO(e,t,n){return Ms(e,{tooltip:t,tooltipItems:n,type:"tooltip"})}function Im(e,t){const n=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return n?e.override(n):e}const Lm={beforeTitle:Rn,title(e){if(e.length>0){const t=e[0],n=t.chart.data.labels,r=n?n.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(r>0&&t.dataIndex<r)return n[t.dataIndex]}return""},afterTitle:Rn,beforeBody:Rn,beforeLabel:Rn,label(e){if(this&&this.options&&this.options.mode==="dataset")return e.label+": "+e.formattedValue||e.formattedValue;let t=e.dataset.label||"";t&&(t+=": ");const n=e.formattedValue;return dt(n)||(t+=n),t},labelColor(e){const n=e.chart.getDatasetMeta(e.datasetIndex).controller.getStyle(e.dataIndex);return{borderColor:n.borderColor,backgroundColor:n.backgroundColor,borderWidth:n.borderWidth,borderDash:n.borderDash,borderDashOffset:n.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(e){const n=e.chart.getDatasetMeta(e.datasetIndex).controller.getStyle(e.dataIndex);return{pointStyle:n.pointStyle,rotation:n.rotation}},afterLabel:Rn,afterBody:Rn,beforeFooter:Rn,footer:Rn,afterFooter:Rn};function on(e,t,n,r){const s=e[t].call(n,r);return typeof s>"u"?Lm[t].call(n,r):s}class Cd extends Br{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const n=this.chart,r=this.options.setContext(this.getContext()),s=r.enabled&&n.options.animation&&r.animations,o=new z0(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=WO(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,n){const{callbacks:r}=n,s=on(r,"beforeTitle",this,t),o=on(r,"title",this,t),i=on(r,"afterTitle",this,t);let a=[];return a=Kn(a,nr(s)),a=Kn(a,nr(o)),a=Kn(a,nr(i)),a}getBeforeBody(t,n){return Dm(on(n.call
Line truncated
|
||||
//# sourceMappingURL=components.iife.js.map
|
||||
@@ -114,8 +114,6 @@
|
||||
"props": { "className": "admin-page-content-responsive-fluid" },
|
||||
"children": [
|
||||
{
|
||||
"type": "basic",
|
||||
"name": "Div",
|
||||
"partial": "partials/admin_extension_language_packs/_content.json"
|
||||
}
|
||||
]
|
||||
|
||||
-2
@@ -12,8 +12,6 @@
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "basic",
|
||||
"name": "Div",
|
||||
"partial": "partials/admin_language_pack_list/_content.json"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -123,46 +123,21 @@ const getComponentMap = (): Record<string, React.ComponentType<any>> => {
|
||||
|
||||
/**
|
||||
* 조건 문자열을 평가합니다.
|
||||
* 복잡한 JavaScript 표현식도 지원합니다 (&&, ||, length 등)
|
||||
*
|
||||
* 판정은 엔진(`G7Core.evaluateCondition`)에 위임합니다. 종전에는 이 파일이
|
||||
* `new Function('row', ...)` 로 자체 평가기를 두어 엔진과 판정이 갈렸고,
|
||||
* `_local`/`_global` 을 참조하는 조건에서 예외가 나면 여기서는 `false`(자식이 사라짐),
|
||||
* DataGrid 에서는 `true`(항상 표시)로 **반대 방향** 결과가 났습니다.
|
||||
*/
|
||||
const evaluateCondition = (condition: string, row: any): boolean => {
|
||||
if (!condition) return true;
|
||||
|
||||
const match = condition.match(/^\{\{(.+)\}\}$/);
|
||||
if (!match) return true;
|
||||
|
||||
const expr = match[1].trim();
|
||||
|
||||
// 디버깅: row 데이터와 표현식 확인
|
||||
logger.log('evaluateCondition:', { condition, expr, row, rowStatus: row?.status });
|
||||
|
||||
// 복잡한 표현식 (&&, ||, length, > 등)이 포함된 경우 eval 사용
|
||||
if (/[&|<>=!]/.test(expr) || expr.includes('.length')) {
|
||||
try {
|
||||
// row 컨텍스트에서 표현식 평가
|
||||
// eslint-disable-next-line no-new-func
|
||||
const evaluator = new Function('row', `return ${expr}`);
|
||||
const result = !!evaluator(row);
|
||||
logger.log('evaluateCondition result:', result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.warn('evaluateCondition: 표현식 평가 실패:', expr, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// !row.field 패턴
|
||||
if (expr.startsWith('!row.')) {
|
||||
const field = expr.slice(5);
|
||||
return !row[field];
|
||||
}
|
||||
|
||||
// row.field 패턴
|
||||
if (expr.startsWith('row.')) {
|
||||
const field = expr.slice(4);
|
||||
return !!row[field];
|
||||
const G7Core = (window as any).G7Core;
|
||||
if (typeof G7Core?.evaluateCondition === 'function') {
|
||||
return G7Core.evaluateCondition(condition, { row, item: row, $item: row });
|
||||
}
|
||||
|
||||
logger.warn('G7Core.evaluateCondition 미노출 — 조건을 평가할 수 없습니다:', condition);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -43,8 +43,18 @@ const logger = ((window as any).G7Core?.createLogger?.('Comp:DataGrid')) ?? {
|
||||
const t = (key: string, params?: Record<string, string | number>) =>
|
||||
(window as any).G7Core?.t?.(key, params) ?? key;
|
||||
|
||||
// cellChildren 및 expandChildren 렌더링을 위한 컴포넌트 맵
|
||||
const componentMap: Record<string, React.ComponentType<any>> = {
|
||||
/**
|
||||
* 반복 렌더 서브트리용 컴포넌트 맵 **폴백**.
|
||||
*
|
||||
* 정본은 템플릿 레지스트리(`G7Core.getComponentMap()`)다 — CardGrid 와 같은 출처를 쓴다.
|
||||
* 이 열거는 런타임 전역이 아직 없는 상황(단위 테스트 등)에서만 쓰인다.
|
||||
*
|
||||
* 종전에는 이 열거가 유일한 출처였고, 등록된 117개 중 29개만 담고 있어 나머지 88개가
|
||||
* `cellChildren`/`expandChildren`/`subRowChildren`/`footerCells` 안에서만 조용히 사라졌다.
|
||||
* 실측: `/admin/identity/logs` 확장 행의 `Pre`(부가 정보 JSON)가 라벨만 남고 미렌더 —
|
||||
* 같은 `Pre` 가 모달(DynamicRenderer 경로)에서는 정상 렌더돼 경로 비대칭이었다.
|
||||
*/
|
||||
const FALLBACK_COMPONENT_MAP: Record<string, React.ComponentType<any>> = {
|
||||
// basic 컴포넌트
|
||||
Div,
|
||||
Span,
|
||||
@@ -79,6 +89,22 @@ const componentMap: Record<string, React.ComponentType<any>> = {
|
||||
ActionMenu,
|
||||
};
|
||||
|
||||
/**
|
||||
* 반복 렌더 서브트리에 넘길 컴포넌트 맵을 반환합니다.
|
||||
*
|
||||
* 템플릿 레지스트리 전체를 쓰므로 `components.json` 에 등록된 컴포넌트는 단발 렌더 경로와
|
||||
* 반복 렌더 경로에서 **동일하게** 조회된다. 전역이 없으면 폴백 열거로 내려간다.
|
||||
*
|
||||
* @returns 컴포넌트명 → React 컴포넌트 맵
|
||||
*/
|
||||
const getComponentMap = (): Record<string, React.ComponentType<any>> => {
|
||||
const registry = (window as any).G7Core?.getComponentMap?.();
|
||||
if (registry && Object.keys(registry).length > 0) {
|
||||
return registry;
|
||||
}
|
||||
return FALLBACK_COMPONENT_MAP;
|
||||
};
|
||||
|
||||
export interface DataGridCellChild {
|
||||
id?: string;
|
||||
type: 'basic' | 'composite';
|
||||
@@ -436,7 +462,11 @@ const safeRenderValue = (value: any): React.ReactNode => {
|
||||
|
||||
/**
|
||||
* subRow 컨테이너의 표시 조건을 평가합니다.
|
||||
* JSX 인라인 조건에서 사용되므로 엔진 위임 불가 — 직접 평가합니다.
|
||||
*
|
||||
* 판정은 엔진(`G7Core.evaluateCondition`)에 위임합니다. 종전에는 이 파일이
|
||||
* `new Function('row', ...)` 로 자체 평가기를 두어, `_local`/`_global` 을 참조하는
|
||||
* 조건에서 예외가 나면 CardGrid 는 자식이 사라지고 DataGrid 는 항상 표시되는
|
||||
* **반대 방향** 결과가 났습니다. 엔진 위임으로 두 컴포넌트의 판정이 같아집니다.
|
||||
*
|
||||
* @param condition 조건 문자열 (예: "{{row.charge_policy !== 'free'}}")
|
||||
* @param row 현재 행 데이터
|
||||
@@ -445,21 +475,13 @@ const safeRenderValue = (value: any): React.ReactNode => {
|
||||
const evaluateSubRowCondition = (condition: string | undefined, row: any): boolean => {
|
||||
if (!condition) return true;
|
||||
|
||||
// {{...}} 패턴 추출
|
||||
const match = condition.match(/^\{\{(.+)\}\}$/);
|
||||
if (!match) return true;
|
||||
|
||||
const expr = match[1].trim();
|
||||
|
||||
try {
|
||||
// row 컨텍스트에서 표현식 평가
|
||||
// eslint-disable-next-line no-new-func
|
||||
const evaluator = new Function('row', `return ${expr}`);
|
||||
return !!evaluator(row);
|
||||
} catch (error) {
|
||||
logger.warn('subRow 조건 평가 실패:', condition, error);
|
||||
return true;
|
||||
const G7Core = (window as any).G7Core;
|
||||
if (typeof G7Core?.evaluateCondition === 'function') {
|
||||
return G7Core.evaluateCondition(condition, { row, item: row, $item: row });
|
||||
}
|
||||
|
||||
logger.warn('G7Core.evaluateCondition 미노출 — subRow 조건을 평가할 수 없습니다:', condition);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -486,7 +508,7 @@ const renderCellChildren = (
|
||||
return G7Core.renderItemChildren(
|
||||
cellChildren,
|
||||
context,
|
||||
componentMap,
|
||||
getComponentMap(),
|
||||
keyPrefix,
|
||||
componentContext ? { componentContext } : undefined
|
||||
);
|
||||
@@ -496,6 +518,39 @@ const renderCellChildren = (
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 푸터 셀의 `text` 를 렌더링합니다.
|
||||
*
|
||||
* `components.json` 의 `skipBindingKeys` 에 `footerCells` 가 들어가면서 DynamicRenderer 는
|
||||
* 이 키를 선평가하지 않는다 — 해석 책임이 반복 렌더 경로로 넘어왔다. 그런데 푸터 렌더는
|
||||
* `children` 만 `renderCellChildren` 으로 넘기고 `text` 는 원문을 그대로 출력해 왔고,
|
||||
* 그 결과 `{{order.data?.total_quantity ?? 0}}개` 같은 값이 화면에 노출됐다
|
||||
* (실측: `/admin/ecommerce/orders/:orderNumber` 합계 행 3셀).
|
||||
*
|
||||
* 바인딩/번역 토큰이 없는 순수 문자열은 그대로 돌려주어 불필요한 래핑을 만들지 않는다.
|
||||
*
|
||||
* @param text 푸터 셀 텍스트 (바인딩 표현식 포함 가능)
|
||||
* @param keyPrefix 키 접두사
|
||||
* @param componentContext 컴포넌트 컨텍스트 (state/setState)
|
||||
* @returns 해석된 React 노드
|
||||
*/
|
||||
const renderFooterCellText = (
|
||||
text: string | undefined,
|
||||
keyPrefix: string,
|
||||
componentContext?: { state?: any; setState?: (updates: any) => void }
|
||||
): React.ReactNode => {
|
||||
if (!text) return '';
|
||||
if (!text.includes('{{') && !text.includes('$t:')) return text;
|
||||
|
||||
return renderCellChildren(
|
||||
[{ type: 'basic', name: 'Span', text }],
|
||||
{},
|
||||
null,
|
||||
keyPrefix,
|
||||
componentContext
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* subRowChildren 배열을 React 요소로 렌더링합니다.
|
||||
*
|
||||
@@ -521,7 +576,7 @@ const renderSubRowChildren = (
|
||||
return G7Core.renderItemChildren(
|
||||
subRowChildren,
|
||||
context,
|
||||
componentMap,
|
||||
getComponentMap(),
|
||||
keyPrefix,
|
||||
componentContext ? { componentContext } : undefined
|
||||
);
|
||||
@@ -1033,7 +1088,7 @@ export const DataGrid: React.FC<DataGridProps> = ({
|
||||
row,
|
||||
expandContext,
|
||||
componentContext: __componentContext,
|
||||
componentMap,
|
||||
componentMap: getComponentMap(),
|
||||
keyPrefix: `expand-${row[idField]}`,
|
||||
});
|
||||
}
|
||||
@@ -1226,7 +1281,7 @@ export const DataGrid: React.FC<DataGridProps> = ({
|
||||
<Span className={cell.className || 'text-gray-900 dark:text-gray-100'}>
|
||||
{cell.children && cell.children.length > 0
|
||||
? renderCellChildren(cell.children, {}, null, `footer-mobile-${idx}`, __componentContext)
|
||||
: cell.text || ''}
|
||||
: renderFooterCellText(cell.text, `footer-mobile-text-${idx}`, __componentContext)}
|
||||
</Span>
|
||||
</Div>
|
||||
);
|
||||
@@ -1543,7 +1598,7 @@ export const DataGrid: React.FC<DataGridProps> = ({
|
||||
>
|
||||
{cell.children && cell.children.length > 0
|
||||
? renderCellChildren(cell.children, {}, null, `footer-${column.field}`, __componentContext)
|
||||
: cell.text || ''}
|
||||
: renderFooterCellText(cell.text, `footer-text-${column.field}`, __componentContext)}
|
||||
</Td>
|
||||
);
|
||||
});
|
||||
|
||||
+62
@@ -66,13 +66,17 @@ describe('CardGrid', () => {
|
||||
|
||||
// mockG7Core 참조 변수 (beforeEach에서 초기화)
|
||||
let mockG7Core: any;
|
||||
// 조건 판정은 엔진 위임이다 — 컴포넌트가 자체 평가기를 두지 않는다는 계약
|
||||
const mockEvaluateCondition = vi.fn(() => true);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockEvaluateCondition.mockImplementation(() => true);
|
||||
// 기존 G7Core를 유지하면서 renderItemChildren만 추가/덮어쓰기
|
||||
mockG7Core = {
|
||||
...(window as any).G7Core,
|
||||
renderItemChildren: mockRenderItemChildren,
|
||||
evaluateCondition: mockEvaluateCondition,
|
||||
};
|
||||
(window as any).G7Core = mockG7Core;
|
||||
});
|
||||
@@ -619,4 +623,62 @@ describe('CardGrid', () => {
|
||||
expect(skeletonBars).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('조건 평가 위임 계약 (engine-v1.56.1)', () => {
|
||||
/** 조건이 걸린 자식 하나만 가진 셀 정의 */
|
||||
const withCondition = (condition: string): CardGridCellChild[] => [
|
||||
{ type: 'basic', name: 'Span', id: 'conditional', text: '조건부', condition },
|
||||
];
|
||||
|
||||
it('조건 판정을 G7Core.evaluateCondition 에 위임한다 (자체 평가기 없음)', () => {
|
||||
render(
|
||||
<CardGrid data={[mockData[0]]} cardColumns={[{ id: 'c', cellChildren: withCondition('{{row.id === 1}}') }]} />
|
||||
);
|
||||
|
||||
expect(mockEvaluateCondition).toHaveBeenCalled();
|
||||
const [condition, context] = mockEvaluateCondition.mock.calls[0] as unknown as [string, any];
|
||||
expect(condition).toBe('{{row.id === 1}}');
|
||||
// 항목은 row/item/$item 세 이름으로 모두 도달해야 한다 (작성 관행이 셋 다 존재)
|
||||
expect(context.row).toEqual(mockData[0]);
|
||||
expect(context.item).toEqual(mockData[0]);
|
||||
expect(context.$item).toEqual(mockData[0]);
|
||||
});
|
||||
|
||||
it('엔진이 false 를 돌려주면 그 자식은 렌더 대상에서 빠진다', () => {
|
||||
mockEvaluateCondition.mockImplementation(() => false);
|
||||
|
||||
render(
|
||||
<CardGrid data={[mockData[0]]} cardColumns={[{ id: 'c', cellChildren: withCondition('{{row.id === 999}}') }]} />
|
||||
);
|
||||
|
||||
const passedChildren = mockRenderItemChildren.mock.calls[0]?.[0] ?? [];
|
||||
expect(passedChildren).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('_local 참조 조건도 예외 없이 엔진이 처리한다', () => {
|
||||
// 종전 자체 평가기는 `new Function('row', ...)` 이라 `_local` 이 스코프에 없어
|
||||
// ReferenceError 가 났고, catch 가 false 를 돌려줘 자식이 조용히 사라졌다.
|
||||
mockEvaluateCondition.mockImplementation(() => true);
|
||||
|
||||
expect(() =>
|
||||
render(
|
||||
<CardGrid data={[mockData[0]]} cardColumns={[{ id: 'c', cellChildren: withCondition('{{_local.expanded}}') }]} />
|
||||
)
|
||||
).not.toThrow();
|
||||
|
||||
expect(mockEvaluateCondition).toHaveBeenCalledWith('{{_local.expanded}}', expect.any(Object));
|
||||
expect(mockRenderItemChildren.mock.calls[0]?.[0]).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('엔진 API 가 없으면 조건을 감춤이 아니라 표시로 처리한다', () => {
|
||||
// 평가 수단이 없다는 이유로 내용을 지우면 화면이 통째로 비어 원인 파악이 불가능하다.
|
||||
(window as any).G7Core = { ...mockG7Core, evaluateCondition: undefined };
|
||||
|
||||
render(
|
||||
<CardGrid data={[mockData[0]]} cardColumns={[{ id: 'c', cellChildren: withCondition('{{row.id === 1}}') }]} />
|
||||
);
|
||||
|
||||
expect(mockRenderItemChildren.mock.calls[0]?.[0]).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
+221
-4
@@ -53,6 +53,44 @@ function shallowArrayEqual<T>(a: T[], b: T[]): boolean {
|
||||
return a.every((item, index) => item === b[index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* window.G7Core.evaluateCondition mock
|
||||
*
|
||||
* 런타임에서는 엔진의 `evaluateStringCondition` 이 처리하며, 컨텍스트에는 컴포넌트가
|
||||
* 넘긴 `row` 뿐 아니라 엔진이 병합한 `_global`/`_local`/`_computed` 도 들어 있다
|
||||
* (`G7CoreGlobals.evaluateCondition`). mock 도 그 계약을 그대로 따라야 `_local` 을
|
||||
* 참조하는 조건이 실제로 검증된다 — 종전처럼 `row` 만 파라미터로 넘기면 `_local` 이
|
||||
* 스코프에 없어 무조건 false 로 떨어져, 통과해도 아무것도 증명하지 못한다.
|
||||
*
|
||||
* 실패 시 false 는 엔진 `evaluateStringCondition` 의 catch 와 같은 방향이다.
|
||||
*/
|
||||
const mockEngineState: { _local: Record<string, any>; _global: Record<string, any> } = {
|
||||
_local: {},
|
||||
_global: {},
|
||||
};
|
||||
|
||||
const mockEvaluateCondition = vi.fn((condition: string, ctx: Record<string, any>) => {
|
||||
if (!condition) return true;
|
||||
const match = condition.match(/^\{\{([\s\S]+)\}\}$/);
|
||||
if (!match) return true;
|
||||
|
||||
const context = {
|
||||
_global: mockEngineState._global,
|
||||
_local: mockEngineState._local,
|
||||
_computed: {},
|
||||
...(ctx || {}),
|
||||
};
|
||||
const names = Object.keys(context);
|
||||
const values = names.map((n) => (context as any)[n]);
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-new-func
|
||||
return !!new Function(...names, `return ${match[1].trim()}`)(...values);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// window.G7Core.useResponsive mock
|
||||
const mockUseResponsive = vi.fn(() => ({
|
||||
width: 1024,
|
||||
@@ -72,6 +110,9 @@ describe('DataGrid', () => {
|
||||
(window as any).G7Core = {
|
||||
...originalG7Core,
|
||||
useResponsive: mockUseResponsive,
|
||||
// subRowCondition 판정은 엔진(G7Core.evaluateCondition)에 위임한다.
|
||||
// 런타임과 같은 계약(조건 문자열 + row 컨텍스트 → boolean)을 최소 구현으로 세운다.
|
||||
evaluateCondition: mockEvaluateCondition,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -859,7 +900,7 @@ describe('DataGrid', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('subRowCondition이 잘못된 표현식이어도 오류 없이 기본값(true)으로 처리됨', () => {
|
||||
it('subRowCondition이 해석되지 않는 경로면 오류 없이 숨김 처리됨', () => {
|
||||
// 콘솔 경고를 억제
|
||||
const consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
@@ -872,13 +913,79 @@ describe('DataGrid', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
// 잘못된 조건이어도 기본값 true로 모든 행에 서브 행이 렌더링됨
|
||||
const subRowContents = screen.getAllByTestId('sub-row-content');
|
||||
expect(subRowContents).toHaveLength(3);
|
||||
// 조건 판정을 엔진에 위임하면서, 해석되지 않는 조건은 다른 화면 요소의 `if` 와
|
||||
// 같은 규칙으로 false(숨김)가 된다. 종전에는 이 컴포넌트만 true(표시)로 갈렸다.
|
||||
expect(screen.queryAllByTestId('sub-row-content')).toHaveLength(0);
|
||||
|
||||
consoleWarn.mockRestore();
|
||||
});
|
||||
|
||||
it('subRowCondition이 _local 을 참조해도 예외 없이 엔진이 판정한다', () => {
|
||||
// 종전에는 이 컴포넌트가 `new Function('row', ...)` 로 자체 평가해서, row 스코프에
|
||||
// 없는 `_local` 을 만나면 ReferenceError 가 났다. 위임 이후에는 엔진이 `_local` 을
|
||||
// 컨텍스트에 병합해 넘기므로 정상 판정된다.
|
||||
mockEngineState._local = { expanded: true };
|
||||
|
||||
expect(() =>
|
||||
render(
|
||||
<DataGrid
|
||||
columns={mockColumns}
|
||||
data={mockData}
|
||||
subRowRender={(row) => <span data-testid="sub-row-content">{row.name}</span>}
|
||||
subRowCondition="{{_local.expanded}}"
|
||||
/>
|
||||
)
|
||||
).not.toThrow();
|
||||
|
||||
// 조건이 참이므로 모든 행에 서브 행이 렌더링된다
|
||||
expect(screen.getAllByTestId('sub-row-content')).toHaveLength(3);
|
||||
// 판정은 엔진에 위임됐다 (자체 평가기 부재 확인)
|
||||
expect(mockEvaluateCondition).toHaveBeenCalledWith(
|
||||
'{{_local.expanded}}',
|
||||
expect.objectContaining({ row: expect.any(Object) })
|
||||
);
|
||||
|
||||
mockEngineState._local = {};
|
||||
});
|
||||
|
||||
it('subRowCondition 의 _local 값이 거짓이면 서브 행이 숨겨진다', () => {
|
||||
mockEngineState._local = { expanded: false };
|
||||
|
||||
render(
|
||||
<DataGrid
|
||||
columns={mockColumns}
|
||||
data={mockData}
|
||||
subRowRender={(row) => <span data-testid="sub-row-content">{row.name}</span>}
|
||||
subRowCondition="{{_local.expanded}}"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryAllByTestId('sub-row-content')).toHaveLength(0);
|
||||
|
||||
mockEngineState._local = {};
|
||||
});
|
||||
|
||||
it('G7Core.evaluateCondition 미노출 시에는 서브 행을 표시한다 (폴백 계약)', () => {
|
||||
const consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const saved = (window as any).G7Core.evaluateCondition;
|
||||
delete (window as any).G7Core.evaluateCondition;
|
||||
|
||||
render(
|
||||
<DataGrid
|
||||
columns={mockColumns}
|
||||
data={mockData}
|
||||
subRowRender={(row) => <span data-testid="sub-row-content">{row.name}</span>}
|
||||
subRowCondition="{{_local.expanded}}"
|
||||
/>
|
||||
);
|
||||
|
||||
// 엔진이 없으면 판정할 수 없으므로 감추지 않고 표시한다 (CardGrid 와 같은 방향)
|
||||
expect(screen.getAllByTestId('sub-row-content')).toHaveLength(3);
|
||||
|
||||
(window as any).G7Core.evaluateCondition = saved;
|
||||
consoleWarn.mockRestore();
|
||||
});
|
||||
|
||||
it('subRowCondition이 빈 문자열이면 모든 행에 서브 행이 렌더링됨', () => {
|
||||
render(
|
||||
<DataGrid
|
||||
@@ -1103,4 +1210,114 @@ describe('DataGrid', () => {
|
||||
expect(screen.getByText('타인')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 푸터 행(footerCells)의 `text` 는 반복 렌더 경로가 해석해야 한다.
|
||||
*
|
||||
* `components.json` 의 `skipBindingKeys` 에 `footerCells` 가 추가되면서 DynamicRenderer 가
|
||||
* 더 이상 이 키를 선평가하지 않는다. 그런데 DataGrid 의 푸터 렌더는 `cell.children` 만
|
||||
* `renderCellChildren` 으로 넘기고 `cell.text` 는 원문 그대로 출력해 왔다.
|
||||
* 그 결과 `{{order.data?.total_quantity ?? 0}}개` 같은 값이 화면에 **원본 문자열로 노출**된다.
|
||||
* (브라우저 실측: `/admin/ecommerce/orders/:orderNumber` 합계 행 3셀)
|
||||
*/
|
||||
describe('푸터 행 text 바인딩 해석', () => {
|
||||
const footerColumns: DataGridColumn[] = [
|
||||
{ field: 'name', header: '이름' },
|
||||
{ field: 'qty', header: '수량' },
|
||||
];
|
||||
const footerData = [
|
||||
{ name: '상품A', qty: 2 },
|
||||
{ name: '상품B', qty: 3 },
|
||||
];
|
||||
|
||||
it('footerCells[].text 의 바인딩 표현식이 원본 문자열로 노출되지 않는다', () => {
|
||||
render(
|
||||
<DataGrid
|
||||
columns={footerColumns}
|
||||
data={footerData}
|
||||
footerCells={[
|
||||
{ field: 'name', text: '합계' },
|
||||
{ field: 'qty', text: '{{order.data?.total_quantity ?? 0}}개' },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const tfoot = document.querySelector('tfoot');
|
||||
expect(tfoot).not.toBeNull();
|
||||
expect(tfoot!.textContent).toContain('합계');
|
||||
expect(tfoot!.textContent).not.toContain('{{');
|
||||
});
|
||||
|
||||
it('파이프가 붙은 footerCells[].text 도 원본 문자열로 노출되지 않는다', () => {
|
||||
render(
|
||||
<DataGrid
|
||||
columns={footerColumns}
|
||||
data={footerData}
|
||||
footerCells={[
|
||||
{ field: 'name', text: '합계' },
|
||||
{ field: 'qty', text: '{{order.data?.total_earned_points_amount | number}}P' },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const tfoot = document.querySelector('tfoot');
|
||||
expect(tfoot!.textContent).not.toContain('{{');
|
||||
expect(tfoot!.textContent).not.toContain('| number');
|
||||
});
|
||||
|
||||
it('바인딩이 없는 순수 문자열 text 는 그대로 출력된다', () => {
|
||||
render(
|
||||
<DataGrid
|
||||
columns={footerColumns}
|
||||
data={footerData}
|
||||
footerCells={[
|
||||
{ field: 'name', text: '합계' },
|
||||
{ field: 'qty', text: '5개' },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const tfoot = document.querySelector('tfoot');
|
||||
expect(tfoot!.textContent).toContain('합계');
|
||||
expect(tfoot!.textContent).toContain('5개');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 반복 렌더 서브트리(cellChildren/expandChildren/subRowChildren/footerCells)의 컴포넌트
|
||||
* 조회는 템플릿 레지스트리 전체를 대상으로 해야 한다.
|
||||
*
|
||||
* 종전에는 이 파일이 29개짜리 **손으로 박은 componentMap** 을 갖고 있어, 등록된 117개 중
|
||||
* 88개가 반복 경로에서만 조용히 사라졌다. 실측 확인: `/admin/identity/logs` 확장 행의
|
||||
* `Pre` 블록(부가 정보 JSON)이 라벨만 남고 내용이 통째로 렌더되지 않음
|
||||
* (콘솔: `renderItemChildren: 컴포넌트를 찾을 수 없습니다: Pre`).
|
||||
* 같은 `Pre` 가 모달(DynamicRenderer 경로)에서는 정상 렌더된다 — 경로 비대칭이다.
|
||||
*/
|
||||
describe('반복 렌더 컴포넌트 레지스트리', () => {
|
||||
it('cellChildren 렌더 시 템플릿 레지스트리 전체(G7Core.getComponentMap)를 넘긴다', () => {
|
||||
const seen: Record<string, unknown>[] = [];
|
||||
(window as any).G7Core = {
|
||||
...(window as any).G7Core,
|
||||
getComponentMap: () => ({ Div: 'div', Span: 'span', Pre: 'pre', Code: 'code' }),
|
||||
renderItemChildren: (_children: any, _ctx: any, componentMap: Record<string, unknown>) => {
|
||||
seen.push(componentMap);
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
render(
|
||||
<DataGrid
|
||||
columns={[
|
||||
{ field: 'name', header: '이름' },
|
||||
{ field: 'meta', header: '메타', cellChildren: [{ type: 'basic', name: 'Pre', text: '{{row.meta}}' }] },
|
||||
]}
|
||||
data={[{ name: 'A', meta: '{"a":1}' }]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(seen.length).toBeGreaterThan(0);
|
||||
// 레지스트리에 등록된 컴포넌트는 반복 경로에서도 조회 가능해야 한다
|
||||
expect(Object.keys(seen[0])).toContain('Pre');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -22,7 +22,7 @@
|
||||
"url": "https://sirsoft.com"
|
||||
},
|
||||
"release_date": "2026-05-15",
|
||||
"g7_version": ">=7.0.0",
|
||||
"g7_version": ">=7.0.6",
|
||||
"dependencies": {
|
||||
"modules": {},
|
||||
"plugins": {}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* E2E: 단일 바인딩 식이 렌더 경로와 무관하게 같은 값으로 해석되는지 (배포 번들 기준)
|
||||
*
|
||||
* 배경: "이 문자열이 단일 바인딩인가", "이 식이 표현식인가 경로인가" 를 판정하는
|
||||
* 로직이 엔진 곳곳에 복제되면서 복사본마다 인식 문자 집합이 갈라졌고, 그 결과
|
||||
* **같은 작성이 어느 렌더 경로를 타느냐에 따라 다른 결과**를 냈다.
|
||||
* (`{{query['sales_status[]']}}` 가 prop 에서는 undefined, `if` 에서는 정상 등)
|
||||
*
|
||||
* 단위 테스트가 소스 레벨을 잠그지만 실제 사용자가 보는 것은 빌드된 번들이므로
|
||||
* 브라우저에서 배포 번들의 공개 API 를 그대로 호출해 결과를 잠근다.
|
||||
*
|
||||
* 파일 헤더의 시나리오 목록은 아래 test 들이 실제로 다는 `@scenario` 의 합집합이어야 한다.
|
||||
* 반복 렌더의 항목별 조건 평가(`iteration_item_condition`)는 별개 축이라
|
||||
* `iteration-condition-render.spec.ts` 가 담당한다.
|
||||
*
|
||||
* @scenario binding_quoted_bracket_symmetry, binding_pipe_type_preservation, binding_brace_in_pipe, binding_empty_expression
|
||||
* @effects same_expression_same_value_across_paths
|
||||
*/
|
||||
import { test, expect } from '../fixtures/auth';
|
||||
|
||||
test.describe('렌더 경로 간 바인딩 해석 대칭', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// 코어 번들이 부팅된 화면이면 충분하다(로그인 화면도 엔진을 부팅한다).
|
||||
await page.goto('/admin/login');
|
||||
await page.waitForFunction(
|
||||
() => Boolean((window as any).G7Core?.renderItemChildren),
|
||||
null,
|
||||
{ timeout: 30_000 },
|
||||
);
|
||||
});
|
||||
|
||||
// @scenario binding_quoted_bracket_symmetry
|
||||
// @effects same_expression_same_value_across_paths
|
||||
test('따옴표 키 인덱싱이 반복 렌더와 조건 판정에서 같은 값을 본다', async ({ page }) => {
|
||||
const result = await page.evaluate(() => {
|
||||
const g7 = (window as any).G7Core;
|
||||
const Span = function Span() { return null; };
|
||||
const context = { query: { 'sales_status[]': ['selling', 'soldout'] } };
|
||||
|
||||
const nodes = g7.renderItemChildren(
|
||||
[{ type: 'basic', name: 'Span', props: { title: "{{query['sales_status[]']}}" } }],
|
||||
context,
|
||||
{ Span },
|
||||
'e2e-symmetry',
|
||||
);
|
||||
|
||||
return {
|
||||
hasEvaluateCondition: typeof g7.evaluateCondition === 'function',
|
||||
viaRepeat: (nodes as any[])[0]?.props?.title ?? null,
|
||||
viaCondition: typeof g7.evaluateCondition === 'function'
|
||||
? g7.evaluateCondition("{{query['sales_status[]']}}", context)
|
||||
: null,
|
||||
};
|
||||
});
|
||||
|
||||
expect(result.hasEvaluateCondition, 'G7Core.evaluateCondition 이 노출되지 않음').toBe(true);
|
||||
// 수정 전: 좁은 방언이 "단순 경로" 로 오판해 경로 탐색 → undefined
|
||||
expect(result.viaRepeat).toEqual(['selling', 'soldout']);
|
||||
expect(result.viaCondition).toBe(true);
|
||||
});
|
||||
|
||||
// @scenario binding_pipe_type_preservation
|
||||
// @effects same_expression_same_value_across_paths
|
||||
test('파이프 결과가 원본 타입으로 전달된다 (문자열 서식 강제 없음)', async ({ page }) => {
|
||||
const result = await page.evaluate(() => {
|
||||
const g7 = (window as any).G7Core;
|
||||
const Span = function Span() { return null; };
|
||||
const nodes = g7.renderItemChildren(
|
||||
[
|
||||
{ type: 'basic', name: 'Span', props: { title: '{{row.meta | keys}}' } },
|
||||
{ type: 'basic', name: 'Span', props: { title: '{{row.flags | first}}' } },
|
||||
{ type: 'basic', name: 'Span', props: { title: '{{row.tags | length}}' } },
|
||||
],
|
||||
{ row: { meta: { a: 1, b: 2 }, flags: [false, true], tags: ['a', 'b', 'c'] } },
|
||||
{ Span },
|
||||
'e2e-pipe-type',
|
||||
);
|
||||
return (nodes as any[]).map((n) => ({
|
||||
value: n?.props?.title ?? null,
|
||||
kind: Array.isArray(n?.props?.title) ? 'array' : typeof n?.props?.title,
|
||||
}));
|
||||
});
|
||||
|
||||
// 수정 전: 전부 문자열(`"[\"a\",\"b\"]"`, `"false"`, `"3"`)
|
||||
expect(result[0].kind).toBe('array');
|
||||
expect(result[0].value).toEqual(['a', 'b']);
|
||||
expect(result[1].kind).toBe('boolean');
|
||||
expect(result[1].value).toBe(false);
|
||||
expect(result[2].kind).toBe('number');
|
||||
expect(result[2].value).toBe(3);
|
||||
});
|
||||
|
||||
// @scenario binding_brace_in_pipe
|
||||
// @effects same_expression_same_value_across_paths
|
||||
test('파이프 식 안의 중괄호가 원본 문자열로 새지 않는다', async ({ page }) => {
|
||||
const value = await page.evaluate(() => {
|
||||
const g7 = (window as any).G7Core;
|
||||
const Span = function Span() { return null; };
|
||||
const nodes = g7.renderItemChildren(
|
||||
[{ type: 'basic', name: 'Span', text: '{{(row.meta ?? {}) | json}}' }],
|
||||
{ row: {} },
|
||||
{ Span },
|
||||
'e2e-brace',
|
||||
);
|
||||
return (nodes as any[])[0]?.props?.children ?? null;
|
||||
});
|
||||
|
||||
// 수정 전: 정규식이 `}` 를 포함한 식을 매칭하지 못해 `{{...}}` 가 그대로 노출됐다.
|
||||
expect(String(value)).toBe('{}');
|
||||
expect(String(value)).not.toContain('{{');
|
||||
});
|
||||
|
||||
// @scenario binding_empty_expression
|
||||
// @effects same_expression_same_value_across_paths
|
||||
test('빈 바인딩이 컨텍스트 객체 전체를 노출하지 않는다', async ({ page }) => {
|
||||
const value = await page.evaluate(() => {
|
||||
const g7 = (window as any).G7Core;
|
||||
const Span = function Span() { return null; };
|
||||
const nodes = g7.renderItemChildren(
|
||||
[{ type: 'basic', name: 'Span', props: { title: '{{}}' } }],
|
||||
{ row: { secret: 'do-not-leak' } },
|
||||
{ Span },
|
||||
'e2e-empty',
|
||||
);
|
||||
return (nodes as any[])[0]?.props?.title ?? null;
|
||||
});
|
||||
|
||||
expect(value == null || value === '').toBe(true);
|
||||
expect(String(value ?? '')).not.toContain('do-not-leak');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* E2E: `iteration` 과 `if` 를 함께 쓴 노드가 항목별로 조건을 평가하는지 (배포 번들 기준)
|
||||
*
|
||||
* 배경: 조건이 항목 변수(`{{comment.depth === 0}}` 등)를 참조하면, 부모 시점 컨텍스트에는
|
||||
* 그 변수가 없다. 조건을 iteration 보다 먼저 평가하던 종전 구조에서는 조건이 항상 거짓이
|
||||
* 되어 **목록 전체가 사라졌다** — 예외도 콘솔 경고도 남지 않으므로 API 응답만 봐서는
|
||||
* 드러나지 않는다. 같은 작성이 반복 렌더 경로(`cellChildren`)에서는 정상 동작했기 때문에
|
||||
* 재현 위치를 특정하기 어려운 결함이었다.
|
||||
*
|
||||
* 단위 테스트(`DynamicRenderer.iterationCondition.test.tsx`)가 소스를 잠그고
|
||||
* `SeoIterationConditionParityTest` 가 봇(PHP) 렌더러를 잠그지만, 실제 사용자가 보는 것은
|
||||
* 빌드된 번들이므로 브라우저에서 배포 번들의 `DynamicRenderer` 를 직접 렌더해 결과를 잠근다.
|
||||
*
|
||||
* 두 렌더러를 동시에 바꾼 수정이라 한쪽만 되돌아가면 브라우저에서는 목록이 보이는데
|
||||
* 봇 화면에서만 비는 비대칭이 생긴다.
|
||||
*
|
||||
* @scenario iteration_item_condition, iteration_item_index_variable
|
||||
* @effects iteration_renders_matching_items, iteration_injects_item_index
|
||||
*/
|
||||
import { test, expect } from '../fixtures/auth';
|
||||
|
||||
/**
|
||||
* 배포 번들에서 `DynamicRenderer` 를 임의 컨테이너에 렌더하고 결과 텍스트를 돌려줍니다.
|
||||
*
|
||||
* 엔진 인스턴스는 반드시 `G7Core.get*()` 접근자로 얻는다 — 클래스의 `getInstance()` 싱글톤은
|
||||
* 화면이 실제로 쓰는 인스턴스가 아니라서(템플릿 부팅 시 앱이 자기 레지스트리를 주입한다)
|
||||
* 컴포넌트가 하나도 등록돼 있지 않고 렌더 결과가 빈 문자열이 된다.
|
||||
*/
|
||||
const RENDER_IN_BUNDLE = `
|
||||
(componentDef, dataContext) => {
|
||||
const g7 = window.G7Core;
|
||||
const runtime = g7 && g7.__runtime;
|
||||
const React = window.React;
|
||||
const ReactDOM = window.ReactDOM;
|
||||
|
||||
const missing = [];
|
||||
if (!runtime) missing.push('G7Core.__runtime');
|
||||
if (!React) missing.push('window.React');
|
||||
if (!ReactDOM || !ReactDOM.createRoot) missing.push('window.ReactDOM.createRoot');
|
||||
if (runtime && !runtime.DynamicRenderer) missing.push('__runtime.DynamicRenderer');
|
||||
for (const name of ['getComponentRegistry', 'getDataBindingEngine', 'getTranslationEngine', 'getActionDispatcher']) {
|
||||
if (!g7 || typeof g7[name] !== 'function') missing.push('G7Core.' + name);
|
||||
}
|
||||
if (missing.length) return { missing };
|
||||
|
||||
const registry = g7.getComponentRegistry();
|
||||
if (!registry || !registry.hasComponent('Div')) {
|
||||
return { missing: ['registry:Div'], texts: [], html: '' };
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
const element = React.createElement(runtime.DynamicRenderer, {
|
||||
componentDef,
|
||||
dataContext,
|
||||
translationContext: { templateId: 'e2e', locale: 'ko' },
|
||||
registry,
|
||||
bindingEngine: g7.getDataBindingEngine(),
|
||||
translationEngine: g7.getTranslationEngine(),
|
||||
actionDispatcher: g7.getActionDispatcher(),
|
||||
});
|
||||
|
||||
const root = ReactDOM.createRoot(container);
|
||||
return new Promise((resolve) => {
|
||||
root.render(element);
|
||||
// 커밋이 끝난 뒤 DOM 을 읽는다 (두 프레임이면 초기 effect 까지 반영된다).
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
const texts = Array.from(container.querySelectorAll('.e2e-item')).map(
|
||||
(n) => (n.textContent || '').trim()
|
||||
);
|
||||
const html = container.innerHTML;
|
||||
root.unmount();
|
||||
container.remove();
|
||||
resolve({ missing: [], texts, html });
|
||||
}));
|
||||
});
|
||||
}
|
||||
`;
|
||||
|
||||
test.describe('iteration + if 항목별 조건 평가', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// 코어 번들이 부팅된 화면이면 충분하다(로그인 화면도 엔진을 부팅한다).
|
||||
await page.goto('/admin/login');
|
||||
// 컴포넌트 등록은 템플릿 부팅이 끝난 뒤에 일어난다. `__templateApp` 존재만 기다리면
|
||||
// 레지스트리가 비어 있는 시점에 렌더해 결과가 빈 문자열이 되고, 그러면 이 테스트가
|
||||
// "항목이 0개" 를 정상으로 오판한다.
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const g7 = (window as any).G7Core;
|
||||
return Boolean(
|
||||
g7?.__runtime?.DynamicRenderer &&
|
||||
typeof g7.getComponentRegistry === 'function' &&
|
||||
g7.getComponentRegistry()?.hasComponent?.('Div')
|
||||
);
|
||||
},
|
||||
null,
|
||||
{ timeout: 30_000 }
|
||||
);
|
||||
});
|
||||
|
||||
// @scenario iteration_item_condition
|
||||
// @effects iteration_renders_matching_items
|
||||
test('항목 변수를 참조하는 if 가 목록 전체를 지우지 않는다', async ({ page }) => {
|
||||
const result: any = await page.evaluate(
|
||||
([source, componentDef, dataContext]) =>
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)(source as string)(componentDef, dataContext),
|
||||
[
|
||||
RENDER_IN_BUNDLE,
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'Div',
|
||||
if: '{{user.is_active}}',
|
||||
iteration: { source: '{{users}}', item_var: 'user' },
|
||||
props: { className: 'e2e-item' },
|
||||
text: '{{user.name}}',
|
||||
},
|
||||
{
|
||||
users: [
|
||||
{ name: '가', is_active: true },
|
||||
{ name: '나', is_active: false },
|
||||
{ name: '다', is_active: true },
|
||||
],
|
||||
},
|
||||
] as any
|
||||
);
|
||||
|
||||
expect(result.missing, `배포 번들에 필요한 런타임 표면이 없습니다: ${result.missing?.join(', ')}`).toEqual([]);
|
||||
// 수정 전: 부모 시점에 `user` 가 없어 조건이 거짓 → 항목이 0개(목록 전체 소실)
|
||||
expect(result.texts, '항목 조건이 항목별로 평가되지 않았습니다').toEqual(['가', '다']);
|
||||
});
|
||||
|
||||
// @scenario iteration_item_condition
|
||||
// @effects iteration_renders_matching_items
|
||||
test('외곽 변수만 참조하는 if 는 종전대로 목록 전체를 차단한다', async ({ page }) => {
|
||||
const blocked: any = await page.evaluate(
|
||||
([source, componentDef, dataContext]) =>
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)(source as string)(componentDef, dataContext),
|
||||
[
|
||||
RENDER_IN_BUNDLE,
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'Div',
|
||||
if: '{{showList}}',
|
||||
iteration: { source: '{{items}}', item_var: 'item' },
|
||||
props: { className: 'e2e-item' },
|
||||
text: '{{item.name}}',
|
||||
},
|
||||
{ showList: false, items: [{ name: '가' }, { name: '나' }] },
|
||||
] as any
|
||||
);
|
||||
|
||||
expect(blocked.missing).toEqual([]);
|
||||
expect(blocked.texts, '외곽 조건이 거짓인데 항목이 렌더됐습니다').toEqual([]);
|
||||
|
||||
const shown: any = await page.evaluate(
|
||||
([source, componentDef, dataContext]) =>
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)(source as string)(componentDef, dataContext),
|
||||
[
|
||||
RENDER_IN_BUNDLE,
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'Div',
|
||||
if: '{{showList}}',
|
||||
iteration: { source: '{{items}}', item_var: 'item' },
|
||||
props: { className: 'e2e-item' },
|
||||
text: '{{item.name}}',
|
||||
},
|
||||
{ showList: true, items: [{ name: '가' }, { name: '나' }] },
|
||||
] as any
|
||||
);
|
||||
|
||||
expect(shown.texts).toEqual(['가', '나']);
|
||||
});
|
||||
|
||||
// @scenario iteration_item_index_variable
|
||||
// @effects iteration_injects_item_index
|
||||
test('{item_var}_index 자동 변수가 단발 렌더 경로에도 주입된다', async ({ page }) => {
|
||||
const result: any = await page.evaluate(
|
||||
([source, componentDef, dataContext]) =>
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)(source as string)(componentDef, dataContext),
|
||||
[
|
||||
RENDER_IN_BUNDLE,
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'Div',
|
||||
iteration: { source: '{{items}}', item_var: 'row' },
|
||||
props: { className: 'e2e-item' },
|
||||
text: '{{row_index}}',
|
||||
},
|
||||
{ items: [{ name: '가' }, { name: '나' }, { name: '다' }] },
|
||||
] as any
|
||||
);
|
||||
|
||||
expect(result.missing).toEqual([]);
|
||||
// 반복 렌더 경로(renderItemChildren)에는 원래 있던 변수다 — 경로 간 격차 해소
|
||||
expect(result.texts).toEqual(['0', '1', '2']);
|
||||
});
|
||||
|
||||
// @scenario iteration_item_condition
|
||||
// @effects iteration_renders_matching_items
|
||||
test('저장소 실사용 형태(게시판 댓글 접기 조건)가 항목별로 평가된다', async ({ page }) => {
|
||||
// 아래 조건식은 합성 예시가 아니라 저장소에 실제로 쓰인 작성 그대로다.
|
||||
// modules/_bundled/sirsoft-board/resources/layouts/admin/partials/
|
||||
// admin_board_post_detail/_comments.json
|
||||
// 항목 변수(`comment`)와 외곽 상태(`_local`/`$computed`)를 함께 참조하는 형태라,
|
||||
// 수정 전에는 부모 시점에 `comment` 가 없어 조건이 거짓 → 댓글 목록이 통째로 비었다.
|
||||
const condition =
|
||||
'{{(comment?.depth ?? 0) === 0 || (_local.collapsedReplies?.[$computed.commentRootMap?.[comment.id]] === false)}}';
|
||||
|
||||
const result: any = await page.evaluate(
|
||||
([source, componentDef, dataContext]) =>
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)(source as string)(componentDef, dataContext),
|
||||
[
|
||||
RENDER_IN_BUNDLE,
|
||||
{
|
||||
type: 'basic',
|
||||
name: 'Div',
|
||||
if: condition,
|
||||
iteration: { source: '{{comments}}', item_var: 'comment' },
|
||||
props: { className: 'e2e-item' },
|
||||
text: '{{comment.id}}',
|
||||
},
|
||||
{
|
||||
comments: [
|
||||
{ id: 'c1', depth: 0 },
|
||||
// 답글이지만 부모가 펼쳐진 상태 → 표시
|
||||
{ id: 'c2', depth: 1 },
|
||||
// 답글이고 부모가 접힌 상태 → 미표시
|
||||
{ id: 'c3', depth: 1 },
|
||||
],
|
||||
_local: { collapsedReplies: { c1: false, root2: true } },
|
||||
$computed: { commentRootMap: { c2: 'c1', c3: 'root2' } },
|
||||
},
|
||||
] as any
|
||||
);
|
||||
|
||||
expect(result.missing).toEqual([]);
|
||||
expect(result.texts, '실사용 조건이 항목별로 평가되지 않았습니다').toEqual(['c1', 'c2']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* E2E: 목록 필터의 따옴표 키 인덱싱 바인딩 (`{{query['xxx[]']}}`)
|
||||
*
|
||||
* 배경: 판정 통일(engine-v1.55.0) 이전에는 단일 바인딩 판정기가 6벌로 갈라져 있었고,
|
||||
* 그중 일부 방언이 `query['sales_status[]']` 처럼 **따옴표 대괄호 인덱싱**을 단일 바인딩으로
|
||||
* 인정하지 않았다. 그 결과 같은 작성이 어느 렌더 경로를 타느냐에 따라 값이 나오기도 하고
|
||||
* `undefined` 가 되기도 했다. 데이터소스 params 자리에서 `undefined` 가 되면 **URL 로 필터를
|
||||
* 걸고 들어와도 그 필터가 서버로 전달되지 않는다** — 예외도 경고도 없이 "필터가 안 걸린
|
||||
* 전체 목록" 이 보이므로 사용자가 원인을 특정할 수 없다.
|
||||
*
|
||||
* 이 spec 은 계획서 「브라우저 검증」의 "라우팅이 바뀌는 식의 소속 화면 직접 확인" 을
|
||||
* 재현 가능한 형태로 고정한다. 스냅샷(`BindingShape.routingParity.test.ts.snap`) 기준
|
||||
* 판정이 갈리는 식 63개 중 **동작이 실제로 바뀌는 것은 따옴표 대괄호 22건**이며(숫자 인덱싱
|
||||
* 39건은 동치성 테스트로 입증됨), 그 22건은 아래 13개 목록 필터 화면에 걸려 있다.
|
||||
* 여기서는 소유 확장이 서로 다른 4개 화면을 대표로 잠근다.
|
||||
*
|
||||
* 검증 방식은 화면 눈으로 보기가 아니라 **나가는 API 요청에 필터가 실렸는가** 다 —
|
||||
* 이 결함의 관측 가능한 결과가 바로 그것이기 때문이다.
|
||||
*
|
||||
* @scenario list_filter_quoted_key_reaches_api
|
||||
* @effects filter_param_sent_to_server
|
||||
*/
|
||||
import { test, expect, issueToken, authenticatePage } from '../fixtures/auth';
|
||||
|
||||
/**
|
||||
* 대표 화면 4종.
|
||||
*
|
||||
* `param` 은 URL 에 싣는 다중값 쿼리 키(`xxx[]`), `apiParam` 은 그 값이 도달해야 하는
|
||||
* 서버측 파라미터 이름이다. 레이아웃의 데이터소스 정의에서 `{{query['<param>']}}` 가
|
||||
* `<apiParam>` 으로 매핑된다.
|
||||
*/
|
||||
const SCREENS = [
|
||||
{
|
||||
name: '상품 목록 — 판매상태',
|
||||
path: '/admin/ecommerce/products',
|
||||
permissions: ['sirsoft-ecommerce.product.read'],
|
||||
param: 'sales_status[]',
|
||||
value: 'selling',
|
||||
apiParam: 'sales_status',
|
||||
apiPattern: /\/api\/modules\/sirsoft-ecommerce\/admin\/products/,
|
||||
},
|
||||
{
|
||||
name: '주문 목록 — 결제수단',
|
||||
path: '/admin/ecommerce/orders',
|
||||
permissions: ['sirsoft-ecommerce.order.read'],
|
||||
param: 'payment_method[]',
|
||||
value: 'card',
|
||||
apiParam: 'payment_method',
|
||||
apiPattern: /\/api\/modules\/sirsoft-ecommerce\/admin\/orders/,
|
||||
},
|
||||
{
|
||||
name: '신고 목록 — 처리상태',
|
||||
path: '/admin/boards/reports',
|
||||
permissions: ['sirsoft-board.notice.admin.manage'],
|
||||
param: 'status[]',
|
||||
value: 'pending',
|
||||
apiParam: 'status',
|
||||
apiPattern: /\/api\/modules\/sirsoft-board\/admin\/reports/,
|
||||
},
|
||||
{
|
||||
name: '본인인증 로그 — 상태',
|
||||
path: '/admin/identity/logs',
|
||||
permissions: ['core.identity.logs.read'],
|
||||
param: 'statuses[]',
|
||||
value: 'requested',
|
||||
apiParam: 'statuses',
|
||||
apiPattern: /\/api\/admin\/identity\/(logs|verifications)/,
|
||||
},
|
||||
] as const;
|
||||
|
||||
test.describe('목록 필터 — 따옴표 키 인덱싱 바인딩이 서버까지 도달한다', () => {
|
||||
for (const screen of SCREENS) {
|
||||
// @scenario list_filter_quoted_key_reaches_api
|
||||
// @effects filter_param_sent_to_server
|
||||
test(`${screen.name}: ?${screen.param} 가 API 요청에 실린다`, async ({ page }) => {
|
||||
// 관리자 목록 화면은 부트스트랩(레이아웃/번들/권한) 요청이 많아 여유를 둔다.
|
||||
// `networkidle` 은 쓰지 않는다 — 이 SPA 는 유휴 상태로 수렴하지 않아 항상 타임아웃난다.
|
||||
test.setTimeout(120_000);
|
||||
|
||||
const token = issueToken(...screen.permissions);
|
||||
await authenticatePage(page, token);
|
||||
|
||||
/** 목록 API 로 나간 요청의 전체 URL 들 */
|
||||
const listRequests: string[] = [];
|
||||
page.on('request', (req) => {
|
||||
if (screen.apiPattern.test(req.url())) listRequests.push(req.url());
|
||||
});
|
||||
|
||||
// 목록 API 가 나가는 것을 직접 기다린다. goto 전에 걸어야 초기 요청을 놓치지 않는다.
|
||||
const firstListRequest = page
|
||||
.waitForRequest((req) => screen.apiPattern.test(req.url()), { timeout: 60_000 })
|
||||
.catch(() => null);
|
||||
|
||||
const url = `${screen.path}?${encodeURIComponent(screen.param)}=${screen.value}`;
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||||
await firstListRequest;
|
||||
// 목록 요청이 여러 번(초기 + 필터 반영) 나가는 화면이 있어 잠시 더 수집한다.
|
||||
await page.waitForTimeout(3_000);
|
||||
|
||||
// ① 목록 API 가 실제로 호출됐는지 먼저 확정한다.
|
||||
// 이 단언 없이 아래 필터 검사만 두면, 요청이 0건일 때도 "위반 없음" 으로
|
||||
// 조용히 통과한다(공허한 통과).
|
||||
expect(
|
||||
listRequests.length,
|
||||
`목록 API(${screen.apiPattern}) 요청이 한 건도 관측되지 않았다 — ` +
|
||||
`화면이 로드되지 않았거나 권한이 부족하다`
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
// ② 그 요청 중 하나는 필터를 싣고 있어야 한다.
|
||||
const carrying = listRequests.filter((u) => {
|
||||
const qs = new URL(u).searchParams;
|
||||
// 서버는 배열을 `k[]=v` 또는 `k=v` 어느 쪽으로도 받을 수 있다.
|
||||
return (
|
||||
qs.getAll(`${screen.apiParam}[]`).includes(screen.value) ||
|
||||
qs.getAll(screen.apiParam).includes(screen.value)
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
carrying.length,
|
||||
`필터가 서버로 전달되지 않았다. 관측된 목록 요청:\n${listRequests.join('\n')}`
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
// ③ 원본 바인딩 문자열이 그대로 새어나가지 않았는지 — 해석 실패의 다른 증상.
|
||||
for (const u of listRequests) {
|
||||
expect(decodeURIComponent(u)).not.toContain('{{');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -161,7 +161,17 @@ test.describe('프로덕션 소스맵 노출 차단', () => {
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
// 이 테스트도 **프로덕션(비-local) 전제**다 — 위 확장 소스맵 테스트와 같은 이유.
|
||||
// `vite.config.core.js` 는 배포 빌드(`G7_BUILD_SOURCEMAP=0`)에서만 맵 생성을 끄고,
|
||||
// 로컬 `core:build` 는 디버깅을 위해 의도적으로 맵을 생성한다. 코어 맵은 `public/build/`
|
||||
// 아래에 있어 웹서버가 정적으로 서빙하므로, 로컬에서 한 번이라도 빌드하면 200 이 나온다.
|
||||
// 그건 결함이 아니라 환경 불일치이므로 형제 테스트와 동일하게 건너뛴다.
|
||||
test('@smoke 코어 소스맵은 웹루트에서 서빙되지 않는다', async ({ request }) => {
|
||||
test.skip(
|
||||
appEnv() === 'local',
|
||||
'APP_ENV=local 은 디버깅을 위해 코어 빌드가 소스맵을 생성한다 (프로덕션 전제 테스트)',
|
||||
);
|
||||
|
||||
const targets = [
|
||||
'/build/core/template-engine.min.js.map',
|
||||
'/build/core/layout-editor.min.js.map',
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* E2E: 반복 서브트리 진입 키의 `skipBindingKeys` 선언 정합 (engine-v1.56.1 / 템플릿)
|
||||
*
|
||||
* 배경: `cardColumns[].cellChildren` / `footerCells[].children` / `footerCardChildren` 은
|
||||
* **반복 렌더 경로**(`renderItemChildren`)가 항목 컨텍스트와 함께 해석하는 자리다. 따라서
|
||||
* 컴포넌트 선언(`components.json`)의 `skipBindingKeys` 에 그 키가 들어 있어야 props 선평가를
|
||||
* 건너뛴다. 선언이 빠지거나 이름이 틀리면(P5 이전 상태: `cardChildren` 오기, `footerCells`·
|
||||
* `footerCardChildren` 누락) 항목 변수가 없는 시점에 표현식이 먼저 평가되어
|
||||
* **셀이 비거나 `{{...}}` 원본이 화면에 남는다**. 예외는 발생하지 않는다.
|
||||
*
|
||||
* 계획서 「브라우저 검증」의 "skipBindingKeys 키 변동은 반복 서브트리 전부가 영향권 →
|
||||
* 대표 화면 시각 확인 필수" 항목을 재현 가능한 형태로 고정한다. 정적 검사(audit 룰
|
||||
* `component-skipbinding-keys-complete`)는 **선언과 구현의 일치**만 보므로, 실제 화면에서
|
||||
* 값이 채워지는지는 여기서만 잡힌다.
|
||||
*
|
||||
* 대상은 `cardColumns` 를 실제로 쓰면서 데이터가 항상 존재하는 화면을 골랐다
|
||||
* (모듈·플러그인은 번들 확장이 있어 목록이 비지 않는다).
|
||||
*
|
||||
* @scenario repeat_subtree_card_columns_render
|
||||
* @effects item_values_rendered_no_raw_binding
|
||||
*/
|
||||
import { test, expect, issueToken, authenticatePage } from '../fixtures/auth';
|
||||
|
||||
const SCREENS = [
|
||||
{
|
||||
name: '모듈 목록 (카드 뷰)',
|
||||
path: '/admin/modules',
|
||||
permissions: ['core.modules.read'],
|
||||
/** 번들 모듈이 반드시 하나는 노출된다 */
|
||||
expectText: '이커머스',
|
||||
},
|
||||
{
|
||||
name: '플러그인 목록 (카드 뷰)',
|
||||
path: '/admin/plugins',
|
||||
permissions: ['core.plugins.read'],
|
||||
expectText: 'CKEditor',
|
||||
},
|
||||
] as const;
|
||||
|
||||
test.describe('반복 서브트리 — cardColumns 항목이 실제 값으로 렌더된다', () => {
|
||||
for (const screen of SCREENS) {
|
||||
// @scenario repeat_subtree_card_columns_render
|
||||
// @effects item_values_rendered_no_raw_binding
|
||||
test(`${screen.name}: 카드 셀이 채워지고 원본 바인딩이 남지 않는다`, async ({ page }) => {
|
||||
test.setTimeout(120_000);
|
||||
|
||||
const token = issueToken(...screen.permissions);
|
||||
await authenticatePage(page, token);
|
||||
|
||||
await page.goto(screen.path, { waitUntil: 'domcontentloaded', timeout: 60_000 });
|
||||
|
||||
// ① 목록이 실제로 그려질 때까지 기다린다. 이 대기 없이 아래 부재 단언만 두면
|
||||
// 화면이 비어 있어도 "원본 바인딩 없음" 으로 조용히 통과한다(공허한 통과).
|
||||
await expect(page.getByText(screen.expectText).first()).toBeVisible({ timeout: 60_000 });
|
||||
|
||||
// ② 반복 서브트리(cardColumns[].cellChildren) 안의 표현식이 해석되지 않은 채
|
||||
// 남으면 화면에 `{{` 가 그대로 보인다.
|
||||
const bodyText = (await page.locator('body').innerText()) ?? '';
|
||||
expect(bodyText).not.toContain('{{');
|
||||
|
||||
// ③ 번역 면제 마커(Unicode Noncharacter)도 화면으로 새지 않아야 한다
|
||||
// — 반복 경로의 raw 마커 unwrap 회귀선(engine-v1.56.3).
|
||||
expect(bodyText).not.toContain('');
|
||||
expect(bodyText).not.toContain('');
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Seo;
|
||||
|
||||
use App\Seo\ComponentHtmlMapper;
|
||||
use App\Seo\ExpressionEvaluator;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* 반복 렌더 노드의 조건 평가 순서와 자동 변수 주입이 React 렌더러와 같은지 감시합니다.
|
||||
*
|
||||
* 배경:
|
||||
* 같은 레이아웃 JSON 을 React `DynamicRenderer` 와 SEO PHP 렌더러가 각각 그린다.
|
||||
* `iteration` 과 `if` 를 함께 쓴 노드에서, 조건이 항목 변수(`{{user.is_active}}` 등)를
|
||||
* 참조하면 부모 시점 컨텍스트에는 그 변수가 없다. 조건을 iteration 보다 먼저 평가하면
|
||||
* 항상 거짓이 되어 **목록 전체가 사라진다** — 예외도 경고도 남지 않는다.
|
||||
*
|
||||
* React 는 engine-v1.56.0 에서 "iteration 이 있으면 부모 시점 조건으로 끊지 않는다" 로
|
||||
* 고쳤다. 한쪽만 고치면 브라우저에서는 목록이 보이는데 봇 화면에서만 비므로, 이 테스트가
|
||||
* PHP 쪽 순서를 고정한다.
|
||||
*
|
||||
* `{item_var}_index` 자동 변수도 같은 이유로 양쪽에 있어야 한다.
|
||||
*
|
||||
* @see \App\Seo\ComponentHtmlMapper::renderComponent()
|
||||
* @see \App\Seo\ComponentHtmlMapper::renderIteration()
|
||||
*/
|
||||
class SeoIterationConditionParityTest extends TestCase
|
||||
{
|
||||
private ComponentHtmlMapper $mapper;
|
||||
|
||||
private ExpressionEvaluator $evaluator;
|
||||
|
||||
/**
|
||||
* 테스트 초기화 - 최소 컴포넌트 매핑으로 매퍼를 준비합니다.
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->mapper = new ComponentHtmlMapper;
|
||||
$this->evaluator = new ExpressionEvaluator;
|
||||
|
||||
$this->mapper->setComponentMap([
|
||||
'Div' => ['tag' => 'div'],
|
||||
'Li' => ['tag' => 'li'],
|
||||
'Span' => ['tag' => 'span'],
|
||||
]);
|
||||
$this->mapper->setTextProps(['text']);
|
||||
$this->mapper->setAllowedAttrs(['class', 'id']);
|
||||
$this->mapper->setAttrMap(['className' => 'class']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 컴포넌트 배열을 렌더링합니다.
|
||||
*
|
||||
* @param array $components 컴포넌트 정의 배열
|
||||
* @param array $context 데이터 컨텍스트
|
||||
* @return string 렌더링된 HTML
|
||||
*/
|
||||
private function render(array $components, array $context = []): string
|
||||
{
|
||||
return $this->mapper->render($components, $context, $this->evaluator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 항목 변수를 참조하는 if 가 iteration 과 함께 있어도 목록이 통째로 사라지지 않습니다.
|
||||
*/
|
||||
public function test_iteration_with_item_referencing_if_renders_matching_items(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Li',
|
||||
'if' => '{{user.is_active}}',
|
||||
'iteration' => [
|
||||
'data' => '{{users}}',
|
||||
'item_var' => 'user',
|
||||
],
|
||||
'props' => ['text' => '{{user.name}}'],
|
||||
],
|
||||
];
|
||||
|
||||
$context = [
|
||||
'users' => [
|
||||
['name' => '가', 'is_active' => true],
|
||||
['name' => '나', 'is_active' => false],
|
||||
['name' => '다', 'is_active' => true],
|
||||
],
|
||||
];
|
||||
|
||||
$html = $this->render($components, $context);
|
||||
|
||||
// 수정 전: 부모 시점에 `user` 가 없어 조건이 거짓 → 빈 문자열(목록 전체 소실)
|
||||
$this->assertStringContainsString('<li>가</li>', $html);
|
||||
$this->assertStringContainsString('<li>다</li>', $html);
|
||||
$this->assertStringNotContainsString('<li>나</li>', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* condition 키로 작성해도 같은 순서가 적용됩니다.
|
||||
*/
|
||||
public function test_iteration_with_item_referencing_condition_key(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Li',
|
||||
'condition' => '{{row.visible}}',
|
||||
'iteration' => [
|
||||
'source' => '{{rows}}',
|
||||
'item_var' => 'row',
|
||||
],
|
||||
'props' => ['text' => '{{row.label}}'],
|
||||
],
|
||||
];
|
||||
|
||||
$context = [
|
||||
'rows' => [
|
||||
['label' => 'A', 'visible' => true],
|
||||
['label' => 'B', 'visible' => false],
|
||||
],
|
||||
];
|
||||
|
||||
$html = $this->render($components, $context);
|
||||
|
||||
$this->assertStringContainsString('<li>A</li>', $html);
|
||||
$this->assertStringNotContainsString('<li>B</li>', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* 외곽 변수만 참조하는 if 는 종전과 같은 결과를 냅니다 (거짓이면 목록 미렌더).
|
||||
*/
|
||||
public function test_iteration_with_outer_scope_if_false_renders_nothing(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Li',
|
||||
'if' => '{{showList}}',
|
||||
'iteration' => [
|
||||
'data' => '{{items}}',
|
||||
'item_var' => 'item',
|
||||
],
|
||||
'props' => ['text' => '{{item.name}}'],
|
||||
],
|
||||
];
|
||||
|
||||
$context = [
|
||||
'showList' => false,
|
||||
'items' => [['name' => '가'], ['name' => '나']],
|
||||
];
|
||||
|
||||
$this->assertSame('', $this->render($components, $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 외곽 변수만 참조하는 if 가 참이면 전 항목이 렌더됩니다.
|
||||
*/
|
||||
public function test_iteration_with_outer_scope_if_true_renders_all_items(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Li',
|
||||
'if' => '{{showList}}',
|
||||
'iteration' => [
|
||||
'data' => '{{items}}',
|
||||
'item_var' => 'item',
|
||||
],
|
||||
'props' => ['text' => '{{item.name}}'],
|
||||
],
|
||||
];
|
||||
|
||||
$context = [
|
||||
'showList' => true,
|
||||
'items' => [['name' => '가'], ['name' => '나']],
|
||||
];
|
||||
|
||||
$html = $this->render($components, $context);
|
||||
|
||||
$this->assertStringContainsString('<li>가</li>', $html);
|
||||
$this->assertStringContainsString('<li>나</li>', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* iteration 이 없는 노드의 if 는 종전처럼 즉시 차단합니다 (회귀 방지).
|
||||
*/
|
||||
public function test_if_without_iteration_still_blocks_immediately(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Div',
|
||||
'if' => '{{visible}}',
|
||||
'props' => ['text' => '숨김 대상'],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertSame('', $this->render($components, ['visible' => false]));
|
||||
}
|
||||
|
||||
/**
|
||||
* `{item_var}_index` 자동 변수가 항목 컨텍스트에 주입됩니다.
|
||||
*/
|
||||
public function test_iteration_injects_item_var_index_automatic_variable(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Li',
|
||||
'iteration' => [
|
||||
'data' => '{{items}}',
|
||||
'item_var' => 'row',
|
||||
],
|
||||
'props' => ['text' => '{{row_index}}'],
|
||||
],
|
||||
];
|
||||
|
||||
$context = ['items' => [['name' => '가'], ['name' => '나'], ['name' => '다']]];
|
||||
|
||||
$html = $this->render($components, $context);
|
||||
|
||||
$this->assertStringContainsString('<li>0</li>', $html);
|
||||
$this->assertStringContainsString('<li>1</li>', $html);
|
||||
$this->assertStringContainsString('<li>2</li>', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* `{item_var}_index` 주입이 명시적 index_var 를 덮어쓰지 않습니다.
|
||||
*/
|
||||
public function test_item_var_index_coexists_with_explicit_index_var(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Li',
|
||||
'iteration' => [
|
||||
'data' => '{{items}}',
|
||||
'item_var' => 'row',
|
||||
'index_var' => 'i',
|
||||
],
|
||||
'children' => [
|
||||
['name' => 'Span', 'props' => ['text' => '{{i}}']],
|
||||
['name' => 'Span', 'props' => ['text' => '{{row_index}}']],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$context = ['items' => [['name' => '가'], ['name' => '나']]];
|
||||
|
||||
$html = $this->render($components, $context);
|
||||
|
||||
$this->assertStringContainsString('<li><span>0</span><span>0</span></li>', $html);
|
||||
$this->assertStringContainsString('<li><span>1</span><span>1</span></li>', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* 항목 조건이 자동 변수(`{item_var}_index`)를 참조해도 항목별로 평가됩니다.
|
||||
*/
|
||||
public function test_item_condition_can_reference_injected_index_variable(): void
|
||||
{
|
||||
$components = [
|
||||
[
|
||||
'name' => 'Li',
|
||||
'if' => '{{row_index < 2}}',
|
||||
'iteration' => [
|
||||
'data' => '{{items}}',
|
||||
'item_var' => 'row',
|
||||
],
|
||||
'props' => ['text' => '{{row.name}}'],
|
||||
],
|
||||
];
|
||||
|
||||
$context = ['items' => [['name' => '가'], ['name' => '나'], ['name' => '다']]];
|
||||
|
||||
$html = $this->render($components, $context);
|
||||
|
||||
$this->assertStringContainsString('<li>가</li>', $html);
|
||||
$this->assertStringContainsString('<li>나</li>', $html);
|
||||
$this->assertStringNotContainsString('<li>다</li>', $html);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user