slick-address-kr 1.0.4 → 2.0.0

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.
@@ -0,0 +1,26 @@
1
+ import * as react from 'react';
2
+ import { d as AddressFinderOptions, e as AddressResult, K as KoreanAddressFinder } from '../address-finder-DIU0lay6.cjs';
3
+ export { f as AddressSelectMeta } from '../address-finder-DIU0lay6.cjs';
4
+
5
+ interface UseKoreanAddressResult {
6
+ /** 검색 UI가 그려질 요소에 연결한다 */
7
+ containerRef: (node: HTMLElement | null) => void;
8
+ /** 현재 선택된 주소. 상세주소 입력도 반영된다 */
9
+ address: AddressResult | null;
10
+ /** 필요할 때 직접 조작하기 위한 인스턴스 */
11
+ finder: KoreanAddressFinder | null;
12
+ }
13
+ /**
14
+ * KoreanAddressFinder를 React 생명주기에 맞춰 붙인다.
15
+ * 로직은 코어에 그대로 두고 마운트·언마운트와 상태 노출만 담당한다.
16
+ */
17
+ declare function useKoreanAddress(options?: AddressFinderOptions): UseKoreanAddressResult;
18
+ interface KoreanAddressFinderViewProps extends AddressFinderOptions {
19
+ className?: string;
20
+ }
21
+ /**
22
+ * 훅을 직접 다루지 않아도 되는 기본 컴포넌트.
23
+ */
24
+ declare function KoreanAddressFinderView({ className, ...options }: KoreanAddressFinderViewProps): react.JSX.Element;
25
+
26
+ export { AddressFinderOptions, AddressResult, KoreanAddressFinderView, type KoreanAddressFinderViewProps, type UseKoreanAddressResult, useKoreanAddress };
@@ -0,0 +1,26 @@
1
+ import * as react from 'react';
2
+ import { d as AddressFinderOptions, e as AddressResult, K as KoreanAddressFinder } from '../address-finder-DIU0lay6.js';
3
+ export { f as AddressSelectMeta } from '../address-finder-DIU0lay6.js';
4
+
5
+ interface UseKoreanAddressResult {
6
+ /** 검색 UI가 그려질 요소에 연결한다 */
7
+ containerRef: (node: HTMLElement | null) => void;
8
+ /** 현재 선택된 주소. 상세주소 입력도 반영된다 */
9
+ address: AddressResult | null;
10
+ /** 필요할 때 직접 조작하기 위한 인스턴스 */
11
+ finder: KoreanAddressFinder | null;
12
+ }
13
+ /**
14
+ * KoreanAddressFinder를 React 생명주기에 맞춰 붙인다.
15
+ * 로직은 코어에 그대로 두고 마운트·언마운트와 상태 노출만 담당한다.
16
+ */
17
+ declare function useKoreanAddress(options?: AddressFinderOptions): UseKoreanAddressResult;
18
+ interface KoreanAddressFinderViewProps extends AddressFinderOptions {
19
+ className?: string;
20
+ }
21
+ /**
22
+ * 훅을 직접 다루지 않아도 되는 기본 컴포넌트.
23
+ */
24
+ declare function KoreanAddressFinderView({ className, ...options }: KoreanAddressFinderViewProps): react.JSX.Element;
25
+
26
+ export { AddressFinderOptions, AddressResult, KoreanAddressFinderView, type KoreanAddressFinderViewProps, type UseKoreanAddressResult, useKoreanAddress };
@@ -0,0 +1,51 @@
1
+ import {
2
+ KoreanAddressFinder
3
+ } from "../chunk-2OLEGOFJ.js";
4
+
5
+ // src/react/index.tsx
6
+ import { useCallback, useEffect, useRef, useState } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ function useKoreanAddress(options = {}) {
9
+ const [address, setAddress] = useState(null);
10
+ const [finder, setFinder] = useState(null);
11
+ const optionsRef = useRef(options);
12
+ optionsRef.current = options;
13
+ const instanceRef = useRef(null);
14
+ const containerRef = useCallback((node) => {
15
+ if (instanceRef.current) {
16
+ instanceRef.current.destroy();
17
+ instanceRef.current = null;
18
+ setFinder(null);
19
+ }
20
+ if (!node) return;
21
+ const instance = new KoreanAddressFinder({
22
+ ...optionsRef.current,
23
+ onSelect: (selected, meta) => {
24
+ setAddress(selected);
25
+ optionsRef.current.onSelect?.(selected, meta);
26
+ }
27
+ });
28
+ instance.init(node);
29
+ instanceRef.current = instance;
30
+ setFinder(instance);
31
+ }, []);
32
+ useEffect(() => {
33
+ return () => {
34
+ instanceRef.current?.destroy();
35
+ instanceRef.current = null;
36
+ };
37
+ }, []);
38
+ return { containerRef, address, finder };
39
+ }
40
+ function KoreanAddressFinderView({
41
+ className,
42
+ ...options
43
+ }) {
44
+ const { containerRef } = useKoreanAddress(options);
45
+ return /* @__PURE__ */ jsx("div", { className, ref: containerRef });
46
+ }
47
+ export {
48
+ KoreanAddressFinderView,
49
+ useKoreanAddress
50
+ };
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/index.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { KoreanAddressFinder } from '../address-finder';\nimport type {\n AddressFinderOptions,\n AddressResult,\n AddressSelectMeta,\n} from '../types';\n\nexport interface UseKoreanAddressResult {\n /** 검색 UI가 그려질 요소에 연결한다 */\n containerRef: (node: HTMLElement | null) => void;\n /** 현재 선택된 주소. 상세주소 입력도 반영된다 */\n address: AddressResult | null;\n /** 필요할 때 직접 조작하기 위한 인스턴스 */\n finder: KoreanAddressFinder | null;\n}\n\n/**\n * KoreanAddressFinder를 React 생명주기에 맞춰 붙인다.\n * 로직은 코어에 그대로 두고 마운트·언마운트와 상태 노출만 담당한다.\n */\nexport function useKoreanAddress(\n options: AddressFinderOptions = {}\n): UseKoreanAddressResult {\n const [address, setAddress] = useState<AddressResult | null>(null);\n const [finder, setFinder] = useState<KoreanAddressFinder | null>(null);\n\n // 옵션이 매 렌더 새 객체로 와도 재마운트하지 않도록 최신 값만 참조한다\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // finder 상태에만 의존하면 ref가 null로 분리될 때(요소는 사라지고\n // 컴포넌트는 유지되는 흔한 조건부 렌더 패턴) state 갱신 타이밍에\n // 기대지 않고는 정리할 방법이 없다. 그래서 살아있는 인스턴스를\n // ref로도 따로 들고, 콜백 ref 안에서 직접 파괴한다.\n const instanceRef = useRef<KoreanAddressFinder | null>(null);\n\n const containerRef = useCallback((node: HTMLElement | null) => {\n // 이전 인스턴스를 먼저 정리한다. ref가 null로 분리될 때도 여기로 들어온다.\n if (instanceRef.current) {\n instanceRef.current.destroy();\n instanceRef.current = null;\n setFinder(null);\n }\n\n if (!node) return;\n\n const instance = new KoreanAddressFinder({\n ...optionsRef.current,\n onSelect: (selected: AddressResult, meta: AddressSelectMeta) => {\n setAddress(selected);\n optionsRef.current.onSelect?.(selected, meta);\n },\n });\n instance.init(node);\n instanceRef.current = instance;\n setFinder(instance);\n }, []);\n\n // 컴포넌트 언마운트 시의 마지막 방어선.\n useEffect(() => {\n return () => {\n instanceRef.current?.destroy();\n instanceRef.current = null;\n };\n }, []);\n\n return { containerRef, address, finder };\n}\n\nexport interface KoreanAddressFinderViewProps extends AddressFinderOptions {\n className?: string;\n}\n\n/**\n * 훅을 직접 다루지 않아도 되는 기본 컴포넌트.\n */\nexport function KoreanAddressFinderView({\n className,\n ...options\n}: KoreanAddressFinderViewProps) {\n const { containerRef } = useKoreanAddress(options);\n return <div className={className} ref={containerRef} />;\n}\n\nexport type { AddressResult, AddressSelectMeta, AddressFinderOptions };\n"],"mappings":";;;;;AAAA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AAkFhD;AA7DF,SAAS,iBACd,UAAgC,CAAC,GACT;AACxB,QAAM,CAAC,SAAS,UAAU,IAAI,SAA+B,IAAI;AACjE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAqC,IAAI;AAGrE,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAMrB,QAAM,cAAc,OAAmC,IAAI;AAE3D,QAAM,eAAe,YAAY,CAAC,SAA6B;AAE7D,QAAI,YAAY,SAAS;AACvB,kBAAY,QAAQ,QAAQ;AAC5B,kBAAY,UAAU;AACtB,gBAAU,IAAI;AAAA,IAChB;AAEA,QAAI,CAAC,KAAM;AAEX,UAAM,WAAW,IAAI,oBAAoB;AAAA,MACvC,GAAG,WAAW;AAAA,MACd,UAAU,CAAC,UAAyB,SAA4B;AAC9D,mBAAW,QAAQ;AACnB,mBAAW,QAAQ,WAAW,UAAU,IAAI;AAAA,MAC9C;AAAA,IACF,CAAC;AACD,aAAS,KAAK,IAAI;AAClB,gBAAY,UAAU;AACtB,cAAU,QAAQ;AAAA,EACpB,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,WAAO,MAAM;AACX,kBAAY,SAAS,QAAQ;AAC7B,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,cAAc,SAAS,OAAO;AACzC;AASO,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA,GAAG;AACL,GAAiC;AAC/B,QAAM,EAAE,aAAa,IAAI,iBAAiB,OAAO;AACjD,SAAO,oBAAC,SAAI,WAAsB,KAAK,cAAc;AACvD;","names":[]}
package/dist/styles.css CHANGED
@@ -4,6 +4,25 @@
4
4
  */
5
5
 
6
6
  .kaf-wrapper {
7
+ --kaf-accent: #4a90e2;
8
+ --kaf-accent-hover: #357abd;
9
+ --kaf-accent-active: #2a6299;
10
+ --kaf-accent-soft: #f0f8ff;
11
+ --kaf-bg: #ffffff;
12
+ --kaf-surface: #f8f9fa;
13
+ --kaf-surface-strong: #e0e0e0;
14
+ --kaf-text: #333333;
15
+ --kaf-text-muted: #666666;
16
+ --kaf-text-subtle: #999999;
17
+ --kaf-border: #e0e0e0;
18
+ --kaf-border-soft: #f0f0f0;
19
+ --kaf-error: #e74c3c;
20
+ --kaf-on-accent: #ffffff;
21
+ --kaf-scrollbar-track: #f1f1f1;
22
+ --kaf-scrollbar-thumb: #888888;
23
+ --kaf-scrollbar-thumb-hover: #555555;
24
+ --kaf-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
25
+
7
26
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
8
27
  'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
9
28
  sans-serif;
@@ -13,6 +32,7 @@
13
32
  max-width: 600px;
14
33
  margin: 0 auto;
15
34
  position: relative;
35
+ color: var(--kaf-text);
16
36
  }
17
37
 
18
38
  /* 검색 박스 */
@@ -26,26 +46,26 @@
26
46
  flex: 1;
27
47
  padding: 12px 16px;
28
48
  font-size: 14px;
29
- border: 2px solid #e0e0e0;
49
+ border: 2px solid var(--kaf-border);
30
50
  border-radius: 8px;
31
51
  outline: none;
32
52
  transition: border-color 0.2s;
33
53
  }
