3사 PG 플러그인의 체크아웃 DOM 인젝터가 렌더 후 브랜드 마크를 주입하고 injector끼리 같은 버튼을 두고 경합하던 구조를 제거한다. 시각정보(색 배지/ SVG 로고)를 결제수단 카탈로그(_cached_brand_mark)로 편입해 코어 체크아웃 레이아웃의 신규 BrandMark 컴포넌트가 데이터 기반으로 직접 렌더한다. reconcileKginicisPatchedDuplicates(중복 방어)도 함께 제거했다. 애플페이 iOS 게이팅도 injector 밖으로 이관: 이커머스 모듈 DetectDevice 미들웨어가 UA로 기기를 판정해 코어 core.frontend.filter_app_config 훅으로 appConfig.isIos를 주입하고(1차), 템플릿 부트스트랩이 iPadOS 데스크탑 UA를 클라 신호로 보정한다(2차). 코어는 훅만 제공하고 감지 로직은 모듈이 소유한다. 미들웨어는 Router::pushMiddlewareToGroup 이 아닌 HTTP Kernel::appendMiddlewareToGroup 으로 web 그룹에 등록한다(전자는 Router 레지스트리만 갱신해 SSR 셸 요청 파이프라인에 미반영 → isIos 항상 false). sirsoft-gdpr 선례와 동일. 코어 최소 요구 버전 7.0.5 상향(appConfig 필터 훅 도입).
191 lines
5.6 KiB
TypeScript
191 lines
5.6 KiB
TypeScript
// e2e:allow편집기 위젯(icon-picker) 등록을 module-load 시점으로 이동(결함#3, admin 동기 — 직접 하드로드 시 위젯 누락 해소). 단위 회귀는 registerEditorWidgets.test.ts(module-load 등록 + index.ts 최상위 호출 정적 가드).
|
|
/**
|
|
* Sirsoft Basic User Template
|
|
*
|
|
* 그누보드7 템플릿 엔진용 사용자 템플릿 컴포넌트 패키지
|
|
* Nexibase 스타일 기반
|
|
*/
|
|
|
|
// Logger 설정 (G7Core 초기화 전에도 동작하도록 폴백 포함)
|
|
const logger = ((window as any).G7Core?.createLogger?.('Template:sirsoft-basic')) ?? {
|
|
log: (...args: unknown[]) => console.log('[Template:sirsoft-basic]', ...args),
|
|
warn: (...args: unknown[]) => console.warn('[Template:sirsoft-basic]', ...args),
|
|
error: (...args: unknown[]) => console.error('[Template:sirsoft-basic]', ...args),
|
|
};
|
|
|
|
// Styles
|
|
import './styles/main.css';
|
|
|
|
// Basic Components (Header, Footer는 composite에서 사용하므로 여기서는 별도 이름으로 export)
|
|
export {
|
|
Button,
|
|
type ButtonProps,
|
|
FileInput,
|
|
type FileInputProps,
|
|
Input,
|
|
type InputProps,
|
|
PasswordInput,
|
|
type PasswordInputProps,
|
|
type PasswordRule,
|
|
defaultPasswordRules,
|
|
availablePasswordRules,
|
|
Textarea,
|
|
type TextareaProps,
|
|
Label,
|
|
type LabelProps,
|
|
Div,
|
|
type DivProps,
|
|
Span,
|
|
type SpanProps,
|
|
P,
|
|
type PProps,
|
|
Img,
|
|
type ImgProps,
|
|
H1,
|
|
type H1Props,
|
|
H2,
|
|
type H2Props,
|
|
H3,
|
|
type H3Props,
|
|
H4,
|
|
type H4Props,
|
|
Ul,
|
|
type UlProps,
|
|
Ol,
|
|
type OlProps,
|
|
Li,
|
|
type LiProps,
|
|
A,
|
|
type AProps,
|
|
Form,
|
|
type FormProps,
|
|
Select,
|
|
type SelectProps,
|
|
Option,
|
|
type OptionProps,
|
|
Optgroup,
|
|
type OptgroupProps,
|
|
Checkbox,
|
|
type CheckboxProps,
|
|
Table,
|
|
type TableProps,
|
|
Thead,
|
|
type TheadProps,
|
|
Tbody,
|
|
type TbodyProps,
|
|
Tr,
|
|
type TrProps,
|
|
Th,
|
|
type ThProps,
|
|
Td,
|
|
type TdProps,
|
|
Nav,
|
|
type NavProps,
|
|
Section,
|
|
type SectionProps,
|
|
Svg,
|
|
type SvgProps,
|
|
Icon,
|
|
type IconProps,
|
|
Code,
|
|
type CodeProps,
|
|
Hr,
|
|
type HrProps,
|
|
IconName,
|
|
type IconStyle,
|
|
type IconSize,
|
|
} from './components/basic';
|
|
|
|
// Composite Components
|
|
export * from './components/composite';
|
|
|
|
// Layout Components
|
|
export * from './components/layout';
|
|
|
|
// Template Metadata
|
|
import templateMetadata from '../template.json';
|
|
|
|
// Handlers
|
|
import { handlerMap } from './handlers';
|
|
|
|
// IDV Modal Launcher (engine-v1.46.0+)
|
|
import { registerSirsoftBasicIdentityLauncher } from './handlers/identityLauncher';
|
|
|
|
// 서버 기기 감지값(appConfig.isIos) 클라이언트 보정 (iPadOS 데스크탑 UA 케이스)
|
|
import { correctIosDeviceState } from './handlers/deviceCorrection';
|
|
|
|
// 레이아웃 편집기 커스텀 위젯(icon-picker 등) 등록
|
|
import { registerSirsoftBasicEditorWidgets } from './layout-editor/registerEditorWidgets';
|
|
|
|
// handlerMap을 전역으로 노출 (로케일 변경 시 재등록용)
|
|
if (typeof window !== 'undefined') {
|
|
(window as any).G7TemplateHandlers = handlerMap;
|
|
}
|
|
|
|
/**
|
|
* 템플릿 메타데이터 export
|
|
*
|
|
* template.json 파일의 내용을 번들에 포함시켜 API 호출 없이
|
|
* 코어 엔진에서 직접 접근 가능하도록 합니다.
|
|
*/
|
|
export { templateMetadata };
|
|
|
|
/**
|
|
* 템플릿 초기화 함수
|
|
*
|
|
* 코어 엔진에 커스텀 핸들러를 등록합니다.
|
|
*/
|
|
export function initTemplate(): void {
|
|
// ActionDispatcher가 로드될 때까지 대기 후 핸들러 등록
|
|
if (typeof window !== 'undefined') {
|
|
let retryCount = 0;
|
|
const maxRetries = 50; // 최대 5초 대기 (50 * 100ms)
|
|
|
|
const registerHandlers = () => {
|
|
const actionDispatcher = (window as any).G7Core?.getActionDispatcher?.();
|
|
|
|
if (actionDispatcher) {
|
|
// handlerMap의 모든 핸들러를 자동으로 등록
|
|
Object.entries(handlerMap).forEach(([name, handler]) => {
|
|
actionDispatcher.registerHandler(name, handler);
|
|
});
|
|
|
|
logger.log(`${Object.keys(handlerMap).length} custom handler(s) registered:`, Object.keys(handlerMap));
|
|
|
|
// IDV Modal Launcher 등록 (window.G7Core.identity.setLauncher 사용)
|
|
registerSirsoftBasicIdentityLauncher();
|
|
|
|
// 서버 UA 판정이 놓친 iPadOS(데스크탑 UA) 를 클라 신호로 보정 → appConfig.isIos.
|
|
// 애플페이 iOS 게이팅(체크아웃 레이아웃 iteration 필터)이 이 값을 참조한다.
|
|
correctIosDeviceState();
|
|
} else {
|
|
retryCount++;
|
|
if (retryCount <= maxRetries) {
|
|
logger.warn(`ActionDispatcher not found, retrying... (${retryCount}/${maxRetries})`);
|
|
setTimeout(registerHandlers, 100);
|
|
} else {
|
|
logger.error('Failed to register handlers: ActionDispatcher not available after maximum retries');
|
|
}
|
|
}
|
|
};
|
|
|
|
// window.load 이벤트 사용 (모든 리소스 로드 완료 후)
|
|
if (document.readyState === 'complete') {
|
|
registerHandlers();
|
|
} else {
|
|
window.addEventListener('load', registerHandlers);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 레이아웃 편집기 커스텀 위젯(icon-picker) 등록 — 모듈 로드 시점에 즉시 실행한다.
|
|
// ActionDispatcher 가용을 기다리는 `registerHandlers`(window.load
|
|
// 게이트) 안에서 등록하면, 편집기 URL 을 직접 하드로드한 경로에서 등록이 편집기 셸 마운트보다
|
|
// 늦어 icon-picker 위젯이 누락된다("Unsupported control"). `G7Core.layoutEditor` 예약 접수함
|
|
// (ready 큐 stub)은 편집기 로드 전 등록도 큐로 보존했다가 flush 하므로, 핸들러 등록 타이밍과
|
|
// 무관하게 모듈 로드 시 즉시 등록하는 것이 진입 경로(SPA 전환 / 직접 로드)와 무관하게 결정적이다.
|
|
registerSirsoftBasicEditorWidgets();
|
|
|
|
// 템플릿 초기화 자동 실행
|
|
initTemplate();
|