sales-frontend-solution 0.0.46 → 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import React2, { createContext, useState, useEffect, useContext, useRef, useCallback } from 'react';
1
+ import React2, { createContext, useState, useEffect, useContext, useCallback, useRef, useMemo } from 'react';
2
2
  import { isClient, getEnvironmentFromHostname, getServicePath, getNlcHostFromEnvironment } from 'sales-frontend-utils';
3
3
  import { jsx, Fragment } from 'react/jsx-runtime';
4
4
 
5
- // src/v3-provider.tsx
5
+ // src/features/v3/v3-provider.tsx
6
6
 
7
- // src/utils/load-script.ts
7
+ // src/shared/utils/load-script.ts
8
8
  async function loadScript(url) {
9
9
  await new Promise((resolve, reject) => {
10
10
  const script = document.createElement("script");
@@ -45,7 +45,7 @@ var getServiceUrl = (serviceName) => {
45
45
  INPUT_PREFILL_USER: `${getServiceUrl("nlc")}/user-ctfn/ncsr-info-inpt/post`
46
46
  });
47
47
 
48
- // src/v3.ts
48
+ // src/features/v3/v3.ts
49
49
  var isAndroid = () => {
50
50
  return /android/i.test(navigator.userAgent) && !/edge/i.test(navigator.userAgent);
51
51
  };
@@ -96,60 +96,102 @@ var useV3 = () => {
96
96
  }
97
97
  return context;
98
98
  };
99
- var getPath = (service) => {
99
+
100
+ // src/features/keypad/constants.ts
101
+ var KEYPAD_DEFAULTS = {
102
+ /** 키패드 너비 (%) */
103
+ WIDTH: 100,
104
+ /** 키패드 상단 위치 */
105
+ POSITION_TOP: 10,
106
+ /** 뷰 타입 */
107
+ VIEW_TYPE: "half",
108
+ /** 닫기 딜레이 */
109
+ CLOSE_DELAY: 0,
110
+ /** 숫자 키 행 수 */
111
+ NUMBER_KEY_ROW_COUNT: 3
112
+ };
113
+ var KEYPAD_SELECTORS = {
114
+ ALERT: "#xkalert"
115
+ };
116
+ var INPUT_SEPARATOR = ",";
117
+ var MASK_CHAR = "*";
118
+
119
+ // src/features/keypad/libs/constants.ts
120
+ var KEYPAD_SERVICE_PATH = {
121
+ XKP: "xkp",
122
+ NLC: "nlc"
123
+ };
124
+ var KEYPAD_SCRIPTS = {
125
+ MOBILE: "xkeypad_mobile.js"
126
+ };
127
+ var KEYPAD_STYLES = {
128
+ MOBILE: "xkp_mobile.css"
129
+ };
130
+ var DEFAULT_KEYPAD_CONFIG = {
131
+ VERSION: "1.0.5.1",
132
+ MAX_INPUT_SIZE: 56,
133
+ TEXT_INPUT_VIEW: 0,
134
+ TOUCH_OPTION: 0,
135
+ INPUT_BACKGROUND_COLOR: "#E4E4E4",
136
+ INPUT_BORDER_STYLE: "1px solid #9E9E9E",
137
+ INVALID_SESSION_MESSAGE: "\uBCF4\uC548\uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n'\uD655\uC778'\uC744 \uB204\uB974\uBA74 \uD0A4\uD328\uB4DC\uAC00 \uAC31\uC2E0 \uB429\uB2C8\uB2E4.",
138
+ INVALID_SESSION_AUTO_REFRESH: true,
139
+ ENABLE_ACCESSIBILITY: true,
140
+ USE_CUSTOM_ALERT: false,
141
+ FUNCTION_KEY_BUTTON_STYLE: "text"
142
+ };
143
+ var getServicePath2 = (service, env) => {
100
144
  if (!isClient()) {
101
145
  return;
102
146
  }
103
- const env = getEnvironmentFromHostname(location.hostname);
104
147
  const convertedEnv = env === "local" ? "stg" : env;
105
148
  return `https://nxl-${convertedEnv !== "prd" ? `${service}-${convertedEnv}` : service}.hanwhalife.com`;
106
149
  };
107
- var XKeyboardMobileInstance = (() => {
108
- if (!isClient()) {
109
- return null;
110
- }
111
- const keypadServicePath = `${getPath("xkp")}/xkp/xkscriptservice`;
112
- const keypadContentsPath = `${getPath("nlc")}/cnts-files/xkeyboard`;
113
- window.XKConfigMobile = {
114
- version: "1.0.5.1",
150
+ var getKeypadServicePath = (env) => {
151
+ return `${getServicePath2(KEYPAD_SERVICE_PATH.XKP, env)}/xkp/xkscriptservice`;
152
+ };
153
+ var getKeypadContentsPath = (env, subPath = "") => {
154
+ const basePath = `${getServicePath2(KEYPAD_SERVICE_PATH.NLC, env)}/cnts-files/xkeyboard`;
155
+ return `${basePath}${subPath}`;
156
+ };
157
+
158
+ // src/features/keypad/libs/config.ts
159
+ var createKeypadConfig = () => {
160
+ const env = getEnvironmentFromHostname(isClient() ? location.hostname : "");
161
+ const keypadServicePath = getKeypadServicePath(env);
162
+ return {
163
+ version: DEFAULT_KEYPAD_CONFIG.VERSION,
115
164
  server: keypadServicePath,
116
- contextRoot: `${keypadContentsPath}/js`,
117
- cssPath: `${keypadContentsPath}/css/xkp_mobile.css`,
118
- logoImgPath: `${keypadContentsPath}/img/logo.png`,
119
- inputObjectBackgroundColor: "#E4E4E4",
120
- inputObjectBorderStyle: "1px solid #9E9E9E",
121
- invalidSessionErrorMessage: "\uBCF4\uC548\uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n'\uD655\uC778'\uC744 \uB204\uB974\uBA74 \uD0A4\uD328\uB4DC\uAC00 \uAC31\uC2E0 \uB429\uB2C8\uB2E4.",
122
- invalidSessionAutoRefresh: true,
123
- enableAccessibility: true,
124
- useCustomAlert: false,
125
- functionKeyButtonStyle: "text",
126
- maxInputSize: 56,
127
- textInputView: 0,
128
- touchOption: 0
165
+ contextRoot: getKeypadContentsPath(env, "/js"),
166
+ cssPath: getKeypadContentsPath(env, `/css/${KEYPAD_STYLES.MOBILE}`),
167
+ logoImgPath: getKeypadContentsPath(env, "/img/logo.png"),
168
+ inputObjectBackgroundColor: DEFAULT_KEYPAD_CONFIG.INPUT_BACKGROUND_COLOR,
169
+ inputObjectBorderStyle: DEFAULT_KEYPAD_CONFIG.INPUT_BORDER_STYLE,
170
+ invalidSessionErrorMessage: DEFAULT_KEYPAD_CONFIG.INVALID_SESSION_MESSAGE,
171
+ invalidSessionAutoRefresh: DEFAULT_KEYPAD_CONFIG.INVALID_SESSION_AUTO_REFRESH,
172
+ enableAccessibility: DEFAULT_KEYPAD_CONFIG.ENABLE_ACCESSIBILITY,
173
+ useCustomAlert: DEFAULT_KEYPAD_CONFIG.USE_CUSTOM_ALERT,
174
+ functionKeyButtonStyle: DEFAULT_KEYPAD_CONFIG.FUNCTION_KEY_BUTTON_STYLE,
175
+ maxInputSize: DEFAULT_KEYPAD_CONFIG.MAX_INPUT_SIZE,
176
+ textInputView: DEFAULT_KEYPAD_CONFIG.TEXT_INPUT_VIEW,
177
+ touchOption: DEFAULT_KEYPAD_CONFIG.TOUCH_OPTION
129
178
  };
130
- document.oncontextmenu = () => false;
131
- document.ondragstart = () => false;
132
- document.onselectstart = () => false;
133
- const headTag = document.head;
134
- const scriptsToLoad = ["xkeypad_mobile.js"];
135
- const urls = scriptsToLoad.map(
136
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
- (x) => `${window.XKConfigMobile.contextRoot}/${x}`
138
- );
139
- Promise.all(urls.map((x) => loadScript(x))).then(() => {
140
- const existingLink = document.getElementById("xkStyle");
141
- if (existingLink?.parentNode) {
142
- existingLink.parentNode.removeChild(existingLink);
143
- }
144
- const linkElement = document.createElement("link");
145
- linkElement.id = "xkStyle";
146
- linkElement.rel = "stylesheet";
147
- linkElement.type = "text/css";
148
- linkElement.href = window.XKConfigMobile.cssPath;
149
- headTag.appendChild(linkElement);
150
- });
179
+ };
180
+ var initializeGlobalConfig = () => {
181
+ if (isClient() === false) {
182
+ return null;
183
+ }
184
+ const config = createKeypadConfig();
185
+ window.XKConfigMobile = config;
186
+ return config;
187
+ };
188
+
189
+ // src/features/keypad/libs/keypad-manager.ts
190
+ var createKeypadManager = () => {
151
191
  const keypads = {};
152
- const getKeypad = (name) => keypads[name];
192
+ const getKeypad = (name) => {
193
+ return keypads[name];
194
+ };
153
195
  const setKeypad = (name, keypad) => {
154
196
  keypads[name] = keypad;
155
197
  return keypad;
@@ -166,7 +208,12 @@ var XKeyboardMobileInstance = (() => {
166
208
  });
167
209
  };
168
210
  const newKeypad = (name) => {
169
- return setKeypad(name, new window.XKModule());
211
+ const { XKModule } = window;
212
+ if (!XKModule) {
213
+ console.error("[XKeyboard] XKModule\uC774 \uB85C\uB4DC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
214
+ return void 0;
215
+ }
216
+ return setKeypad(name, new XKModule());
170
217
  };
171
218
  return {
172
219
  keypads,
@@ -176,9 +223,166 @@ var XKeyboardMobileInstance = (() => {
176
223
  removeKeypad,
177
224
  closeAll
178
225
  };
226
+ };
227
+
228
+ // src/features/keypad/libs/script-loader.ts
229
+ var isScriptsLoaded = false;
230
+ var loadingPromise = null;
231
+ var disableDocumentEvents = () => {
232
+ if (typeof document === "undefined") {
233
+ return;
234
+ }
235
+ document.oncontextmenu = () => false;
236
+ document.ondragstart = () => false;
237
+ document.onselectstart = () => false;
238
+ };
239
+ var loadKeypadStyles = (cssPath) => {
240
+ if (typeof document === "undefined") {
241
+ return;
242
+ }
243
+ const headTag = document.head;
244
+ const existingLink = document.getElementById("xkStyle");
245
+ if (existingLink?.parentNode) {
246
+ existingLink.parentNode.removeChild(existingLink);
247
+ }
248
+ const linkElement = document.createElement("link");
249
+ linkElement.id = "xkStyle";
250
+ linkElement.rel = "stylesheet";
251
+ linkElement.type = "text/css";
252
+ linkElement.href = cssPath;
253
+ headTag.appendChild(linkElement);
254
+ };
255
+ var loadKeypadScripts = async (config) => {
256
+ if (isScriptsLoaded) {
257
+ return;
258
+ }
259
+ if (loadingPromise) {
260
+ return loadingPromise;
261
+ }
262
+ loadingPromise = (async () => {
263
+ try {
264
+ disableDocumentEvents();
265
+ const scriptUrl = `${config.contextRoot}/${KEYPAD_SCRIPTS.MOBILE}`;
266
+ console.log("scriptUrl::", scriptUrl);
267
+ await loadScript(scriptUrl);
268
+ loadKeypadStyles(config.cssPath);
269
+ isScriptsLoaded = true;
270
+ } catch (error) {
271
+ loadingPromise = null;
272
+ throw error;
273
+ }
274
+ })();
275
+ return loadingPromise;
276
+ };
277
+
278
+ // src/features/keypad/libs/index.ts
279
+ var XKeyboardMobileInstance = (() => {
280
+ if (isClient() === false) {
281
+ return null;
282
+ }
283
+ const config = initializeGlobalConfig();
284
+ if (!config) {
285
+ console.error("[XKeyboard] \uC124\uC815 \uCD08\uAE30\uD654\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
286
+ return null;
287
+ }
288
+ loadKeypadScripts(config).catch((error) => {
289
+ console.error("[XKeyboard] \uC2A4\uD06C\uB9BD\uD2B8 \uB85C\uB4DC \uC2E4\uD328:", error);
290
+ });
291
+ return createKeypadManager();
179
292
  })();
180
293
 
181
- // src/types/keypad-message.enum.ts
294
+ // src/features/keypad/libs/keypad-param.ts
295
+ var createKeypadParam = (options) => {
296
+ const {
297
+ name,
298
+ inputRef,
299
+ keyType,
300
+ maxLength,
301
+ numberKeyRowCount,
302
+ onInputChange,
303
+ onKeypadClose,
304
+ keypad,
305
+ setIsShow,
306
+ setKeypadId,
307
+ removeKeypad
308
+ } = options;
309
+ return {
310
+ name: `xk-pad-${name}`,
311
+ editBox: inputRef,
312
+ keyType,
313
+ width: KEYPAD_DEFAULTS.WIDTH,
314
+ position: { top: KEYPAD_DEFAULTS.POSITION_TOP, left: null },
315
+ viewType: KEYPAD_DEFAULTS.VIEW_TYPE,
316
+ closeDelay: KEYPAD_DEFAULTS.CLOSE_DELAY,
317
+ autoKeyResize: true,
318
+ isE2E: true,
319
+ onlyMobile: false,
320
+ hasPressEffect: true,
321
+ maxLength,
322
+ numberKeyRowCount,
323
+ onInputChange: createInputChangeHandler({
324
+ maxLength,
325
+ keypad,
326
+ setIsShow,
327
+ setKeypadId,
328
+ onInputChange
329
+ }),
330
+ onKeypadClose: createCloseHandler({
331
+ name,
332
+ inputRef,
333
+ keyType,
334
+ keypad,
335
+ setIsShow,
336
+ setKeypadId,
337
+ onKeypadClose,
338
+ removeKeypad
339
+ })
340
+ };
341
+ };
342
+ var createInputChangeHandler = (options) => {
343
+ const { maxLength, keypad, setIsShow, setKeypadId, onInputChange } = options;
344
+ return (newLength) => {
345
+ onInputChange(newLength);
346
+ if (maxLength > 0 && newLength >= maxLength && keypad.isOpend()) {
347
+ keypad.close();
348
+ setIsShow(false);
349
+ setKeypadId(null);
350
+ }
351
+ };
352
+ };
353
+ var createCloseHandler = (options) => {
354
+ const { name, inputRef, keyType, keypad, setIsShow, setKeypadId, onKeypadClose, removeKeypad } = options;
355
+ return () => {
356
+ const sessionInfo = keypad.get_sessionInfo();
357
+ const length = calculateInputLength(sessionInfo.input);
358
+ const response = {
359
+ length,
360
+ element: inputRef,
361
+ sessionInfo: {
362
+ input: sessionInfo.input,
363
+ sessionId: sessionInfo.sessionId,
364
+ secToken: sessionInfo.secToken
365
+ },
366
+ keyType,
367
+ name
368
+ };
369
+ setIsShow(false);
370
+ setKeypadId(null);
371
+ onKeypadClose(response);
372
+ removeKeypad(name);
373
+ };
374
+ };
375
+ var calculateInputLength = (input) => {
376
+ if (!input || input.length === 0) {
377
+ return 0;
378
+ }
379
+ if (input.indexOf(INPUT_SEPARATOR) >= 0) {
380
+ return input.split(INPUT_SEPARATOR).length;
381
+ }
382
+ return 0;
383
+ };
384
+
385
+ // src/features/keypad/types/keypad-message.enum.ts
182
386
  var KeypadMessageTypeEnum = /* @__PURE__ */ ((KeypadMessageTypeEnum2) => {
183
387
  KeypadMessageTypeEnum2[KeypadMessageTypeEnum2["NotSupportDevice"] = -1] = "NotSupportDevice";
184
388
  KeypadMessageTypeEnum2[KeypadMessageTypeEnum2["Already"] = -2] = "Already";
@@ -186,7 +390,7 @@ var KeypadMessageTypeEnum = /* @__PURE__ */ ((KeypadMessageTypeEnum2) => {
186
390
  })(KeypadMessageTypeEnum || {});
187
391
  var keypad_message_enum_default = KeypadMessageTypeEnum;
188
392
 
189
- // src/types/keypad-mode.enum.ts
393
+ // src/features/keypad/types/keypad-mode.enum.ts
190
394
  var KeypadModeEnum = /* @__PURE__ */ ((KeypadModeEnum2) => {
191
395
  KeypadModeEnum2["QWERTY_SMART"] = "qwertysmart";
192
396
  KeypadModeEnum2["NUMBER"] = "number";
@@ -194,172 +398,143 @@ var KeypadModeEnum = /* @__PURE__ */ ((KeypadModeEnum2) => {
194
398
  })(KeypadModeEnum || {});
195
399
  var keypad_mode_enum_default = KeypadModeEnum;
196
400
 
197
- // src/utils/uuid.ts
198
- var uuidv4 = () => {
199
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
200
- const r = Math.random() * 16 || 0, v = c === "x" ? r : r && 3 || 8;
201
- return v.toString(16);
202
- });
203
- };
204
-
205
- // src/hooks/use-keypad.ts
401
+ // src/features/keypad/hooks/use-keypad.tsx
402
+ var keypadCounter = 0;
403
+ var generateKeypadId = () => `kp-${++keypadCounter}`;
206
404
  function useKeypad() {
207
405
  const [isShow, setIsShow] = useState(false);
406
+ const [keypadId, setKeypadId] = useState(null);
208
407
  const show = useCallback(
209
- (inputRef, onInputChange, onKeypadClose, keyType = keypad_mode_enum_default.QWERTY_SMART, maxLength, numberKeyRowCount) => {
210
- const name = uuidv4();
408
+ (inputRef, onInputChange, onKeypadClose, keyType = keypad_mode_enum_default.QWERTY_SMART, maxLength = 0, numberKeyRowCount = KEYPAD_DEFAULTS.NUMBER_KEY_ROW_COUNT) => {
211
409
  if (!XKeyboardMobileInstance) {
410
+ console.warn("[useKeypad] XKeyboardMobileInstance\uAC00 \uCD08\uAE30\uD654\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
212
411
  return;
213
412
  }
214
- let keypad = XKeyboardMobileInstance.getKeypad(name);
413
+ const name = generateKeypadId();
414
+ const keypad = getOrCreateKeypad(name);
215
415
  if (!keypad) {
216
- keypad = XKeyboardMobileInstance.newKeypad(name);
217
- } else {
218
- keypad.refresh();
219
- }
220
- if (onInputChange && typeof onInputChange === "function") {
221
- onInputChange(0);
416
+ console.error("[useKeypad] \uD0A4\uD328\uB4DC \uC778\uC2A4\uD134\uC2A4 \uC0DD\uC131\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
417
+ return;
222
418
  }
223
- const keyPadParam = {
224
- name: `xk-pad-${name}`,
225
- editBox: inputRef,
419
+ onInputChange(0);
420
+ const keyPadParam = createKeypadParam({
421
+ name,
422
+ inputRef,
226
423
  keyType,
227
- width: 100,
228
- position: { top: 10, left: null },
229
- viewType: "half",
230
- closeDelay: 0,
231
- autoKeyResize: true,
232
- isE2E: true,
233
- onlyMobile: false,
234
- hasPressEffect: true,
235
424
  maxLength,
236
425
  numberKeyRowCount,
237
- onInputChange: (newLength) => {
238
- if (onInputChange && typeof onInputChange === "function") {
239
- onInputChange(newLength);
240
- }
241
- if (!keypad) {
242
- return;
243
- }
244
- if (keyPadParam.maxLength && keyPadParam.maxLength > 0 && newLength >= keyPadParam.maxLength && keypad.isOpend()) {
245
- keypad.close();
246
- setIsShow(false);
247
- return;
248
- }
249
- },
250
- onKeypadClose: () => {
251
- if (!keypad) {
252
- return;
253
- }
254
- if (!XKeyboardMobileInstance) {
255
- return;
256
- }
257
- if (onKeypadClose && typeof onKeypadClose === "function") {
258
- const sessionInfo = keypad.get_sessionInfo();
259
- let length = 0;
260
- if (sessionInfo.input && sessionInfo.input.indexOf(",") >= 0) {
261
- length = sessionInfo.input.split(",").length;
262
- }
263
- const res = {
264
- length,
265
- element: inputRef,
266
- sessionInfo: {
267
- input: sessionInfo.input,
268
- sessionId: sessionInfo.sessionId,
269
- secToken: sessionInfo.secToken
270
- },
271
- keyType,
272
- name
273
- };
274
- setIsShow(false);
275
- onKeypadClose(res);
276
- }
277
- XKeyboardMobileInstance.removeKeypad(name);
278
- if (!onKeypadClose) {
279
- throw new Error("not found onKeypadClose");
280
- }
281
- if (typeof onKeypadClose !== "function") {
282
- throw new Error("not found onKeypadClose");
283
- }
284
- }
285
- };
286
- const aRet = keypad.initialize(keyPadParam);
287
- if (aRet === keypad_message_enum_default.NotSupportDevice) {
288
- alert("\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uAE30\uAE30 \uC785\uB2C8\uB2E4.");
289
- return;
290
- } else if (aRet === keypad_message_enum_default.Already) {
426
+ onInputChange,
427
+ onKeypadClose,
428
+ keypad,
429
+ setIsShow,
430
+ setKeypadId,
431
+ removeKeypad: (n) => XKeyboardMobileInstance?.removeKeypad(n)
432
+ });
433
+ const result = keypad.initialize(keyPadParam);
434
+ if (!handleInitializeResult(result)) {
435
+ console.warn("[useKeypad] \uD0A4\uD328\uB4DC \uCD08\uAE30\uD654 \uC2E4\uD328:", result);
291
436
  return;
292
437
  }
438
+ setKeypadId(name);
293
439
  setIsShow(true);
294
440
  },
295
441
  []
296
442
  );
297
- const getValue = (name) => {
443
+ const getOrCreateKeypad = (name) => {
298
444
  if (!XKeyboardMobileInstance) {
299
- return;
445
+ return void 0;
300
446
  }
301
- const targetKeypad = XKeyboardMobileInstance.keypads[name];
302
- if (!targetKeypad) {
303
- return null;
447
+ let keypad = XKeyboardMobileInstance.getKeypad(name);
448
+ if (!keypad) {
449
+ keypad = XKeyboardMobileInstance.newKeypad(name);
450
+ } else {
451
+ keypad.refresh();
304
452
  }
305
- return targetKeypad.get_sessionInfo();
453
+ return keypad;
306
454
  };
307
- const find = (name) => {
308
- if (!XKeyboardMobileInstance) {
309
- return;
455
+ const handleInitializeResult = (result) => {
456
+ if (result === keypad_message_enum_default.NotSupportDevice) {
457
+ alert("\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uAE30\uAE30 \uC785\uB2C8\uB2E4.");
458
+ return false;
310
459
  }
311
- if (name) {
312
- return XKeyboardMobileInstance.keypads[name];
313
- } else {
314
- const keypadIds = Object.keys(XKeyboardMobileInstance.keypads);
315
- for (const keypadId of keypadIds) {
316
- const targetKeypad = XKeyboardMobileInstance.keypads[keypadId];
317
- if (targetKeypad && targetKeypad.isOpen()) {
318
- return targetKeypad;
319
- }
320
- }
460
+ if (result === keypad_message_enum_default.Already) {
461
+ return false;
321
462
  }
463
+ return true;
322
464
  };
323
- const isOpen = (name) => {
324
- const targetKeypad = find(name);
325
- return !!targetKeypad;
326
- };
327
- const close = (name) => {
328
- const targetKeypad = find(name);
329
- if (targetKeypad) {
330
- targetKeypad.close();
331
- setIsShow(false);
465
+ const getValue = useCallback((name) => {
466
+ if (!XKeyboardMobileInstance) {
467
+ return void 0;
332
468
  }
333
- };
334
- const clear = (name) => {
335
- const _xkmodule = find(name);
336
- if (_xkmodule) {
337
- _xkmodule.clear();
338
- setIsShow(false);
469
+ const targetKeypad = XKeyboardMobileInstance.keypads[name];
470
+ return targetKeypad?.get_sessionInfo() ?? null;
471
+ }, []);
472
+ const find = useCallback((name) => {
473
+ if (!XKeyboardMobileInstance) {
474
+ return void 0;
339
475
  }
340
- };
341
- const refresh = (name) => {
342
- const targetKeypad = find(name);
343
- if (targetKeypad) {
344
- targetKeypad.refresh();
476
+ if (name) {
477
+ return XKeyboardMobileInstance.keypads[name];
345
478
  }
346
- };
479
+ const keypadIds = Object.keys(XKeyboardMobileInstance.keypads);
480
+ for (const keypadId2 of keypadIds) {
481
+ const targetKeypad = XKeyboardMobileInstance.keypads[keypadId2];
482
+ if (targetKeypad?.isOpen()) {
483
+ return targetKeypad;
484
+ }
485
+ }
486
+ return void 0;
487
+ }, []);
488
+ const isOpen = useCallback(
489
+ (name) => {
490
+ return !!find(name);
491
+ },
492
+ [find]
493
+ );
494
+ const close = useCallback(
495
+ (name) => {
496
+ const targetKeypad = find(name);
497
+ if (targetKeypad) {
498
+ targetKeypad.close();
499
+ setIsShow(false);
500
+ setKeypadId(null);
501
+ }
502
+ },
503
+ [find]
504
+ );
505
+ const clear = useCallback(
506
+ (name) => {
507
+ const targetKeypad = find(name);
508
+ if (targetKeypad) {
509
+ targetKeypad.clear();
510
+ setIsShow(false);
511
+ setKeypadId(null);
512
+ }
513
+ },
514
+ [find]
515
+ );
516
+ const refresh = useCallback(
517
+ (name) => {
518
+ const targetKeypad = find(name);
519
+ targetKeypad?.refresh();
520
+ },
521
+ [find]
522
+ );
347
523
  useEffect(() => {
348
524
  const handleMouseDown = (event) => {
349
525
  if (!XKeyboardMobileInstance) {
350
526
  return;
351
527
  }
352
- const keypadAlertDivTag = document.querySelector("#xkalert");
528
+ const keypadAlertDivTag = document.querySelector(KEYPAD_SELECTORS.ALERT);
353
529
  const keypadDivTag = keypadAlertDivTag?.parentElement;
354
530
  if (keypadDivTag && !keypadDivTag.contains(event.target)) {
355
531
  XKeyboardMobileInstance.closeAll();
356
532
  setIsShow(false);
533
+ setKeypadId(null);
357
534
  }
358
535
  };
359
536
  document.addEventListener("mousedown", handleMouseDown);
360
- return () => {
361
- document.removeEventListener("mousedown", handleMouseDown);
362
- };
537
+ return () => document.removeEventListener("mousedown", handleMouseDown);
363
538
  }, []);
364
539
  return {
365
540
  show,
@@ -369,174 +544,354 @@ function useKeypad() {
369
544
  getValue,
370
545
  isOpen,
371
546
  find,
372
- isShow
547
+ isShow,
548
+ keypadId
373
549
  };
374
550
  }
375
- function Keypad({ value, onChange, mode, maxLength, inputElement }) {
376
- const { show} = useKeypad();
551
+ var createMaskedValue = (length) => {
552
+ return "".padStart(length, MASK_CHAR);
553
+ };
554
+ var getInputLength = (input) => {
555
+ if (!input || input.length === 0) {
556
+ return 0;
557
+ }
558
+ return input.split(INPUT_SEPARATOR).length;
559
+ };
560
+ var KEYPAD_ID_PREFIX = "xk-pad-";
561
+ var findKeypadById = (keypadId) => {
562
+ const fullId = `${KEYPAD_ID_PREFIX}${keypadId}`;
563
+ const element = document.getElementById(fullId);
564
+ if (element && element.offsetHeight > 0) {
565
+ return element;
566
+ }
567
+ return null;
568
+ };
569
+ var waitForKeypadElement = (keypadId) => {
570
+ return new Promise((resolve) => {
571
+ const existing = findKeypadById(keypadId);
572
+ if (existing) {
573
+ resolve(existing);
574
+ return;
575
+ }
576
+ const fullId = `${KEYPAD_ID_PREFIX}${keypadId}`;
577
+ const observer = new MutationObserver((_mutations, obs) => {
578
+ const keypad = document.getElementById(fullId);
579
+ if (keypad && keypad.offsetHeight > 0) {
580
+ obs.disconnect();
581
+ resolve(keypad);
582
+ }
583
+ });
584
+ observer.observe(document.body, {
585
+ childList: true,
586
+ subtree: true
587
+ });
588
+ setTimeout(() => {
589
+ observer.disconnect();
590
+ resolve(null);
591
+ }, 3e3);
592
+ });
593
+ };
594
+ var toKeypadRect = (rect) => ({
595
+ height: rect.height,
596
+ top: rect.top,
597
+ bottom: rect.bottom,
598
+ width: rect.width,
599
+ left: rect.left,
600
+ right: rect.right
601
+ });
602
+ function Keypad({
603
+ value,
604
+ onChange,
605
+ mode,
606
+ maxLength,
607
+ inputElement,
608
+ onShow,
609
+ numberKeyRowCount = KEYPAD_DEFAULTS.NUMBER_KEY_ROW_COUNT,
610
+ wrapId
611
+ }) {
612
+ const { show, isShow, keypadId } = useKeypad();
377
613
  const inputRef = useRef(null);
378
614
  const [maskedValue, setMaskedValue] = useState("");
379
- const onCloseBack = useCallback(
615
+ useEffect(() => {
616
+ const input = inputRef.current;
617
+ if (!input) {
618
+ return;
619
+ }
620
+ if (isShow && keypadId) {
621
+ waitForKeypadElement(keypadId).then((keypadElement) => {
622
+ if (keypadElement) {
623
+ const rect = keypadElement.getBoundingClientRect();
624
+ const wrap = wrapId && document.getElementById(wrapId);
625
+ if (wrap) {
626
+ const inputRect = input.getBoundingClientRect();
627
+ if (inputRect.bottom > rect.top) {
628
+ const diff = inputRect.bottom - rect.top;
629
+ wrap.style.transform = `translateY(-${diff}px)`;
630
+ }
631
+ }
632
+ onShow?.({
633
+ isShow: true,
634
+ keypadElement,
635
+ keypadRect: toKeypadRect(rect),
636
+ inputElement: input
637
+ });
638
+ } else {
639
+ onShow?.({ isShow: true, keypadElement: null, keypadRect: null, inputElement: input });
640
+ }
641
+ });
642
+ } else {
643
+ const wrap = wrapId && document.getElementById(wrapId);
644
+ if (wrap) {
645
+ wrap.style.transform = "";
646
+ }
647
+ onShow?.({ isShow: false, keypadElement: null, keypadRect: null, inputElement: input });
648
+ }
649
+ }, [isShow, keypadId, onShow, wrapId]);
650
+ const handleKeypadClose = useCallback(
380
651
  (res) => {
381
652
  onChange(res.sessionInfo);
382
653
  },
383
654
  [onChange]
384
655
  );
385
- const onChangeBack = useCallback(
656
+ const handleInputChange = useCallback(
386
657
  (length) => {
387
658
  if (length === 0) {
388
659
  onChange(null);
389
660
  }
390
- setMaskedValue("".padStart(length, "*"));
661
+ setMaskedValue(createMaskedValue(length));
391
662
  },
392
- [onChange, setMaskedValue]
663
+ [onChange]
393
664
  );
394
- const handleKeypad = useCallback(() => {
395
- show(inputRef.current, onChangeBack, onCloseBack, mode, maxLength, 3);
396
- }, [show, inputRef, onChangeBack, onCloseBack, mode, maxLength]);
397
- const enhancedOnClick = (existingClickHandler, newClickHandler) => {
398
- return () => {
399
- if (existingClickHandler) {
400
- existingClickHandler();
401
- }
402
- newClickHandler();
403
- };
404
- };
405
- const cloneElement = React2.cloneElement(inputElement, {
406
- ref: inputRef,
407
- value: maskedValue,
408
- onClick: enhancedOnClick(inputElement.props.onClick, handleKeypad)
409
- // iskeypadactive: isShow
410
- });
665
+ const handleOpenKeypad = useCallback(() => {
666
+ if (!inputRef.current) {
667
+ console.error("[Keypad] inputRef\uAC00 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
668
+ return;
669
+ }
670
+ show(inputRef.current, handleInputChange, handleKeypadClose, mode, maxLength, numberKeyRowCount);
671
+ }, [show, handleInputChange, handleKeypadClose, mode, maxLength, numberKeyRowCount]);
672
+ const combinedOnClick = useCallback(() => {
673
+ inputElement.props.onClick?.();
674
+ handleOpenKeypad();
675
+ }, [inputElement.props.onClick, handleOpenKeypad]);
411
676
  useEffect(() => {
412
- const length = value?.input && value.input.length > 0 ? value.input.split(",").length : 0;
413
- setMaskedValue("".padStart(length, "*"));
677
+ const length = getInputLength(value?.input);
678
+ setMaskedValue(createMaskedValue(length));
414
679
  }, [value]);
415
680
  useEffect(() => {
416
681
  if (inputRef.current && !(inputRef.current instanceof HTMLInputElement)) {
417
- throw new Error("Input \uD0DC\uADF8\uAC00 \uC815\uC758\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
682
+ throw new Error("[Keypad] inputElement\uB294 HTMLInputElement\uB97C \uB80C\uB354\uB9C1\uD574\uC57C \uD569\uB2C8\uB2E4.");
418
683
  }
419
684
  }, []);
420
- return /* @__PURE__ */ jsx(Fragment, { children: cloneElement });
685
+ const clonedElement = useMemo(
686
+ () => React2.cloneElement(inputElement, {
687
+ ref: inputRef,
688
+ value: maskedValue,
689
+ onClick: combinedOnClick
690
+ }),
691
+ [inputElement, maskedValue, combinedOnClick]
692
+ );
693
+ return /* @__PURE__ */ jsx(Fragment, { children: clonedElement });
421
694
  }
422
- function useNxlOne({ bizCode, tmplCode, ncsrInfoUuid, nlcCtfnId, t }) {
423
- const buildUrl = useCallback(() => {
424
- const baseUrl = getNlcHostFromEnvironment();
425
- const targetUrl = new URL("/auth/v1", baseUrl);
426
- targetUrl.searchParams.set("bizCode", bizCode);
427
- targetUrl.searchParams.set("tmplCode", tmplCode);
428
- if (nlcCtfnId) {
429
- targetUrl.searchParams.set("nlcCtfnId", nlcCtfnId);
430
- }
431
- if (t) {
432
- targetUrl.searchParams.set("t", t);
433
- }
434
- if (ncsrInfoUuid) {
435
- targetUrl.searchParams.set("ncsrInfoUuid", ncsrInfoUuid);
695
+
696
+ // src/features/nxl-one/libs/nxl-one.ts
697
+ var NXL_ONE_ENV_URLS = {
698
+ dev: "https://nxl-nlc-dev.hanwhalife.com",
699
+ stg: "https://nxl-nlc-stg.hanwhalife.com",
700
+ //prod: 'https://nxl-nlc.hanwhalife.com',
701
+ prd: "https://nxl-nlc.hanwhalife.com"
702
+ };
703
+ var NXL_ONE_AUTH_PATH = "/auth/v1";
704
+ var NXL_ONE_DEFAULT_POPUP = {
705
+ width: 744,
706
+ height: 720,
707
+ windowName: "self_cert"
708
+ };
709
+ var NXL_ONE_DEFAULT_IFRAME = {
710
+ sandbox: "allow-scripts allow-forms allow-same-origin"
711
+ };
712
+ var detectEnvFromUrl = () => {
713
+ if (typeof window === "undefined") {
714
+ return "prd";
715
+ }
716
+ const { href } = window.location;
717
+ if (href.includes("localhost") || href.includes("dev")) {
718
+ return "dev";
719
+ }
720
+ if (href.includes("stg")) {
721
+ return "stg";
722
+ }
723
+ return "prd";
724
+ };
725
+ var getBaseUrl = (env) => NXL_ONE_ENV_URLS[env ?? detectEnvFromUrl()];
726
+ var buildAuthUrl = (baseUrl, params, mode) => {
727
+ const url = new URL(NXL_ONE_AUTH_PATH, baseUrl);
728
+ url.searchParams.set("bizCode", params.bizCode);
729
+ url.searchParams.set("tmplCode", params.tmplCode);
730
+ url.searchParams.set("m", mode);
731
+ if (params.nlcCtfnId) {
732
+ url.searchParams.set("nlcCtfnId", params.nlcCtfnId);
733
+ }
734
+ if (params.t) {
735
+ url.searchParams.set("t", params.t);
736
+ }
737
+ if (params.ncsrInfoUuid) {
738
+ url.searchParams.set("ncsrInfoUuid", params.ncsrInfoUuid);
739
+ }
740
+ return url.toString();
741
+ };
742
+ var calcPopupCenter = (width, height) => ({
743
+ left: window.screenX + (window.outerWidth - width) / 2,
744
+ top: window.screenY + (window.outerHeight - height) / 2
745
+ });
746
+ var NXL_ONE_ERROR_CODES = {
747
+ /** 팝업이 브라우저에 의해 차단됨 */
748
+ POPUP_BLOCKED: "POPUP_BLOCKED",
749
+ /** 사용자가 인증을 취소함 */
750
+ AUTH_CANCELLED: "AUTH_CANCELLED",
751
+ /** 인증 실패 */
752
+ AUTH_FAILED: "AUTH_FAILED",
753
+ /** 잘못된 응답 */
754
+ INVALID_RESPONSE: "INVALID_RESPONSE"
755
+ };
756
+ var ERROR_MESSAGES = {
757
+ POPUP_BLOCKED: "\uD31D\uC5C5\uC774 \uCC28\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uD31D\uC5C5 \uCC28\uB2E8\uC744 \uD574\uC81C\uD574\uC8FC\uC138\uC694.",
758
+ AUTH_CANCELLED: "\uC778\uC99D\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
759
+ AUTH_FAILED: "\uC778\uC99D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.",
760
+ INVALID_RESPONSE: "\uC798\uBABB\uB41C \uC751\uB2F5\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4."
761
+ };
762
+ var createNxlOneError = (code, customMessage) => ({
763
+ code,
764
+ message: customMessage ?? ERROR_MESSAGES[code]
765
+ });
766
+ var isNxlOneError = (value) => typeof value === "object" && value !== null && "code" in value && "message" in value && Object.values(NXL_ONE_ERROR_CODES).includes(value.code);
767
+ function NxlOneIframe({
768
+ bizCode,
769
+ tmplCode,
770
+ ncsrInfoUuid,
771
+ nlcCtfnId,
772
+ env,
773
+ width = "100%",
774
+ height = "100%",
775
+ className,
776
+ style,
777
+ allow,
778
+ sandbox = NXL_ONE_DEFAULT_IFRAME.sandbox,
779
+ onSuccess,
780
+ onError,
781
+ onClose,
782
+ strictOrigin = true,
783
+ autoHide = false
784
+ }) {
785
+ const [visible, setVisible] = useState(true);
786
+ const callbacksRef = useRef({ onSuccess, onError, onClose });
787
+ callbacksRef.current = { onSuccess, onError, onClose };
788
+ const baseUrl = env ? getBaseUrl(env) : getNlcHostFromEnvironment();
789
+ const targetUrl = useMemo(
790
+ () => buildAuthUrl(baseUrl, { bizCode, tmplCode, nlcCtfnId, ncsrInfoUuid }, "iframe"),
791
+ [baseUrl, bizCode, tmplCode, nlcCtfnId, ncsrInfoUuid]
792
+ );
793
+ useEffect(() => {
794
+ const handleMessage = (e) => {
795
+ if (strictOrigin && e.origin !== baseUrl) {
796
+ return;
797
+ }
798
+ const data = e.data;
799
+ if (!data?.action) {
800
+ return;
801
+ }
802
+ const { onSuccess: onSuccess2, onError: onError2, onClose: onClose2 } = callbacksRef.current;
803
+ if (autoHide) {
804
+ setVisible(false);
805
+ }
806
+ switch (data.action) {
807
+ case "complete":
808
+ onSuccess2?.(data);
809
+ break;
810
+ case "error":
811
+ onError2?.(data);
812
+ break;
813
+ case "close":
814
+ onClose2(data);
815
+ break;
816
+ }
817
+ };
818
+ window.addEventListener("message", handleMessage);
819
+ return () => window.removeEventListener("message", handleMessage);
820
+ }, [baseUrl, strictOrigin, autoHide]);
821
+ if (!visible) {
822
+ return null;
823
+ }
824
+ return /* @__PURE__ */ jsx(
825
+ "iframe",
826
+ {
827
+ src: targetUrl,
828
+ width: typeof width === "number" ? `${width}px` : width,
829
+ height: typeof height === "number" ? `${height}px` : height,
830
+ className,
831
+ style: { border: 0, ...style },
832
+ sandbox,
833
+ allow
436
834
  }
437
- return targetUrl;
438
- }, [bizCode, tmplCode, t, ncsrInfoUuid, nlcCtfnId]);
835
+ );
836
+ }
837
+ function useNxlOne({ bizCode, tmplCode, ncsrInfoUuid, nlcCtfnId, env }) {
838
+ const baseUrl = env ? getBaseUrl(env) : getNlcHostFromEnvironment();
839
+ const params = useMemo(
840
+ () => ({ bizCode, tmplCode, nlcCtfnId, ncsrInfoUuid }),
841
+ [bizCode, tmplCode, nlcCtfnId, ncsrInfoUuid]
842
+ );
439
843
  const redirect = () => {
440
- const targetUrl = buildUrl();
441
- targetUrl.searchParams.set("m", "redirect");
442
- location.href = targetUrl.toString();
844
+ location.href = buildAuthUrl(baseUrl, params, "redirect");
443
845
  };
444
- const open = async (options) => {
846
+ const open = (options) => {
445
847
  return new Promise((resolve, reject) => {
446
- const targetUrlBuild = buildUrl();
447
- targetUrlBuild.searchParams.set("m", "popup");
448
- const targetUrl = targetUrlBuild.toString();
449
- const width = options && options.popupWidth || 744;
450
- const height = options && options.popupHeight || 720;
451
- const left = options && options.popupLeft || window.screenX + (window.outerWidth - width) / 2;
452
- const top = options && options.popupTop || window.screenX + (window.outerWidth - width) / 2;
453
- const popupFeatures = `width=${width},height=${height},left=${left},top=${top}`;
454
- const popup = window.open(targetUrlBuild.toString(), options && options.windowName || "self_cert", popupFeatures);
848
+ const targetUrl = buildAuthUrl(baseUrl, params, "popup");
849
+ const width = options?.width ?? NXL_ONE_DEFAULT_POPUP.width;
850
+ const height = options?.height ?? NXL_ONE_DEFAULT_POPUP.height;
851
+ const center = calcPopupCenter(width, height);
852
+ const left = options?.left ?? center.left;
853
+ const top = options?.top ?? center.top;
854
+ const windowName = options?.windowName ?? NXL_ONE_DEFAULT_POPUP.windowName;
855
+ const popup = window.open(targetUrl, windowName, `width=${width},height=${height},left=${left},top=${top}`);
455
856
  if (!popup) {
456
- reject(new Error("\uCC28\uB2E8\uB418\uAC70\uB098 \uC5F4\uB9AC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."));
857
+ reject(createNxlOneError(NXL_ONE_ERROR_CODES.POPUP_BLOCKED));
457
858
  return;
458
859
  }
459
860
  let isSettled = false;
861
+ let popupChecker = 0;
862
+ const cleanup = () => {
863
+ window.removeEventListener("message", handleMessage);
864
+ window.clearInterval(popupChecker);
865
+ };
460
866
  const handleMessage = (event) => {
461
- if (event.origin !== new URL(targetUrl).origin) {
867
+ if (event.origin !== baseUrl) {
462
868
  return;
463
869
  }
464
- if (!isSettled) {
465
- isSettled = true;
466
- if (event.data) {
467
- resolve(event.data);
468
- } else {
469
- reject("\uC778\uC99D\uC774 \uCDE8\uC18C\uB418\uAC70\uB098 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
470
- }
471
- popup?.close();
472
- window.removeEventListener("message", handleMessage);
473
- }
474
- };
475
- window.addEventListener("message", handleMessage);
476
- });
477
- };
478
- const Iframe = function({
479
- height = 720,
480
- className,
481
- style,
482
- allow,
483
- sandbox = "allow-scripts allow-forms allow-same-origin",
484
- onSuccess,
485
- onError,
486
- onClose,
487
- strictOrigin = true
488
- }) {
489
- const targetUrlBuild = buildUrl();
490
- targetUrlBuild.searchParams.set("m", "iframe");
491
- const targetUrl = targetUrlBuild.toString();
492
- useEffect(() => {
493
- if (!onSuccess && !onError) {
494
- return;
495
- }
496
- const handler = (e) => {
497
- const baseUrl = getNlcHostFromEnvironment();
498
- if (strictOrigin && e.origin !== baseUrl) {
870
+ if (isSettled) {
499
871
  return;
500
872
  }
501
- const d = e.data;
502
- if (d) {
503
- if (d.action === "close") {
504
- onClose(d);
505
- return;
506
- } else if (onError && d.action === "error") {
507
- onError(d);
508
- return;
509
- } else if (onSuccess && d.action === "complete") {
510
- onSuccess(d);
511
- return;
512
- }
513
- } else if (onError) {
514
- onError({
515
- action: "error",
516
- nlcCtfnId: "",
517
- redirectUrl: ""
518
- });
873
+ isSettled = true;
874
+ cleanup();
875
+ popup.close();
876
+ if (event.data) {
877
+ resolve(event.data);
878
+ } else {
879
+ reject(createNxlOneError(NXL_ONE_ERROR_CODES.INVALID_RESPONSE));
519
880
  }
520
881
  };
521
- window.addEventListener("message", handler);
522
- return () => window.removeEventListener("message", handler);
523
- }, [onClose, onError, onSuccess, strictOrigin]);
524
- return /* @__PURE__ */ jsx(
525
- "iframe",
526
- {
527
- src: targetUrl,
528
- width: "100%",
529
- height: typeof height === "number" ? `${height}px` : height,
530
- style: { border: 0, ...style },
531
- className,
532
- sandbox,
533
- ...allow ? { allow } : {}
534
- }
535
- );
882
+ popupChecker = window.setInterval(() => {
883
+ if (popup.closed && !isSettled) {
884
+ isSettled = true;
885
+ cleanup();
886
+ reject(createNxlOneError(NXL_ONE_ERROR_CODES.AUTH_CANCELLED));
887
+ }
888
+ }, 300);
889
+ window.addEventListener("message", handleMessage);
890
+ });
536
891
  };
537
- return { redirect, open, Iframe };
892
+ return { redirect, open };
538
893
  }
539
894
 
540
- export { Keypad, keypad_mode_enum_default as KeypadModeEnum, V3Provider, useNxlOne, useV3 };
895
+ export { Keypad, keypad_mode_enum_default as KeypadModeEnum, NXL_ONE_ERROR_CODES, NxlOneIframe, V3Provider, createNxlOneError, isNxlOneError, useKeypad, useNxlOne, useV3 };
541
896
  //# sourceMappingURL=index.js.map
542
897
  //# sourceMappingURL=index.js.map