34
54
 
35
55
  .kaf-search-input:focus {
36
- border-color: #4a90e2;
56
+ border-color: var(--kaf-accent);
37
57
  }
38
58
 
39
59
  .kaf-search-input::placeholder {
40
- color: #999;
60
+ color: var(--kaf-text-subtle);
41
61
  }
42
62
 
43
63
  .kaf-search-btn {
44
64
  padding: 12px 24px;
45
65
  font-size: 14px;
46
66
  font-weight: 600;
47
- color: white;
48
- background-color: #4a90e2;
67
+ color: var(--kaf-on-accent);
68
+ background-color: var(--kaf-accent);
49
69
  border: none;
50
70
  border-radius: 8px;
51
71
  cursor: pointer;
@@ -54,11 +74,11 @@
54
74
  }
55
75
 
56
76
  .kaf-search-btn:hover {
57
- background-color: #357abd;
77
+ background-color: var(--kaf-accent-hover);
58
78
  }
59
79
 
60
80
  .kaf-search-btn:active {
61
- background-color: #2a6299;
81
+ background-color: var(--kaf-accent-active);
62
82
  }
63
83
 
64
84
  /* 검색 결과 컨테이너 */
@@ -67,10 +87,10 @@
67
87
  top: 100%;
68
88
  left: 0;
