woori-fisa-account-form 1.0.7

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/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # 우리 FISA 금융 React 컴포넌트 프로젝트
2
+
3
+ ## 개요
4
+ 이 프로젝트는 금융 관련 UI/UX 컴포넌트를 제공하는 리액트 컴포넌트 라이브러리입니다.
5
+ 현재 두 가지 주요 컴포넌트를 포함하고 있으며, 사용자 경험을 높이는 직관적인 UI를 목표로 합니다.
6
+
7
+ ---
8
+
9
+ ## 주요 컴포넌트
10
+
11
+ ### 1. 카드 혜택 분석 컴포넌트
12
+ <img width="517" height="583" alt="image" src="https://github.com/user-attachments/assets/96f661ae-7a49-4bca-b757-9b34bc939b5e" />
13
+
14
+ - **기능:** 사용자가 적립받은 금액과 사용한 혜택 금액을 시각적으로 확인할 수 있습니다.
15
+ - **목적:** 카드 혜택 데이터를 직관적으로 보여주어 사용자 체류 시간을 늘리는 UI 제공
16
+ - **특징:**
17
+ - 카드 사용 내역 기반 적립 및 사용 금액 시각화
18
+ - 시각화된 데이터로 빠른 이해 가능
19
+ - 반응형 UI 지원
20
+
21
+ ### 2. 계좌 입력 컴포넌트
22
+ <img width="545" height="396" alt="image" src="https://github.com/user-attachments/assets/6ca5c56b-6431-4914-a615-1ac6b18349e8" />
23
+
24
+ - **기능:** 사용자가 계좌를 입력하면 실시간으로 은행을 분석하여 보여줍니다. 사용자의 클립보드의 정보를 가져와 복사한 계좌 정보를 자동으로 붙여넣기 가능하도록 하였습니다.
25
+ - **지원 은행:** 현재는 카카오뱅크, 토스뱅크, 우리은행만 지원
26
+ - **목적:** 계좌 입력 과정의 편의성 향상
27
+ - **특징:**
28
+ - 은행 자동 분석 및 선택 UI 제공
29
+ - 직관적인 입력 방식
30
+ - 복사한 계좌 정보 즉시 입력 제공
31
+
32
+ ---
33
+
34
+ ## 설치
35
+ > 현재 NPM 배포는 아직 진행 중입니다.
36
+ > 추후 NPM 패키지로 설치 가능하도록 업데이트 예정입니다.
@@ -0,0 +1,606 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.appendChild(document.createTextNode(":root{--color-primary-pink: #FF9E9E;--color-primary-mint: #4CD9C0;--color-bg-gray: #F5F6F8;--color-text-main: #333333;--color-text-sub: #888888;--color-white: #FFFFFF;--app-max-width: 420px}*{box-sizing:border-box;margin:0;padding:0}body{font-family:Noto Sans KR,sans-serif;background-color:#e0e0e0;display:flex;justify-content:center;min-height:100vh}#root{width:100%;max-width:var(--app-max-width);background-color:var(--color-white);min-height:100vh;box-shadow:0 0 20px #0000001a;position:relative;overflow-x:hidden}.flex-center{display:flex;justify-content:center;align-items:center}button{cursor:pointer;border:none;background:none;font-family:inherit}")),document.head.appendChild(o)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
2
+ import te, { useState as v, useRef as ne, useEffect as N } from "react";
3
+ var E = { exports: {} }, x = {};
4
+ var D;
5
+ function se() {
6
+ if (D) return x;
7
+ D = 1;
8
+ var r = /* @__PURE__ */ Symbol.for("react.transitional.element"), s = /* @__PURE__ */ Symbol.for("react.fragment");
9
+ function a(t, n, l) {
10
+ var m = null;
11
+ if (l !== void 0 && (m = "" + l), n.key !== void 0 && (m = "" + n.key), "key" in n) {
12
+ l = {};
13
+ for (var u in n)
14
+ u !== "key" && (l[u] = n[u]);
15
+ } else l = n;
16
+ return n = l.ref, {
17
+ $$typeof: r,
18
+ type: t,
19
+ key: m,
20
+ ref: n !== void 0 ? n : null,
21
+ props: l
22
+ };
23
+ }
24
+ return x.Fragment = s, x.jsx = a, x.jsxs = a, x;
25
+ }
26
+ var h = {};
27
+ var W;
28
+ function oe() {
29
+ return W || (W = 1, process.env.NODE_ENV !== "production" && (function() {
30
+ function r(e) {
31
+ if (e == null) return null;
32
+ if (typeof e == "function")
33
+ return e.$$typeof === K ? null : e.displayName || e.name || null;
34
+ if (typeof e == "string") return e;
35
+ switch (e) {
36
+ case R:
37
+ return "Fragment";
38
+ case V:
39
+ return "Profiler";
40
+ case z:
41
+ return "StrictMode";
42
+ case H:
43
+ return "Suspense";
44
+ case X:
45
+ return "SuspenseList";
46
+ case Q:
47
+ return "Activity";
48
+ }
49
+ if (typeof e == "object")
50
+ switch (typeof e.tag == "number" && console.error(
51
+ "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
52
+ ), e.$$typeof) {
53
+ case U:
54
+ return "Portal";
55
+ case J:
56
+ return e.displayName || "Context";
57
+ case q:
58
+ return (e._context.displayName || "Context") + ".Consumer";
59
+ case G:
60
+ var o = e.render;
61
+ return e = e.displayName, e || (e = o.displayName || o.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
62
+ case Z:
63
+ return o = e.displayName || null, o !== null ? o : r(e.type) || "Memo";
64
+ case k:
65
+ o = e._payload, e = e._init;
66
+ try {
67
+ return r(e(o));
68
+ } catch {
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+ function s(e) {
74
+ return "" + e;
75
+ }
76
+ function a(e) {
77
+ try {
78
+ s(e);
79
+ var o = !1;
80
+ } catch {
81
+ o = !0;
82
+ }
83
+ if (o) {
84
+ o = console;
85
+ var i = o.error, d = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
86
+ return i.call(
87
+ o,
88
+ "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
89
+ d
90
+ ), s(e);
91
+ }
92
+ }
93
+ function t(e) {
94
+ if (e === R) return "<>";
95
+ if (typeof e == "object" && e !== null && e.$$typeof === k)
96
+ return "<...>";
97
+ try {
98
+ var o = r(e);
99
+ return o ? "<" + o + ">" : "<...>";
100
+ } catch {
101
+ return "<...>";
102
+ }
103
+ }
104
+ function n() {
105
+ var e = _.A;
106
+ return e === null ? null : e.getOwner();
107
+ }
108
+ function l() {
109
+ return Error("react-stack-top-frame");
110
+ }
111
+ function m(e) {
112
+ if (C.call(e, "key")) {
113
+ var o = Object.getOwnPropertyDescriptor(e, "key").get;
114
+ if (o && o.isReactWarning) return !1;
115
+ }
116
+ return e.key !== void 0;
117
+ }
118
+ function u(e, o) {
119
+ function i() {
120
+ $ || ($ = !0, console.error(
121
+ "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
122
+ o
123
+ ));
124
+ }
125
+ i.isReactWarning = !0, Object.defineProperty(e, "key", {
126
+ get: i,
127
+ configurable: !0
128
+ });
129
+ }
130
+ function p() {
131
+ var e = r(this.type);
132
+ return F[e] || (F[e] = !0, console.error(
133
+ "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
134
+ )), e = this.props.ref, e !== void 0 ? e : null;
135
+ }
136
+ function M(e, o, i, d, y, w) {
137
+ var f = i.ref;
138
+ return e = {
139
+ $$typeof: P,
140
+ type: e,
141
+ key: o,
142
+ props: i,
143
+ _owner: d
144
+ }, (f !== void 0 ? f : null) !== null ? Object.defineProperty(e, "ref", {
145
+ enumerable: !1,
146
+ get: p
147
+ }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
148
+ configurable: !1,
149
+ enumerable: !1,
150
+ writable: !0,
151
+ value: 0
152
+ }), Object.defineProperty(e, "_debugInfo", {
153
+ configurable: !1,
154
+ enumerable: !1,
155
+ writable: !0,
156
+ value: null
157
+ }), Object.defineProperty(e, "_debugStack", {
158
+ configurable: !1,
159
+ enumerable: !1,
160
+ writable: !0,
161
+ value: y
162
+ }), Object.defineProperty(e, "_debugTask", {
163
+ configurable: !1,
164
+ enumerable: !1,
165
+ writable: !0,
166
+ value: w
167
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
168
+ }
169
+ function A(e, o, i, d, y, w) {
170
+ var f = o.children;
171
+ if (f !== void 0)
172
+ if (d)
173
+ if (ee(f)) {
174
+ for (d = 0; d < f.length; d++)
175
+ O(f[d]);
176
+ Object.freeze && Object.freeze(f);
177
+ } else
178
+ console.error(
179
+ "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
180
+ );
181
+ else O(f);
182
+ if (C.call(o, "key")) {
183
+ f = r(e);
184
+ var b = Object.keys(o).filter(function(re) {
185
+ return re !== "key";
186
+ });
187
+ d = 0 < b.length ? "{key: someKey, " + b.join(": ..., ") + ": ...}" : "{key: someKey}", L[f + d] || (b = 0 < b.length ? "{" + b.join(": ..., ") + ": ...}" : "{}", console.error(
188
+ `A props object containing a "key" prop is being spread into JSX:
189
+ let props = %s;
190
+ <%s {...props} />
191
+ React keys must be passed directly to JSX without using spread:
192
+ let props = %s;
193
+ <%s key={someKey} {...props} />`,
194
+ d,
195
+ f,
196
+ b,
197
+ f
198
+ ), L[f + d] = !0);
199
+ }
200
+ if (f = null, i !== void 0 && (a(i), f = "" + i), m(o) && (a(o.key), f = "" + o.key), "key" in o) {
201
+ i = {};
202
+ for (var T in o)
203
+ T !== "key" && (i[T] = o[T]);
204
+ } else i = o;
205
+ return f && u(
206
+ i,
207
+ typeof e == "function" ? e.displayName || e.name || "Unknown" : e
208
+ ), M(
209
+ e,
210
+ f,
211
+ i,
212
+ n(),
213
+ y,
214
+ w
215
+ );
216
+ }
217
+ function O(e) {
218
+ S(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === k && (e._payload.status === "fulfilled" ? S(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
219
+ }
220
+ function S(e) {
221
+ return typeof e == "object" && e !== null && e.$$typeof === P;
222
+ }
223
+ var g = te, P = /* @__PURE__ */ Symbol.for("react.transitional.element"), U = /* @__PURE__ */ Symbol.for("react.portal"), R = /* @__PURE__ */ Symbol.for("react.fragment"), z = /* @__PURE__ */ Symbol.for("react.strict_mode"), V = /* @__PURE__ */ Symbol.for("react.profiler"), q = /* @__PURE__ */ Symbol.for("react.consumer"), J = /* @__PURE__ */ Symbol.for("react.context"), G = /* @__PURE__ */ Symbol.for("react.forward_ref"), H = /* @__PURE__ */ Symbol.for("react.suspense"), X = /* @__PURE__ */ Symbol.for("react.suspense_list"), Z = /* @__PURE__ */ Symbol.for("react.memo"), k = /* @__PURE__ */ Symbol.for("react.lazy"), Q = /* @__PURE__ */ Symbol.for("react.activity"), K = /* @__PURE__ */ Symbol.for("react.client.reference"), _ = g.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, C = Object.prototype.hasOwnProperty, ee = Array.isArray, j = console.createTask ? console.createTask : function() {
224
+ return null;
225
+ };
226
+ g = {
227
+ react_stack_bottom_frame: function(e) {
228
+ return e();
229
+ }
230
+ };
231
+ var $, F = {}, I = g.react_stack_bottom_frame.bind(
232
+ g,
233
+ l
234
+ )(), Y = j(t(l)), L = {};
235
+ h.Fragment = R, h.jsx = function(e, o, i) {
236
+ var d = 1e4 > _.recentlyCreatedOwnerStacks++;
237
+ return A(
238
+ e,
239
+ o,
240
+ i,
241
+ !1,
242
+ d ? Error("react-stack-top-frame") : I,
243
+ d ? j(t(e)) : Y
244
+ );
245
+ }, h.jsxs = function(e, o, i) {
246
+ var d = 1e4 > _.recentlyCreatedOwnerStacks++;
247
+ return A(
248
+ e,
249
+ o,
250
+ i,
251
+ !0,
252
+ d ? Error("react-stack-top-frame") : I,
253
+ d ? j(t(e)) : Y
254
+ );
255
+ };
256
+ })()), h;
257
+ }
258
+ var B;
259
+ function ae() {
260
+ return B || (B = 1, process.env.NODE_ENV === "production" ? E.exports = se() : E.exports = oe()), E.exports;
261
+ }
262
+ var c = ae();
263
+ const ce = /^[0-9-]*$/, le = ({ accountNumber: r, setAccountNumber: s, className: a }) => {
264
+ const t = (n) => {
265
+ const l = n.target.value;
266
+ if (l == "") {
267
+ s("");
268
+ return;
269
+ }
270
+ if (l.startsWith("-")) {
271
+ alert("계좌번호는 숫자로 시작해야 합니다.");
272
+ return;
273
+ }
274
+ ce.test(l) ? s(l) : alert("계좌번호는 숫자와 -로만 입력 가능합니다.");
275
+ };
276
+ return /* @__PURE__ */ c.jsx(c.Fragment, { children: /* @__PURE__ */ c.jsx("input", { type: "text", inputMode: "tel", placeholder: "계좌번호", value: r, onChange: t, className: `rounded-sm border border-black w-full max-w-xl p-3 my-3 block justify-self-center ${a}` }) });
277
+ }, ie = ({ children: r, className: s }) => /* @__PURE__ */ c.jsx("div", { className: `bg-white flex justify-center ${s}`, children: /* @__PURE__ */ c.jsx(
278
+ "div",
279
+ {
280
+ className: `\r
281
+ w-full max-w-[430px]\r
282
+ px-4\r
283
+ `,
284
+ children: r
285
+ }
286
+ ) }), ue = ({ className: r, accountBank: s, setAccountBank: a, bankList: t }) => {
287
+ const [n, l] = v(!1), m = ne(null);
288
+ return N(() => {
289
+ const u = (p) => {
290
+ m.current && !m.current.contains(p.target) && l(!1);
291
+ };
292
+ return document.addEventListener("mousedown", u), document.addEventListener("touchstart", u), () => {
293
+ document.removeEventListener("mousedown", u), document.removeEventListener("touchstart", u);
294
+ };
295
+ }, []), /* @__PURE__ */ c.jsxs("div", { ref: m, className: `relative w-full max-w-xl my-3 block justify-self-center ${r}`, children: [
296
+ /* @__PURE__ */ c.jsxs(
297
+ "button",
298
+ {
299
+ type: "button",
300
+ onClick: () => l(!n),
301
+ className: `flex items-center justify-between w-full p-3 border border-black ${n ? "rounded-t-sm" : "rounded-sm"} bg-white text-left`,
302
+ children: [
303
+ /* @__PURE__ */ c.jsx("span", { className: s === "" ? "text-gray-400" : "text-black", children: s ? t.find((u) => u.id === s)?.label : "은행" }),
304
+ /* @__PURE__ */ c.jsx("span", { className: `transition-transform duration-300 inline-block ${n ? "rotate-180" : ""}`, children: "▼" })
305
+ ]
306
+ }
307
+ ),
308
+ /* @__PURE__ */ c.jsx("ul", { className: `absolute z-10 w-full border-t-0 bg-white border border-black rounded-b-sm shadow-lg overflow-hidden transition-all duration-300 ease-in-out ${n ? "max-h-60 opacity-100 visible" : "max-h-0 opacity-0 invisible pointer-events-none"}`, children: t.map((u) => /* @__PURE__ */ c.jsx(
309
+ "li",
310
+ {
311
+ onClick: () => {
312
+ a(u.id), l(!1);
313
+ },
314
+ className: "p-3 hover:bg-gray-100 cursor-pointer text-black border-b last:border-b-0",
315
+ children: u.label
316
+ },
317
+ u.id
318
+ )) })
319
+ ] });
320
+ }, de = ({ label: r, code: s, color: a, setAccountBank: t, bankId: n }) => {
321
+ const l = () => {
322
+ t(n);
323
+ };
324
+ return /* @__PURE__ */ c.jsxs(
325
+ "button",
326
+ {
327
+ type: "button",
328
+ onClick: l,
329
+ className: `\r
330
+ rounded-xl px-3 py-2\r
331
+ flex items-center gap-2 text-left\r
332
+ bg-gray-100\r
333
+ transition-all duration-150 ease-out\r
334
+ hover:bg-gray-200 hover:-translate-y-[1px] hover:shadow-sm\r
335
+ focus-visible:ring-2 focus-visible:rnoneing-gray-400\r
336
+ active:scale-[0.98]\r
337
+ `,
338
+ children: [
339
+ /* @__PURE__ */ c.jsx(
340
+ "span",
341
+ {
342
+ className: "flex h-8 w-8 items-center justify-center rounded-lg text-white font-bold text-sm",
343
+ style: { backgroundColor: a },
344
+ children: s
345
+ }
346
+ ),
347
+ /* @__PURE__ */ c.jsx("span", { className: "text-sm font-medium", children: r })
348
+ ]
349
+ }
350
+ );
351
+ }, fe = ({ banks: r, setAccountBank: s, className: a }) => {
352
+ const t = r.map(
353
+ (n) => /* @__PURE__ */ c.jsx(de, { label: n.label, code: n.code, color: n.color, setAccountBank: s, bankId: n.id }, n.id)
354
+ );
355
+ return /* @__PURE__ */ c.jsx("div", { className: `flex gap-2 mt-3 justify-self-center max-w-xl ${a}`, children: t });
356
+ }, me = ({ bank: r, account: s, onClick: a }) => /* @__PURE__ */ c.jsxs(
357
+ "button",
358
+ {
359
+ type: "button",
360
+ className: `\r
361
+ flex w-full items-center justify-between\r
362
+ rounded-xl bg-gray-100 px-4 py-3\r
363
+ text-left text-sm\r
364
+ transition\r
365
+ hover:bg-gray-200\r
366
+ active:scale-[0.98]\r
367
+ `,
368
+ onClick: () => a(),
369
+ children: [
370
+ /* @__PURE__ */ c.jsxs("div", { className: "flex items-center gap-3", children: [
371
+ r && /* @__PURE__ */ c.jsx(
372
+ "div",
373
+ {
374
+ className: "flex h-8 w-8 items-center justify-center rounded-lg text-xs font-bold text-white",
375
+ style: { backgroundColor: r.color },
376
+ children: r.code
377
+ }
378
+ ),
379
+ /* @__PURE__ */ c.jsxs("div", { children: [
380
+ r && /* @__PURE__ */ c.jsx("div", { className: "text-sm font-medium text-gray-900", children: r.label }),
381
+ /* @__PURE__ */ c.jsx(
382
+ "div",
383
+ {
384
+ className: "text-sm 'text-gray-500",
385
+ children: s
386
+ }
387
+ )
388
+ ] })
389
+ ] }),
390
+ /* @__PURE__ */ c.jsx("span", { className: "text-xs text-gray-400", children: "입력" })
391
+ ]
392
+ }
393
+ ), pe = (r, s) => {
394
+ if (!r) return null;
395
+ const a = r.match(/[\d-]{8,}/);
396
+ if (!a) return null;
397
+ const t = a[0], n = s.find(
398
+ (l) => r.includes(l.label) || r.includes(l.code)
399
+ );
400
+ return n ? { account: t, bank: n } : { account: t };
401
+ }, be = [
402
+ {
403
+ id: "woori",
404
+ label: "우리",
405
+ code: "WR",
406
+ color: "#003a8f",
407
+ scoreRules: [
408
+ {
409
+ type: "length",
410
+ value: [11, 12, 13, 14],
411
+ score: 20,
412
+ reason: "통합 역사로 인해 다양한 계좌 길이"
413
+ },
414
+ {
415
+ type: "prefix",
416
+ match: [
417
+ "006",
418
+ "007",
419
+ "002",
420
+ "004",
421
+ "003",
422
+ "005",
423
+ "05",
424
+ "06",
425
+ "07",
426
+ "08",
427
+ "02",
428
+ "01",
429
+ "04",
430
+ "15",
431
+ "12",
432
+ "13",
433
+ "21",
434
+ "24",
435
+ "25",
436
+ "09"
437
+ ],
438
+ position: "start-2-or-3",
439
+ score: 25,
440
+ reason: "우리은행 및 구계좌 과목코드"
441
+ },
442
+ {
443
+ type: "virtualAccountHint",
444
+ length: 14,
445
+ score: 10,
446
+ reason: "가상계좌 비중"
447
+ }
448
+ ]
449
+ },
450
+ {
451
+ id: "toss",
452
+ label: "토스뱅크",
453
+ code: "TB",
454
+ color: "#2254F6",
455
+ scoreRules: [
456
+ {
457
+ type: "length",
458
+ value: 12,
459
+ score: 35,
460
+ reason: "토스 일반계좌는 12자리"
461
+ },
462
+ {
463
+ type: "length",
464
+ value: 14,
465
+ score: 25,
466
+ reason: "토스 가상계좌는 14자리"
467
+ },
468
+ {
469
+ type: "prefix",
470
+ match: ["100", "106", "200", "300", "150", "700", "190"],
471
+ position: "start-3",
472
+ score: 40,
473
+ reason: "토스 과목코드"
474
+ }
475
+ ]
476
+ },
477
+ {
478
+ id: "kakao",
479
+ label: "카카오뱅크",
480
+ code: "B",
481
+ color: "#FFE401",
482
+ scoreRules: [
483
+ {
484
+ type: "length",
485
+ value: 13,
486
+ score: 40,
487
+ reason: "카카오뱅크는 현행 계좌가 항상 13자리"
488
+ },
489
+ {
490
+ type: "prefix",
491
+ match: ["333", "388", "355", "310"],
492
+ position: "start-3",
493
+ score: 25,
494
+ reason: "일반계좌 과목코드"
495
+ },
496
+ {
497
+ type: "prefix",
498
+ match: ["777", "979"],
499
+ position: "start-3",
500
+ score: 20,
501
+ reason: "가상계좌 과목코드"
502
+ },
503
+ {
504
+ type: "firstDigit",
505
+ match: ["3", "7"],
506
+ score: 10,
507
+ reason: "업무구분은 3(일반) 또는 7(가상)"
508
+ }
509
+ ]
510
+ }
511
+ ], xe = ({ setAccountNumber: r, setAccountBank: s, className: a }) => {
512
+ const [t, n] = v(null);
513
+ N(() => {
514
+ const m = async () => {
515
+ try {
516
+ const u = await navigator.clipboard.readText(), p = pe(u, be);
517
+ p && n(p);
518
+ } catch {
519
+ console.log("Clipboard access failed");
520
+ }
521
+ };
522
+ m(), window.addEventListener("focus", m);
523
+ }, []);
524
+ const l = () => {
525
+ if (t)
526
+ "bank" in t ? (r(t.account), s(t.bank.id)) : r(t.account);
527
+ else return;
528
+ };
529
+ return t != null && /* @__PURE__ */ c.jsxs("section", { className: `justify-self-center mt-6 w-full max-w-xl ${a}`, children: [
530
+ /* @__PURE__ */ c.jsx("h3", { className: "mb-2 text-sm font-semibold text-gray-700", children: "복사한 계좌로 송금" }),
531
+ /* @__PURE__ */ c.jsx(
532
+ me,
533
+ {
534
+ bank: t.bank,
535
+ account: t.account,
536
+ onClick: l
537
+ }
538
+ )
539
+ ] });
540
+ }, he = (r, s) => {
541
+ switch (r.type) {
542
+ case "length":
543
+ return Array.isArray(r.value) ? r.value.includes(s.length) ? r.score : 0 : s.length === r.value ? r.score : 0;
544
+ case "prefix": {
545
+ const a = r.match || [];
546
+ return r.position === "start-3" ? a.some((t) => s.startsWith(t)) ? r.score : 0 : r.position === "start-2-or-3" && a.some(
547
+ (t) => s.startsWith(t) || s.startsWith(t.slice(0, 2))
548
+ ) ? r.score : 0;
549
+ }
550
+ case "firstDigit":
551
+ return r.match.includes(s[0]) ? r.score : 0;
552
+ case "virtualAccountHint":
553
+ return s.length === r.length ? r.score : 0;
554
+ default:
555
+ return 0;
556
+ }
557
+ }, ve = (r, s) => {
558
+ const a = r.replace(/-/g, "");
559
+ return s.map((t) => {
560
+ let n = 0;
561
+ return t.scoreRules.forEach((l) => {
562
+ n += he(l, a);
563
+ }), {
564
+ ...t,
565
+ score: n
566
+ };
567
+ }).filter((t) => t.score > 0).sort((t, n) => n.score - t.score);
568
+ }, ge = (r, s) => {
569
+ const [a, t] = v([]);
570
+ return N(() => {
571
+ if (r.length < 10) {
572
+ t([]);
573
+ return;
574
+ }
575
+ const n = ve(r, s);
576
+ t(n.slice(0, 3));
577
+ }, [r, s]), a;
578
+ }, Ee = ({ accountState: r, banks: s, className: a }) => {
579
+ const {
580
+ accountNumber: t,
581
+ setAccountNumber: n,
582
+ accountBank: l,
583
+ setAccountBank: m,
584
+ numericAccountNumber: u
585
+ } = r, p = ge(u, s);
586
+ return /* @__PURE__ */ c.jsxs(ie, { className: `${a}`, children: [
587
+ /* @__PURE__ */ c.jsx(le, { accountNumber: t, setAccountNumber: n }),
588
+ /* @__PURE__ */ c.jsx(ue, { accountBank: l, setAccountBank: m, bankList: s }),
589
+ p.length > 0 && /* @__PURE__ */ c.jsx(fe, { banks: p, setAccountBank: m }),
590
+ /* @__PURE__ */ c.jsx(xe, { setAccountNumber: n, setAccountBank: m })
591
+ ] });
592
+ }, Re = () => {
593
+ const [r, s] = v(""), [a, t] = v(""), n = r.replace(/-/g, "");
594
+ return {
595
+ accountNumber: r,
596
+ setAccountNumber: s,
597
+ accountBank: a,
598
+ setAccountBank: t,
599
+ numericAccountNumber: n
600
+ };
601
+ };
602
+ export {
603
+ Ee as AccountForm,
604
+ be as banks,
605
+ Re as useAccountFormState
606
+ };
@@ -0,0 +1,25 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.appendChild(document.createTextNode(":root{--color-primary-pink: #FF9E9E;--color-primary-mint: #4CD9C0;--color-bg-gray: #F5F6F8;--color-text-main: #333333;--color-text-sub: #888888;--color-white: #FFFFFF;--app-max-width: 420px}*{box-sizing:border-box;margin:0;padding:0}body{font-family:Noto Sans KR,sans-serif;background-color:#e0e0e0;display:flex;justify-content:center;min-height:100vh}#root{width:100%;max-width:var(--app-max-width);background-color:var(--color-white);min-height:100vh;box-shadow:0 0 20px #0000001a;position:relative;overflow-x:hidden}.flex-center{display:flex;justify-content:center;align-items:center}button{cursor:pointer;border:none;background:none;font-family:inherit}")),document.head.appendChild(o)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
2
+ (function(b,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],p):(b=typeof globalThis<"u"?globalThis:b||self,p(b["@woori-fisa/acccount-form"]={},b.React))})(this,(function(b,p){"use strict";var g={exports:{}},v={};var N;function z(){if(N)return v;N=1;var t=Symbol.for("react.transitional.element"),s=Symbol.for("react.fragment");function c(r,n,l){var m=null;if(l!==void 0&&(m=""+l),n.key!==void 0&&(m=""+n.key),"key"in n){l={};for(var u in n)u!=="key"&&(l[u]=n[u])}else l=n;return n=l.ref,{$$typeof:t,type:r,key:m,ref:n!==void 0?n:null,props:l}}return v.Fragment=s,v.jsx=c,v.jsxs=c,v}var y={};var S;function V(){return S||(S=1,process.env.NODE_ENV!=="production"&&(function(){function t(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===he?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case k:return"Fragment";case ie:return"Profiler";case le:return"StrictMode";case me:return"Suspense";case pe:return"SuspenseList";case xe:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case ce:return"Portal";case fe:return e.displayName||"Context";case ue:return(e._context.displayName||"Context")+".Consumer";case de:var o=e.render;return e=e.displayName,e||(e=o.displayName||o.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case be:return o=e.displayName||null,o!==null?o:t(e.type)||"Memo";case _:o=e._payload,e=e._init;try{return t(e(o))}catch{}}return null}function s(e){return""+e}function c(e){try{s(e);var o=!1}catch{o=!0}if(o){o=console;var i=o.error,f=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return i.call(o,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",f),s(e)}}function r(e){if(e===k)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===_)return"<...>";try{var o=t(e);return o?"<"+o+">":"<...>"}catch{return"<...>"}}function n(){var e=j.A;return e===null?null:e.getOwner()}function l(){return Error("react-stack-top-frame")}function m(e){if(L.call(e,"key")){var o=Object.getOwnPropertyDescriptor(e,"key").get;if(o&&o.isReactWarning)return!1}return e.key!==void 0}function u(e,o){function i(){D||(D=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",o))}i.isReactWarning=!0,Object.defineProperty(e,"key",{get:i,configurable:!0})}function x(){var e=t(this.type);return W[e]||(W[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function ae(e,o,i,f,R,T){var d=i.ref;return e={$$typeof:Y,type:e,key:o,props:i,_owner:f},(d!==void 0?d:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:x}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:R}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:T}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function C(e,o,i,f,R,T){var d=o.children;if(d!==void 0)if(f)if(ve(d)){for(f=0;f<d.length;f++)F(d[f]);Object.freeze&&Object.freeze(d)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else F(d);if(L.call(o,"key")){d=t(e);var h=Object.keys(o).filter(function(ye){return ye!=="key"});f=0<h.length?"{key: someKey, "+h.join(": ..., ")+": ...}":"{key: someKey}",U[d+f]||(h=0<h.length?"{"+h.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
3
+ let props = %s;
4
+ <%s {...props} />
5
+ React keys must be passed directly to JSX without using spread:
6
+ let props = %s;
7
+ <%s key={someKey} {...props} />`,f,d,h,d),U[d+f]=!0)}if(d=null,i!==void 0&&(c(i),d=""+i),m(o)&&(c(o.key),d=""+o.key),"key"in o){i={};for(var A in o)A!=="key"&&(i[A]=o[A])}else i=o;return d&&u(i,typeof e=="function"?e.displayName||e.name||"Unknown":e),ae(e,d,i,n(),R,T)}function F(e){I(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===_&&(e._payload.status==="fulfilled"?I(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function I(e){return typeof e=="object"&&e!==null&&e.$$typeof===Y}var E=p,Y=Symbol.for("react.transitional.element"),ce=Symbol.for("react.portal"),k=Symbol.for("react.fragment"),le=Symbol.for("react.strict_mode"),ie=Symbol.for("react.profiler"),ue=Symbol.for("react.consumer"),fe=Symbol.for("react.context"),de=Symbol.for("react.forward_ref"),me=Symbol.for("react.suspense"),pe=Symbol.for("react.suspense_list"),be=Symbol.for("react.memo"),_=Symbol.for("react.lazy"),xe=Symbol.for("react.activity"),he=Symbol.for("react.client.reference"),j=E.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,L=Object.prototype.hasOwnProperty,ve=Array.isArray,w=console.createTask?console.createTask:function(){return null};E={react_stack_bottom_frame:function(e){return e()}};var D,W={},B=E.react_stack_bottom_frame.bind(E,l)(),M=w(r(l)),U={};y.Fragment=k,y.jsx=function(e,o,i){var f=1e4>j.recentlyCreatedOwnerStacks++;return C(e,o,i,!1,f?Error("react-stack-top-frame"):B,f?w(r(e)):M)},y.jsxs=function(e,o,i){var f=1e4>j.recentlyCreatedOwnerStacks++;return C(e,o,i,!0,f?Error("react-stack-top-frame"):B,f?w(r(e)):M)}})()),y}var O;function J(){return O||(O=1,process.env.NODE_ENV==="production"?g.exports=z():g.exports=V()),g.exports}var a=J();const $=/^[0-9-]*$/,G=({accountNumber:t,setAccountNumber:s,className:c})=>{const r=n=>{const l=n.target.value;if(l==""){s("");return}if(l.startsWith("-")){alert("계좌번호는 숫자로 시작해야 합니다.");return}$.test(l)?s(l):alert("계좌번호는 숫자와 -로만 입력 가능합니다.")};return a.jsx(a.Fragment,{children:a.jsx("input",{type:"text",inputMode:"tel",placeholder:"계좌번호",value:t,onChange:r,className:`rounded-sm border border-black w-full max-w-xl p-3 my-3 block justify-self-center ${c}`})})},H=({children:t,className:s})=>a.jsx("div",{className:`bg-white flex justify-center ${s}`,children:a.jsx("div",{className:`\r
8
+ w-full max-w-[430px]\r
9
+ px-4\r
10
+ `,children:t})}),X=({className:t,accountBank:s,setAccountBank:c,bankList:r})=>{const[n,l]=p.useState(!1),m=p.useRef(null);return p.useEffect(()=>{const u=x=>{m.current&&!m.current.contains(x.target)&&l(!1)};return document.addEventListener("mousedown",u),document.addEventListener("touchstart",u),()=>{document.removeEventListener("mousedown",u),document.removeEventListener("touchstart",u)}},[]),a.jsxs("div",{ref:m,className:`relative w-full max-w-xl my-3 block justify-self-center ${t}`,children:[a.jsxs("button",{type:"button",onClick:()=>l(!n),className:`flex items-center justify-between w-full p-3 border border-black ${n?"rounded-t-sm":"rounded-sm"} bg-white text-left`,children:[a.jsx("span",{className:s===""?"text-gray-400":"text-black",children:s?r.find(u=>u.id===s)?.label:"은행"}),a.jsx("span",{className:`transition-transform duration-300 inline-block ${n?"rotate-180":""}`,children:"▼"})]}),a.jsx("ul",{className:`absolute z-10 w-full border-t-0 bg-white border border-black rounded-b-sm shadow-lg overflow-hidden transition-all duration-300 ease-in-out ${n?"max-h-60 opacity-100 visible":"max-h-0 opacity-0 invisible pointer-events-none"}`,children:r.map(u=>a.jsx("li",{onClick:()=>{c(u.id),l(!1)},className:"p-3 hover:bg-gray-100 cursor-pointer text-black border-b last:border-b-0",children:u.label},u.id))})]})},Z=({label:t,code:s,color:c,setAccountBank:r,bankId:n})=>{const l=()=>{r(n)};return a.jsxs("button",{type:"button",onClick:l,className:`\r
11
+ rounded-xl px-3 py-2\r
12
+ flex items-center gap-2 text-left\r
13
+ bg-gray-100\r
14
+ transition-all duration-150 ease-out\r
15
+ hover:bg-gray-200 hover:-translate-y-[1px] hover:shadow-sm\r
16
+ focus-visible:ring-2 focus-visible:rnoneing-gray-400\r
17
+ active:scale-[0.98]\r
18
+ `,children:[a.jsx("span",{className:"flex h-8 w-8 items-center justify-center rounded-lg text-white font-bold text-sm",style:{backgroundColor:c},children:s}),a.jsx("span",{className:"text-sm font-medium",children:t})]})},Q=({banks:t,setAccountBank:s,className:c})=>{const r=t.map(n=>a.jsx(Z,{label:n.label,code:n.code,color:n.color,setAccountBank:s,bankId:n.id},n.id));return a.jsx("div",{className:`flex gap-2 mt-3 justify-self-center max-w-xl ${c}`,children:r})},q=({bank:t,account:s,onClick:c})=>a.jsxs("button",{type:"button",className:`\r
19
+ flex w-full items-center justify-between\r
20
+ rounded-xl bg-gray-100 px-4 py-3\r
21
+ text-left text-sm\r
22
+ transition\r
23
+ hover:bg-gray-200\r
24
+ active:scale-[0.98]\r
25
+ `,onClick:()=>c(),children:[a.jsxs("div",{className:"flex items-center gap-3",children:[t&&a.jsx("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg text-xs font-bold text-white",style:{backgroundColor:t.color},children:t.code}),a.jsxs("div",{children:[t&&a.jsx("div",{className:"text-sm font-medium text-gray-900",children:t.label}),a.jsx("div",{className:"text-sm 'text-gray-500",children:s})]})]}),a.jsx("span",{className:"text-xs text-gray-400",children:"입력"})]}),K=(t,s)=>{if(!t)return null;const c=t.match(/[\d-]{8,}/);if(!c)return null;const r=c[0],n=s.find(l=>t.includes(l.label)||t.includes(l.code));return n?{account:r,bank:n}:{account:r}},P=[{id:"woori",label:"우리",code:"WR",color:"#003a8f",scoreRules:[{type:"length",value:[11,12,13,14],score:20,reason:"통합 역사로 인해 다양한 계좌 길이"},{type:"prefix",match:["006","007","002","004","003","005","05","06","07","08","02","01","04","15","12","13","21","24","25","09"],position:"start-2-or-3",score:25,reason:"우리은행 및 구계좌 과목코드"},{type:"virtualAccountHint",length:14,score:10,reason:"가상계좌 비중"}]},{id:"toss",label:"토스뱅크",code:"TB",color:"#2254F6",scoreRules:[{type:"length",value:12,score:35,reason:"토스 일반계좌는 12자리"},{type:"length",value:14,score:25,reason:"토스 가상계좌는 14자리"},{type:"prefix",match:["100","106","200","300","150","700","190"],position:"start-3",score:40,reason:"토스 과목코드"}]},{id:"kakao",label:"카카오뱅크",code:"B",color:"#FFE401",scoreRules:[{type:"length",value:13,score:40,reason:"카카오뱅크는 현행 계좌가 항상 13자리"},{type:"prefix",match:["333","388","355","310"],position:"start-3",score:25,reason:"일반계좌 과목코드"},{type:"prefix",match:["777","979"],position:"start-3",score:20,reason:"가상계좌 과목코드"},{type:"firstDigit",match:["3","7"],score:10,reason:"업무구분은 3(일반) 또는 7(가상)"}]}],ee=({setAccountNumber:t,setAccountBank:s,className:c})=>{const[r,n]=p.useState(null);p.useEffect(()=>{const m=async()=>{try{const u=await navigator.clipboard.readText(),x=K(u,P);x&&n(x)}catch{console.log("Clipboard access failed")}};m(),window.addEventListener("focus",m)},[]);const l=()=>{if(r)"bank"in r?(t(r.account),s(r.bank.id)):t(r.account);else return};return r!=null&&a.jsxs("section",{className:`justify-self-center mt-6 w-full max-w-xl ${c}`,children:[a.jsx("h3",{className:"mb-2 text-sm font-semibold text-gray-700",children:"복사한 계좌로 송금"}),a.jsx(q,{bank:r.bank,account:r.account,onClick:l})]})},te=(t,s)=>{switch(t.type){case"length":return Array.isArray(t.value)?t.value.includes(s.length)?t.score:0:s.length===t.value?t.score:0;case"prefix":{const c=t.match||[];return t.position==="start-3"?c.some(r=>s.startsWith(r))?t.score:0:t.position==="start-2-or-3"&&c.some(r=>s.startsWith(r)||s.startsWith(r.slice(0,2)))?t.score:0}case"firstDigit":return t.match.includes(s[0])?t.score:0;case"virtualAccountHint":return s.length===t.length?t.score:0;default:return 0}},re=(t,s)=>{const c=t.replace(/-/g,"");return s.map(r=>{let n=0;return r.scoreRules.forEach(l=>{n+=te(l,c)}),{...r,score:n}}).filter(r=>r.score>0).sort((r,n)=>n.score-r.score)},ne=(t,s)=>{const[c,r]=p.useState([]);return p.useEffect(()=>{if(t.length<10){r([]);return}const n=re(t,s);r(n.slice(0,3))},[t,s]),c},se=({accountState:t,banks:s,className:c})=>{const{accountNumber:r,setAccountNumber:n,accountBank:l,setAccountBank:m,numericAccountNumber:u}=t,x=ne(u,s);return a.jsxs(H,{className:`${c}`,children:[a.jsx(G,{accountNumber:r,setAccountNumber:n}),a.jsx(X,{accountBank:l,setAccountBank:m,bankList:s}),x.length>0&&a.jsx(Q,{banks:x,setAccountBank:m}),a.jsx(ee,{setAccountNumber:n,setAccountBank:m})]})},oe=()=>{const[t,s]=p.useState(""),[c,r]=p.useState(""),n=t.replace(/-/g,"");return{accountNumber:t,setAccountNumber:s,accountBank:c,setAccountBank:r,numericAccountNumber:n}};b.AccountForm=se,b.banks=P,b.useAccountFormState=oe,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
package/dist/vite.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "woori-fisa-account-form",
3
+ "private": false,
4
+ "version": "1.0.7",
5
+ "type": "module",
6
+ "main": "./dist/index.umd.js",
7
+ "module": "./dist/index.es.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.es.js",
11
+ "require": "./dist/index.umd.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "dev": "vite",
19
+ "build": "vite build",
20
+ "lint": "eslint .",
21
+ "preview": "vite preview"
22
+ },
23
+ "dependencies": {
24
+ "@tailwindcss/vite": "^4.1.18",
25
+ "react": "^19.2.0",
26
+ "react-dom": "^19.2.0",
27
+ "tailwindcss": "^4.1.18"
28
+ },
29
+ "devDependencies": {
30
+ "@eslint/js": "^9.39.1",
31
+ "@tailwindcss/postcss": "^4.1.18",
32
+ "@types/react": "^19.2.5",
33
+ "@types/react-dom": "^19.2.3",
34
+ "@vitejs/plugin-react": "^5.1.1",
35
+ "eslint": "^9.39.1",
36
+ "eslint-plugin-react-hooks": "^7.0.1",
37
+ "eslint-plugin-react-refresh": "^0.4.24",
38
+ "globals": "^16.5.0",
39
+ "vite": "^7.2.4",
40
+ "vite-plugin-css-injected-by-js": "^3.5.2"
41
+ }
42
+ }