Files
Gnuboard7/templates/_bundled/sirsoft-basic/src/index.ts
T
HeuJung 8b9bfd0c91 refactor(ecommerce,pay): 간편결제 브랜드 마크 카탈로그 편입 + 애플페이 iOS 게이팅 이관
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 필터 훅 도입).
2026-07-15 17:50:39 +09:00

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();