69
89
  right: 0;
70
- background: white;
71
- border: 1px solid #e0e0e0;
90
+ background: var(--kaf-bg);
91
+ border: 1px solid var(--kaf-border);
72
92
  border-radius: 8px;
73
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
93
+ box-shadow: var(--kaf-shadow);
74
94
  z-index: 1000;
75
95
  max-height: 400px;
76
96
  overflow: hidden;
@@ -83,21 +103,21 @@
83
103
  justify-content: space-between;
84
104
  align-items: center;
85
105
  padding: 12px 16px;
86
- border-bottom: 1px solid #e0e0e0;
87
- background-color: #f8f9fa;
106
+ border-bottom: 1px solid var(--kaf-border);
107
+ background-color: var(--kaf-surface);
88
108
  }
89
109
 
90
110
  .kaf-results-count {
91
111
  font-size: 14px;
92
112
  font-weight: 600;
93
- color: #333;
113
+ color: var(--kaf-text);
94
114
  }
95
115
 
96
116
  .kaf-close-btn {
97
117
  background: none;
98
118
  border: none;
99
119
  font-size: 24px;
100
- color: #666;
120
+ color: var(--kaf-text-muted);
101
121
  cursor: pointer;
102
122
  padding: 0;
103
123
  width: 28px;
@@ -110,7 +130,7 @@
110
130
  }
111
131
 
112
132
  .kaf-close-btn:hover {
113
- background-color: #e0e0e0;
133
+ background-color: var(--kaf-surface-strong);
114
134
  }
115
135
 
116
136
  .kaf-results-list {
@@ -121,13 +141,14 @@
121
141
  /* 검색 결과 항목 */
122
142
  .kaf-result-item {
123
143
  padding: 16px;
124
- border-bottom: 1px solid #f0f0f0;
144
+ border-bottom: 1px solid var(--kaf-border-soft);
125
145
  cursor: pointer;
126
146
  transition: background-color 0.2s;
127
147
  }
128
148
 
129
- .kaf-result-item:hover {
130
- background-color: #f8f9fa;
149
+ .kaf-result-item:hover,
150
+ .kaf-result-item--active {
151
+ background-color: var(--kaf-surface);
131
152
  }
132
153
 
133
154
  .kaf-result-item:last-child {
@@ -146,34 +167,34 @@
146
167
  padding: 2px 8px;
147
168
  font-size: 11px;
148
169
  font-weight: 600;
149
- color: white;
170
+ color: var(--kaf-on-accent);
150
171
  border-radius: 4px;
151
172
  flex-shrink: 0;
152
173
  }
153
174
 
154
175
  .kaf-result-main .kaf-result-type {
155
- background-color: #4a90e2;
176
+ background-color: var(--kaf-accent);
156
177
  }
157
178
 
158
179
  .kaf-result-sub .kaf-result-type {
159
- background-color: #999;
180
+ background-color: var(--kaf-text-subtle);
160
181
  }
161
182
 
162
183
  .kaf-result-address {
163
184
  font-size: 14px;
164
- color: #333;
185
+ color: var(--kaf-text);
165
186
  line-height: 1.5;
166
187
  }
167
188
 
168
189
  .kaf-result-zipcode {
169
190
  font-size: 12px;
170
- color: #666;
191
+ color: var(--kaf-text-muted);
171
192
  margin-top: 4px;
172
193
  }
173
194
 
174
195
  .kaf-result-building {
175
196
  font-size: 12px;
176
- color: #4a90e2;
197
+ color: var(--kaf-accent);
177
198
  margin-top: 4px;
178
199
  font-weight: 500;
179
200
  }
@@ -183,12 +204,12 @@
183
204
  .kaf-error {
184
205
  padding: 32px 16px;
185
206
  text-align: center;
186
- color: #999;
207
+ color: var(--kaf-text-subtle);
187
208
  font-size: 14px;
188
209
  }
189
210
 
190
211
  .kaf-error {
191
- color: #e74c3c;
212
+ color: var(--kaf-error);
192
213
  }
193
214
 
194
215
  /* 상세주소 입력 */
@@ -200,7 +221,7 @@
200
221
  width: 100%;
201
222
  padding: 12px 16px;
202
223
  font-size: 14px;
203
- border: 2px solid #e0e0e0;
224
+ border: 2px solid var(--kaf-border);
204
225
  border-radius: 8px;
205
226
  outline: none;
206
227
  transition: border-color 0.2s;
@@ -208,15 +229,15 @@
208
229
  }
209
230
 
210
231
  .kaf-detail-input:focus {
211
- border-color: #4a90e2;
232
+ border-color: var(--kaf-accent);
212
233
  }
213
234
 
214
235
  /* 선택된 주소 */
215
236
  .kaf-selected-address {
216
237
  margin-top: 12px;
217
238
  padding: 16px;
218
- background-color: #f0f8ff;
219
- border: 1px solid #4a90e2;
239
+ background-color: var(--kaf-accent-soft);
240
+ border: 1px solid var(--kaf-accent);
220
241
  border-radius: 8px;
221
242
  }
222
243
 
@@ -231,7 +252,7 @@
231
252
 
232
253
  .kaf-selected-main strong,
233
254
  .kaf-selected-sub strong {
234
- color: #4a90e2;
255
+ color: var(--kaf-accent);
235
256
  margin-right: 8px;
236
257
  }
237
258
 
@@ -241,16 +262,40 @@
241
262
  }
242
263
 
243
264
  .kaf-results-list::-webkit-scrollbar-track {
244
- background: #f1f1f1;
265
+ background: var(--kaf-scrollbar-track);
245
266
  }
246
267
 
247
268
  .kaf-results-list::-webkit-scrollbar-thumb {
248
- background: #888;
269
+ background: var(--kaf-scrollbar-thumb);
249
270
  border-radius: 4px;
250
271
  }
251
272
 
252
273
  .kaf-results-list::-webkit-scrollbar-thumb:hover {
253
- background: #555;
274
+ background: var(--kaf-scrollbar-thumb-hover);
275
+ }
276
+
277
+ /* 다크모드 — 토큰만 덮어쓴다.
278
+ 명시적으로 밝은 테마를 지정한 경우(.kaf-wrapper--light)는 제외한다. */
279
+ @media (prefers-color-scheme: dark) {
280
+ .kaf-wrapper:not(.kaf-wrapper--light) {
281
+ --kaf-accent: #6aa9ea;
282
+ --kaf-accent-hover: #85bbf0;
283
+ --kaf-accent-active: #4a90e2;
284
+ --kaf-accent-soft: #1c2b3a;
285
+ --kaf-bg: #1e1e1e;
286
+ --kaf-surface: #262626;
287
+ --kaf-surface-strong: #3a3a3a;
288
+ --kaf-text: #e8e8e8;
289
+ --kaf-text-muted: #b0b0b0;
290
+ --kaf-text-subtle: #a0a0a0;
291
+ --kaf-border: #3a3a3a;
292
+ --kaf-border-soft: #2e2e2e;
293
+ --kaf-error: #ff6b5b;
294
+ --kaf-on-accent: #10161d;
295
+ --kaf-scrollbar-track: #262626;
296
+ --kaf-scrollbar-thumb: #555555;
297
+ --kaf-scrollbar-thumb-hover: #777777;
298
+ }
254
299
  }
255
300
 
256
301
  /* 반응형 */