@x-plat/design-system 0.5.45 → 0.5.47
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/components/Swiper/index.cjs +30 -5
- package/dist/components/Swiper/index.d.cts +2 -0
- package/dist/components/Swiper/index.d.ts +2 -0
- package/dist/components/Swiper/index.js +30 -5
- package/dist/components/index.cjs +30 -5
- package/dist/components/index.js +30 -5
- package/dist/index.cjs +30 -5
- package/dist/index.js +30 -5
- package/guidelines/AGENT_PROMPT.md +265 -0
- package/package.json +1 -1
|
@@ -66,6 +66,7 @@ var Swiper = (props) => {
|
|
|
66
66
|
spaceBetween = 16,
|
|
67
67
|
showProgress = false,
|
|
68
68
|
autoplayDelay = 3e3,
|
|
69
|
+
pauseOnInteraction = 0,
|
|
69
70
|
speed = 300,
|
|
70
71
|
slideBy = 1,
|
|
71
72
|
index: indexProp,
|
|
@@ -93,6 +94,8 @@ var Swiper = (props) => {
|
|
|
93
94
|
const startXRef = import_react.default.useRef(0);
|
|
94
95
|
const startTimeRef = import_react.default.useRef(0);
|
|
95
96
|
const autoplayTimerRef = import_react.default.useRef(null);
|
|
97
|
+
const resumeTimeoutRef = import_react.default.useRef(null);
|
|
98
|
+
const [paused, setPaused] = import_react.default.useState(false);
|
|
96
99
|
import_react.default.useEffect(() => {
|
|
97
100
|
const el = containerRef.current;
|
|
98
101
|
if (!el) return;
|
|
@@ -192,15 +195,36 @@ var Swiper = (props) => {
|
|
|
192
195
|
slideTo
|
|
193
196
|
}));
|
|
194
197
|
import_react.default.useEffect(() => {
|
|
195
|
-
if (!auto || !canSlide) return;
|
|
198
|
+
if (!auto || !canSlide || paused) return;
|
|
196
199
|
autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
|
|
197
200
|
return () => {
|
|
198
201
|
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
|
199
202
|
};
|
|
200
|
-
}, [auto, autoplayDelay, slideNext, canSlide]);
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
+
}, [auto, autoplayDelay, slideNext, canSlide, paused]);
|
|
204
|
+
const pauseForInteraction = () => {
|
|
205
|
+
setPaused(true);
|
|
206
|
+
if (resumeTimeoutRef.current) {
|
|
207
|
+
clearTimeout(resumeTimeoutRef.current);
|
|
208
|
+
resumeTimeoutRef.current = null;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const scheduleAutoplayResume = () => {
|
|
212
|
+
if (!auto) return;
|
|
213
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
214
|
+
if (pauseOnInteraction <= 0) {
|
|
215
|
+
setPaused(false);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
resumeTimeoutRef.current = setTimeout(() => {
|
|
219
|
+
setPaused(false);
|
|
220
|
+
resumeTimeoutRef.current = null;
|
|
221
|
+
}, pauseOnInteraction);
|
|
203
222
|
};
|
|
223
|
+
import_react.default.useEffect(() => {
|
|
224
|
+
return () => {
|
|
225
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
226
|
+
};
|
|
227
|
+
}, []);
|
|
204
228
|
const getClientX = (e) => {
|
|
205
229
|
if ("touches" in e) return e.touches[0]?.clientX ?? 0;
|
|
206
230
|
return e.clientX;
|
|
@@ -208,7 +232,7 @@ var Swiper = (props) => {
|
|
|
208
232
|
const handleDragStart = (e) => {
|
|
209
233
|
if (!canSlide) return;
|
|
210
234
|
if ("button" in e && e.button !== 0) return;
|
|
211
|
-
|
|
235
|
+
pauseForInteraction();
|
|
212
236
|
setIsDragging(true);
|
|
213
237
|
setAnimated(false);
|
|
214
238
|
startXRef.current = getClientX(e);
|
|
@@ -238,6 +262,7 @@ var Swiper = (props) => {
|
|
|
238
262
|
setAnimated(true);
|
|
239
263
|
}
|
|
240
264
|
setDragOffset(0);
|
|
265
|
+
scheduleAutoplayResume();
|
|
241
266
|
};
|
|
242
267
|
window.addEventListener("mousemove", handleMove);
|
|
243
268
|
window.addEventListener("mouseup", handleEnd);
|
|
@@ -25,6 +25,8 @@ interface SwiperProps {
|
|
|
25
25
|
showProgress?: boolean;
|
|
26
26
|
/** 오토플레이 딜레이 (ms) */
|
|
27
27
|
autoplayDelay?: number;
|
|
28
|
+
/** 사용자 인터랙션(드래그/탭) 후 autoplay 가 재개되기까지 대기 시간 (ms). 0 이면 즉시 재개. 기본 0 */
|
|
29
|
+
pauseOnInteraction?: number;
|
|
28
30
|
/** 슬라이드 전환 속도 (ms) */
|
|
29
31
|
speed?: number;
|
|
30
32
|
/** 한 번에 이동할 슬라이드 수 (기본: 1) */
|
|
@@ -25,6 +25,8 @@ interface SwiperProps {
|
|
|
25
25
|
showProgress?: boolean;
|
|
26
26
|
/** 오토플레이 딜레이 (ms) */
|
|
27
27
|
autoplayDelay?: number;
|
|
28
|
+
/** 사용자 인터랙션(드래그/탭) 후 autoplay 가 재개되기까지 대기 시간 (ms). 0 이면 즉시 재개. 기본 0 */
|
|
29
|
+
pauseOnInteraction?: number;
|
|
28
30
|
/** 슬라이드 전환 속도 (ms) */
|
|
29
31
|
speed?: number;
|
|
30
32
|
/** 한 번에 이동할 슬라이드 수 (기본: 1) */
|
|
@@ -30,6 +30,7 @@ var Swiper = (props) => {
|
|
|
30
30
|
spaceBetween = 16,
|
|
31
31
|
showProgress = false,
|
|
32
32
|
autoplayDelay = 3e3,
|
|
33
|
+
pauseOnInteraction = 0,
|
|
33
34
|
speed = 300,
|
|
34
35
|
slideBy = 1,
|
|
35
36
|
index: indexProp,
|
|
@@ -57,6 +58,8 @@ var Swiper = (props) => {
|
|
|
57
58
|
const startXRef = React.useRef(0);
|
|
58
59
|
const startTimeRef = React.useRef(0);
|
|
59
60
|
const autoplayTimerRef = React.useRef(null);
|
|
61
|
+
const resumeTimeoutRef = React.useRef(null);
|
|
62
|
+
const [paused, setPaused] = React.useState(false);
|
|
60
63
|
React.useEffect(() => {
|
|
61
64
|
const el = containerRef.current;
|
|
62
65
|
if (!el) return;
|
|
@@ -156,15 +159,36 @@ var Swiper = (props) => {
|
|
|
156
159
|
slideTo
|
|
157
160
|
}));
|
|
158
161
|
React.useEffect(() => {
|
|
159
|
-
if (!auto || !canSlide) return;
|
|
162
|
+
if (!auto || !canSlide || paused) return;
|
|
160
163
|
autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
|
|
161
164
|
return () => {
|
|
162
165
|
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
|
163
166
|
};
|
|
164
|
-
}, [auto, autoplayDelay, slideNext, canSlide]);
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
+
}, [auto, autoplayDelay, slideNext, canSlide, paused]);
|
|
168
|
+
const pauseForInteraction = () => {
|
|
169
|
+
setPaused(true);
|
|
170
|
+
if (resumeTimeoutRef.current) {
|
|
171
|
+
clearTimeout(resumeTimeoutRef.current);
|
|
172
|
+
resumeTimeoutRef.current = null;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const scheduleAutoplayResume = () => {
|
|
176
|
+
if (!auto) return;
|
|
177
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
178
|
+
if (pauseOnInteraction <= 0) {
|
|
179
|
+
setPaused(false);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
resumeTimeoutRef.current = setTimeout(() => {
|
|
183
|
+
setPaused(false);
|
|
184
|
+
resumeTimeoutRef.current = null;
|
|
185
|
+
}, pauseOnInteraction);
|
|
167
186
|
};
|
|
187
|
+
React.useEffect(() => {
|
|
188
|
+
return () => {
|
|
189
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
190
|
+
};
|
|
191
|
+
}, []);
|
|
168
192
|
const getClientX = (e) => {
|
|
169
193
|
if ("touches" in e) return e.touches[0]?.clientX ?? 0;
|
|
170
194
|
return e.clientX;
|
|
@@ -172,7 +196,7 @@ var Swiper = (props) => {
|
|
|
172
196
|
const handleDragStart = (e) => {
|
|
173
197
|
if (!canSlide) return;
|
|
174
198
|
if ("button" in e && e.button !== 0) return;
|
|
175
|
-
|
|
199
|
+
pauseForInteraction();
|
|
176
200
|
setIsDragging(true);
|
|
177
201
|
setAnimated(false);
|
|
178
202
|
startXRef.current = getClientX(e);
|
|
@@ -202,6 +226,7 @@ var Swiper = (props) => {
|
|
|
202
226
|
setAnimated(true);
|
|
203
227
|
}
|
|
204
228
|
setDragOffset(0);
|
|
229
|
+
scheduleAutoplayResume();
|
|
205
230
|
};
|
|
206
231
|
window.addEventListener("mousemove", handleMove);
|
|
207
232
|
window.addEventListener("mouseup", handleEnd);
|
|
@@ -4712,6 +4712,7 @@ var Swiper = (props) => {
|
|
|
4712
4712
|
spaceBetween = 16,
|
|
4713
4713
|
showProgress = false,
|
|
4714
4714
|
autoplayDelay = 3e3,
|
|
4715
|
+
pauseOnInteraction = 0,
|
|
4715
4716
|
speed = 300,
|
|
4716
4717
|
slideBy = 1,
|
|
4717
4718
|
index: indexProp,
|
|
@@ -4739,6 +4740,8 @@ var Swiper = (props) => {
|
|
|
4739
4740
|
const startXRef = import_react31.default.useRef(0);
|
|
4740
4741
|
const startTimeRef = import_react31.default.useRef(0);
|
|
4741
4742
|
const autoplayTimerRef = import_react31.default.useRef(null);
|
|
4743
|
+
const resumeTimeoutRef = import_react31.default.useRef(null);
|
|
4744
|
+
const [paused, setPaused] = import_react31.default.useState(false);
|
|
4742
4745
|
import_react31.default.useEffect(() => {
|
|
4743
4746
|
const el = containerRef.current;
|
|
4744
4747
|
if (!el) return;
|
|
@@ -4838,15 +4841,36 @@ var Swiper = (props) => {
|
|
|
4838
4841
|
slideTo
|
|
4839
4842
|
}));
|
|
4840
4843
|
import_react31.default.useEffect(() => {
|
|
4841
|
-
if (!auto || !canSlide) return;
|
|
4844
|
+
if (!auto || !canSlide || paused) return;
|
|
4842
4845
|
autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
|
|
4843
4846
|
return () => {
|
|
4844
4847
|
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
|
4845
4848
|
};
|
|
4846
|
-
}, [auto, autoplayDelay, slideNext, canSlide]);
|
|
4847
|
-
const
|
|
4848
|
-
|
|
4849
|
+
}, [auto, autoplayDelay, slideNext, canSlide, paused]);
|
|
4850
|
+
const pauseForInteraction = () => {
|
|
4851
|
+
setPaused(true);
|
|
4852
|
+
if (resumeTimeoutRef.current) {
|
|
4853
|
+
clearTimeout(resumeTimeoutRef.current);
|
|
4854
|
+
resumeTimeoutRef.current = null;
|
|
4855
|
+
}
|
|
4856
|
+
};
|
|
4857
|
+
const scheduleAutoplayResume = () => {
|
|
4858
|
+
if (!auto) return;
|
|
4859
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
4860
|
+
if (pauseOnInteraction <= 0) {
|
|
4861
|
+
setPaused(false);
|
|
4862
|
+
return;
|
|
4863
|
+
}
|
|
4864
|
+
resumeTimeoutRef.current = setTimeout(() => {
|
|
4865
|
+
setPaused(false);
|
|
4866
|
+
resumeTimeoutRef.current = null;
|
|
4867
|
+
}, pauseOnInteraction);
|
|
4849
4868
|
};
|
|
4869
|
+
import_react31.default.useEffect(() => {
|
|
4870
|
+
return () => {
|
|
4871
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
4872
|
+
};
|
|
4873
|
+
}, []);
|
|
4850
4874
|
const getClientX = (e) => {
|
|
4851
4875
|
if ("touches" in e) return e.touches[0]?.clientX ?? 0;
|
|
4852
4876
|
return e.clientX;
|
|
@@ -4854,7 +4878,7 @@ var Swiper = (props) => {
|
|
|
4854
4878
|
const handleDragStart = (e) => {
|
|
4855
4879
|
if (!canSlide) return;
|
|
4856
4880
|
if ("button" in e && e.button !== 0) return;
|
|
4857
|
-
|
|
4881
|
+
pauseForInteraction();
|
|
4858
4882
|
setIsDragging(true);
|
|
4859
4883
|
setAnimated(false);
|
|
4860
4884
|
startXRef.current = getClientX(e);
|
|
@@ -4884,6 +4908,7 @@ var Swiper = (props) => {
|
|
|
4884
4908
|
setAnimated(true);
|
|
4885
4909
|
}
|
|
4886
4910
|
setDragOffset(0);
|
|
4911
|
+
scheduleAutoplayResume();
|
|
4887
4912
|
};
|
|
4888
4913
|
window.addEventListener("mousemove", handleMove);
|
|
4889
4914
|
window.addEventListener("mouseup", handleEnd);
|
package/dist/components/index.js
CHANGED
|
@@ -4622,6 +4622,7 @@ var Swiper = (props) => {
|
|
|
4622
4622
|
spaceBetween = 16,
|
|
4623
4623
|
showProgress = false,
|
|
4624
4624
|
autoplayDelay = 3e3,
|
|
4625
|
+
pauseOnInteraction = 0,
|
|
4625
4626
|
speed = 300,
|
|
4626
4627
|
slideBy = 1,
|
|
4627
4628
|
index: indexProp,
|
|
@@ -4649,6 +4650,8 @@ var Swiper = (props) => {
|
|
|
4649
4650
|
const startXRef = React30.useRef(0);
|
|
4650
4651
|
const startTimeRef = React30.useRef(0);
|
|
4651
4652
|
const autoplayTimerRef = React30.useRef(null);
|
|
4653
|
+
const resumeTimeoutRef = React30.useRef(null);
|
|
4654
|
+
const [paused, setPaused] = React30.useState(false);
|
|
4652
4655
|
React30.useEffect(() => {
|
|
4653
4656
|
const el = containerRef.current;
|
|
4654
4657
|
if (!el) return;
|
|
@@ -4748,15 +4751,36 @@ var Swiper = (props) => {
|
|
|
4748
4751
|
slideTo
|
|
4749
4752
|
}));
|
|
4750
4753
|
React30.useEffect(() => {
|
|
4751
|
-
if (!auto || !canSlide) return;
|
|
4754
|
+
if (!auto || !canSlide || paused) return;
|
|
4752
4755
|
autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
|
|
4753
4756
|
return () => {
|
|
4754
4757
|
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
|
4755
4758
|
};
|
|
4756
|
-
}, [auto, autoplayDelay, slideNext, canSlide]);
|
|
4757
|
-
const
|
|
4758
|
-
|
|
4759
|
+
}, [auto, autoplayDelay, slideNext, canSlide, paused]);
|
|
4760
|
+
const pauseForInteraction = () => {
|
|
4761
|
+
setPaused(true);
|
|
4762
|
+
if (resumeTimeoutRef.current) {
|
|
4763
|
+
clearTimeout(resumeTimeoutRef.current);
|
|
4764
|
+
resumeTimeoutRef.current = null;
|
|
4765
|
+
}
|
|
4766
|
+
};
|
|
4767
|
+
const scheduleAutoplayResume = () => {
|
|
4768
|
+
if (!auto) return;
|
|
4769
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
4770
|
+
if (pauseOnInteraction <= 0) {
|
|
4771
|
+
setPaused(false);
|
|
4772
|
+
return;
|
|
4773
|
+
}
|
|
4774
|
+
resumeTimeoutRef.current = setTimeout(() => {
|
|
4775
|
+
setPaused(false);
|
|
4776
|
+
resumeTimeoutRef.current = null;
|
|
4777
|
+
}, pauseOnInteraction);
|
|
4759
4778
|
};
|
|
4779
|
+
React30.useEffect(() => {
|
|
4780
|
+
return () => {
|
|
4781
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
4782
|
+
};
|
|
4783
|
+
}, []);
|
|
4760
4784
|
const getClientX = (e) => {
|
|
4761
4785
|
if ("touches" in e) return e.touches[0]?.clientX ?? 0;
|
|
4762
4786
|
return e.clientX;
|
|
@@ -4764,7 +4788,7 @@ var Swiper = (props) => {
|
|
|
4764
4788
|
const handleDragStart = (e) => {
|
|
4765
4789
|
if (!canSlide) return;
|
|
4766
4790
|
if ("button" in e && e.button !== 0) return;
|
|
4767
|
-
|
|
4791
|
+
pauseForInteraction();
|
|
4768
4792
|
setIsDragging(true);
|
|
4769
4793
|
setAnimated(false);
|
|
4770
4794
|
startXRef.current = getClientX(e);
|
|
@@ -4794,6 +4818,7 @@ var Swiper = (props) => {
|
|
|
4794
4818
|
setAnimated(true);
|
|
4795
4819
|
}
|
|
4796
4820
|
setDragOffset(0);
|
|
4821
|
+
scheduleAutoplayResume();
|
|
4797
4822
|
};
|
|
4798
4823
|
window.addEventListener("mousemove", handleMove);
|
|
4799
4824
|
window.addEventListener("mouseup", handleEnd);
|
package/dist/index.cjs
CHANGED
|
@@ -9124,6 +9124,7 @@ var Swiper = (props) => {
|
|
|
9124
9124
|
spaceBetween = 16,
|
|
9125
9125
|
showProgress = false,
|
|
9126
9126
|
autoplayDelay = 3e3,
|
|
9127
|
+
pauseOnInteraction = 0,
|
|
9127
9128
|
speed = 300,
|
|
9128
9129
|
slideBy = 1,
|
|
9129
9130
|
index: indexProp,
|
|
@@ -9151,6 +9152,8 @@ var Swiper = (props) => {
|
|
|
9151
9152
|
const startXRef = import_react31.default.useRef(0);
|
|
9152
9153
|
const startTimeRef = import_react31.default.useRef(0);
|
|
9153
9154
|
const autoplayTimerRef = import_react31.default.useRef(null);
|
|
9155
|
+
const resumeTimeoutRef = import_react31.default.useRef(null);
|
|
9156
|
+
const [paused, setPaused] = import_react31.default.useState(false);
|
|
9154
9157
|
import_react31.default.useEffect(() => {
|
|
9155
9158
|
const el = containerRef.current;
|
|
9156
9159
|
if (!el) return;
|
|
@@ -9250,15 +9253,36 @@ var Swiper = (props) => {
|
|
|
9250
9253
|
slideTo
|
|
9251
9254
|
}));
|
|
9252
9255
|
import_react31.default.useEffect(() => {
|
|
9253
|
-
if (!auto || !canSlide) return;
|
|
9256
|
+
if (!auto || !canSlide || paused) return;
|
|
9254
9257
|
autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
|
|
9255
9258
|
return () => {
|
|
9256
9259
|
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
|
9257
9260
|
};
|
|
9258
|
-
}, [auto, autoplayDelay, slideNext, canSlide]);
|
|
9259
|
-
const
|
|
9260
|
-
|
|
9261
|
+
}, [auto, autoplayDelay, slideNext, canSlide, paused]);
|
|
9262
|
+
const pauseForInteraction = () => {
|
|
9263
|
+
setPaused(true);
|
|
9264
|
+
if (resumeTimeoutRef.current) {
|
|
9265
|
+
clearTimeout(resumeTimeoutRef.current);
|
|
9266
|
+
resumeTimeoutRef.current = null;
|
|
9267
|
+
}
|
|
9268
|
+
};
|
|
9269
|
+
const scheduleAutoplayResume = () => {
|
|
9270
|
+
if (!auto) return;
|
|
9271
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
9272
|
+
if (pauseOnInteraction <= 0) {
|
|
9273
|
+
setPaused(false);
|
|
9274
|
+
return;
|
|
9275
|
+
}
|
|
9276
|
+
resumeTimeoutRef.current = setTimeout(() => {
|
|
9277
|
+
setPaused(false);
|
|
9278
|
+
resumeTimeoutRef.current = null;
|
|
9279
|
+
}, pauseOnInteraction);
|
|
9261
9280
|
};
|
|
9281
|
+
import_react31.default.useEffect(() => {
|
|
9282
|
+
return () => {
|
|
9283
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
9284
|
+
};
|
|
9285
|
+
}, []);
|
|
9262
9286
|
const getClientX = (e) => {
|
|
9263
9287
|
if ("touches" in e) return e.touches[0]?.clientX ?? 0;
|
|
9264
9288
|
return e.clientX;
|
|
@@ -9266,7 +9290,7 @@ var Swiper = (props) => {
|
|
|
9266
9290
|
const handleDragStart = (e) => {
|
|
9267
9291
|
if (!canSlide) return;
|
|
9268
9292
|
if ("button" in e && e.button !== 0) return;
|
|
9269
|
-
|
|
9293
|
+
pauseForInteraction();
|
|
9270
9294
|
setIsDragging(true);
|
|
9271
9295
|
setAnimated(false);
|
|
9272
9296
|
startXRef.current = getClientX(e);
|
|
@@ -9296,6 +9320,7 @@ var Swiper = (props) => {
|
|
|
9296
9320
|
setAnimated(true);
|
|
9297
9321
|
}
|
|
9298
9322
|
setDragOffset(0);
|
|
9323
|
+
scheduleAutoplayResume();
|
|
9299
9324
|
};
|
|
9300
9325
|
window.addEventListener("mousemove", handleMove);
|
|
9301
9326
|
window.addEventListener("mouseup", handleEnd);
|
package/dist/index.js
CHANGED
|
@@ -8725,6 +8725,7 @@ var Swiper = (props) => {
|
|
|
8725
8725
|
spaceBetween = 16,
|
|
8726
8726
|
showProgress = false,
|
|
8727
8727
|
autoplayDelay = 3e3,
|
|
8728
|
+
pauseOnInteraction = 0,
|
|
8728
8729
|
speed = 300,
|
|
8729
8730
|
slideBy = 1,
|
|
8730
8731
|
index: indexProp,
|
|
@@ -8752,6 +8753,8 @@ var Swiper = (props) => {
|
|
|
8752
8753
|
const startXRef = React30.useRef(0);
|
|
8753
8754
|
const startTimeRef = React30.useRef(0);
|
|
8754
8755
|
const autoplayTimerRef = React30.useRef(null);
|
|
8756
|
+
const resumeTimeoutRef = React30.useRef(null);
|
|
8757
|
+
const [paused, setPaused] = React30.useState(false);
|
|
8755
8758
|
React30.useEffect(() => {
|
|
8756
8759
|
const el = containerRef.current;
|
|
8757
8760
|
if (!el) return;
|
|
@@ -8851,15 +8854,36 @@ var Swiper = (props) => {
|
|
|
8851
8854
|
slideTo
|
|
8852
8855
|
}));
|
|
8853
8856
|
React30.useEffect(() => {
|
|
8854
|
-
if (!auto || !canSlide) return;
|
|
8857
|
+
if (!auto || !canSlide || paused) return;
|
|
8855
8858
|
autoplayTimerRef.current = setInterval(slideNext, autoplayDelay);
|
|
8856
8859
|
return () => {
|
|
8857
8860
|
if (autoplayTimerRef.current) clearInterval(autoplayTimerRef.current);
|
|
8858
8861
|
};
|
|
8859
|
-
}, [auto, autoplayDelay, slideNext, canSlide]);
|
|
8860
|
-
const
|
|
8861
|
-
|
|
8862
|
+
}, [auto, autoplayDelay, slideNext, canSlide, paused]);
|
|
8863
|
+
const pauseForInteraction = () => {
|
|
8864
|
+
setPaused(true);
|
|
8865
|
+
if (resumeTimeoutRef.current) {
|
|
8866
|
+
clearTimeout(resumeTimeoutRef.current);
|
|
8867
|
+
resumeTimeoutRef.current = null;
|
|
8868
|
+
}
|
|
8869
|
+
};
|
|
8870
|
+
const scheduleAutoplayResume = () => {
|
|
8871
|
+
if (!auto) return;
|
|
8872
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
8873
|
+
if (pauseOnInteraction <= 0) {
|
|
8874
|
+
setPaused(false);
|
|
8875
|
+
return;
|
|
8876
|
+
}
|
|
8877
|
+
resumeTimeoutRef.current = setTimeout(() => {
|
|
8878
|
+
setPaused(false);
|
|
8879
|
+
resumeTimeoutRef.current = null;
|
|
8880
|
+
}, pauseOnInteraction);
|
|
8862
8881
|
};
|
|
8882
|
+
React30.useEffect(() => {
|
|
8883
|
+
return () => {
|
|
8884
|
+
if (resumeTimeoutRef.current) clearTimeout(resumeTimeoutRef.current);
|
|
8885
|
+
};
|
|
8886
|
+
}, []);
|
|
8863
8887
|
const getClientX = (e) => {
|
|
8864
8888
|
if ("touches" in e) return e.touches[0]?.clientX ?? 0;
|
|
8865
8889
|
return e.clientX;
|
|
@@ -8867,7 +8891,7 @@ var Swiper = (props) => {
|
|
|
8867
8891
|
const handleDragStart = (e) => {
|
|
8868
8892
|
if (!canSlide) return;
|
|
8869
8893
|
if ("button" in e && e.button !== 0) return;
|
|
8870
|
-
|
|
8894
|
+
pauseForInteraction();
|
|
8871
8895
|
setIsDragging(true);
|
|
8872
8896
|
setAnimated(false);
|
|
8873
8897
|
startXRef.current = getClientX(e);
|
|
@@ -8897,6 +8921,7 @@ var Swiper = (props) => {
|
|
|
8897
8921
|
setAnimated(true);
|
|
8898
8922
|
}
|
|
8899
8923
|
setDragOffset(0);
|
|
8924
|
+
scheduleAutoplayResume();
|
|
8900
8925
|
};
|
|
8901
8926
|
window.addEventListener("mousemove", handleMove);
|
|
8902
8927
|
window.addEventListener("mouseup", handleEnd);
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
2. 컴포넌트는 `width: 100%` 로 부모를 채우므로 **반드시 GridItem 또는 wrapper 로 감싸서 너비를 제어**.
|
|
22
22
|
3. **CSS 로 DS 컴포넌트를 오버라이드하지 않는다**.
|
|
23
23
|
4. 색상·간격·타이포는 **semantic 토큰만** 사용. primitive / brand 직접 참조 금지.
|
|
24
|
+
5. **모든 visible 그룹은 명시적 spacing 필수**. wrapper 의 `padding` / `gap` / 간격을 spacing 토큰으로 지정. 매직 넘버(`16px`, `1rem` 등) 금지.
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -158,6 +159,270 @@ import { FullGrid, FullScreen, GapGrid, GridItem } from "@x-plat/design-system/l
|
|
|
158
159
|
|
|
159
160
|
---
|
|
160
161
|
|
|
162
|
+
## 공간(Spacing) 처리 — **누락 금지**
|
|
163
|
+
|
|
164
|
+
DS 컴포넌트는 자체적으로 spacing 을 가지지 않는다(`width: 100%` 만). **wrapper 가 모든 spacing 을 책임진다.** 누락하면 콘텐츠가 붙어버린다.
|
|
165
|
+
|
|
166
|
+
### 어디에 적용하는가
|
|
167
|
+
|
|
168
|
+
| 상황 | 처리 |
|
|
169
|
+
|---|---|
|
|
170
|
+
| 컴포넌트들 사이 세로 간격 | wrapper 에 `display: flex; flex-direction: column; gap: var(--spacing-space-N);` |
|
|
171
|
+
| 컴포넌트들 사이 가로 간격 | wrapper 에 `display: flex; gap: var(--spacing-space-N);` 또는 `FullGrid gap` |
|
|
172
|
+
| 컨테이너 내부 padding | wrapper 에 `padding: var(--spacing-space-N);` |
|
|
173
|
+
| 카드/모달 등 박스 내부 | `padding: var(--spacing-space-4);` 가 일반적 |
|
|
174
|
+
| 섹션과 섹션 사이 | `margin-top: var(--spacing-space-6);` 또는 부모 gap |
|
|
175
|
+
|
|
176
|
+
### 사용 가능한 spacing 토큰 (semantic 만)
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
--spacing-space-{none|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16}
|
|
180
|
+
--spacing-radius-{none|sm|md|lg|xl|full}
|
|
181
|
+
--spacing-stroke-{none|xs|sm|md|lg|xl|2xl}
|
|
182
|
+
--spacing-control-height-{xs|sm|md|lg}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
space-1 = 4px, space-2 = 8px, space-3 = 12px, space-4 = 16px, space-5 = 20px, space-6 = 24px, ...
|
|
186
|
+
|
|
187
|
+
### 자주 누락되는 케이스 — 반드시 적용
|
|
188
|
+
|
|
189
|
+
DS 컴포넌트는 자체 외부 margin 없음. 부모가 gap 안 주면 모두 붙어버린다. 아래 케이스에서 절대 빼먹지 말 것.
|
|
190
|
+
|
|
191
|
+
#### 1. 섹션 내 카드 목록 (자주 누락 #1)
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
// ❌ 카드들이 다닥다닥 붙어버림
|
|
195
|
+
<section>
|
|
196
|
+
<Card>...</Card>
|
|
197
|
+
<Card>...</Card>
|
|
198
|
+
<Card>...</Card>
|
|
199
|
+
</section>
|
|
200
|
+
|
|
201
|
+
// ✅ 부모에 gap 필수
|
|
202
|
+
<section style={{
|
|
203
|
+
display: "flex",
|
|
204
|
+
flexDirection: "column",
|
|
205
|
+
gap: "var(--spacing-space-4)", // 카드 간 16px
|
|
206
|
+
}}>
|
|
207
|
+
<Card>...</Card>
|
|
208
|
+
<Card>...</Card>
|
|
209
|
+
<Card>...</Card>
|
|
210
|
+
</section>
|
|
211
|
+
|
|
212
|
+
// ✅ 카드 그리드형 — FullGrid 사용 시 gap prop
|
|
213
|
+
<FullGrid gap={16}>
|
|
214
|
+
<GridItem column={{ default: 4 }}><Card>...</Card></GridItem>
|
|
215
|
+
<GridItem column={{ default: 4 }}><Card>...</Card></GridItem>
|
|
216
|
+
<GridItem column={{ default: 4 }}><Card>...</Card></GridItem>
|
|
217
|
+
</FullGrid>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### 2. 폼 요소 사이 간격 (자주 누락 #2)
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
// ❌ Input 들이 붙고 라벨/도움말도 빽빽
|
|
224
|
+
<form>
|
|
225
|
+
<Input label="이름" />
|
|
226
|
+
<Input label="이메일" />
|
|
227
|
+
<Select options={...} />
|
|
228
|
+
<Button type="primary">제출</Button>
|
|
229
|
+
</form>
|
|
230
|
+
|
|
231
|
+
// ✅ form 전체 세로 gap + 버튼 가로 정렬
|
|
232
|
+
<form style={{
|
|
233
|
+
display: "flex",
|
|
234
|
+
flexDirection: "column",
|
|
235
|
+
gap: "var(--spacing-space-4)", // 폼 필드 간 16px
|
|
236
|
+
}}>
|
|
237
|
+
<Input label="이름" />
|
|
238
|
+
<Input label="이메일" />
|
|
239
|
+
<Select options={...} />
|
|
240
|
+
<div style={{
|
|
241
|
+
display: "flex",
|
|
242
|
+
gap: "var(--spacing-space-2)", // 버튼 간 8px
|
|
243
|
+
justifyContent: "flex-end",
|
|
244
|
+
marginTop: "var(--spacing-space-2)", // 폼 필드와 추가 8px (선택)
|
|
245
|
+
}}>
|
|
246
|
+
<Button type="default">취소</Button>
|
|
247
|
+
<Button type="primary">제출</Button>
|
|
248
|
+
</div>
|
|
249
|
+
</form>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### 3. 페이지 섹션 사이 간격
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
// ❌ 섹션들이 붙어버림
|
|
256
|
+
<main>
|
|
257
|
+
<section><h2>섹션 A</h2>...</section>
|
|
258
|
+
<section><h2>섹션 B</h2>...</section>
|
|
259
|
+
</main>
|
|
260
|
+
|
|
261
|
+
// ✅ 페이지 padding + 섹션 간 gap
|
|
262
|
+
<main style={{
|
|
263
|
+
display: "flex",
|
|
264
|
+
flexDirection: "column",
|
|
265
|
+
gap: "var(--spacing-space-8)", // 섹션 간 32px
|
|
266
|
+
padding: "var(--spacing-space-6)", // 페이지 padding 24px
|
|
267
|
+
}}>
|
|
268
|
+
<section>...</section>
|
|
269
|
+
<section>...</section>
|
|
270
|
+
</main>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### 4. 카드 내부 콘텐츠
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
// Card 컴포넌트 children 직접 배치 시
|
|
277
|
+
<Card>
|
|
278
|
+
<div style={{
|
|
279
|
+
padding: "var(--spacing-space-4)", // 카드 내부 padding 16px
|
|
280
|
+
display: "flex",
|
|
281
|
+
flexDirection: "column",
|
|
282
|
+
gap: "var(--spacing-space-3)", // 콘텐츠 간 12px
|
|
283
|
+
}}>
|
|
284
|
+
<h3>제목</h3>
|
|
285
|
+
<p>본문</p>
|
|
286
|
+
<Button type="primary">액션</Button>
|
|
287
|
+
</div>
|
|
288
|
+
</Card>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### 5. 가로 액션 버튼 그룹
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
<div style={{
|
|
295
|
+
display: "flex",
|
|
296
|
+
gap: "var(--spacing-space-2)", // 버튼 간 8px
|
|
297
|
+
}}>
|
|
298
|
+
<Button type="default">취소</Button>
|
|
299
|
+
<Button type="primary">저장</Button>
|
|
300
|
+
</div>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 폼 요소 구조 — 중첩 금지
|
|
304
|
+
|
|
305
|
+
**Input / TextArea / Select 안에 다른 컴포넌트를 넣지 않는다.** 레이아웃이 깨진다. 폼 요소는 항상 **형제(sibling)** 로 배치한다.
|
|
306
|
+
|
|
307
|
+
#### ❌ 금지 — 폼 요소 중첩
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
// Input 안에 Button → 잘못
|
|
311
|
+
<Input>
|
|
312
|
+
<Button>검색</Button>
|
|
313
|
+
</Input>
|
|
314
|
+
|
|
315
|
+
// TextArea 안에 Button → 잘못
|
|
316
|
+
<TextArea>
|
|
317
|
+
<Button>전송</Button>
|
|
318
|
+
</TextArea>
|
|
319
|
+
|
|
320
|
+
// Select 안에 Input → 잘못
|
|
321
|
+
<Select>
|
|
322
|
+
<Input placeholder="검색" />
|
|
323
|
+
</Select>
|
|
324
|
+
|
|
325
|
+
// form 안에 form → 절대 금지
|
|
326
|
+
<form>
|
|
327
|
+
<form>...</form>
|
|
328
|
+
</form>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### ✅ 올바른 패턴
|
|
332
|
+
|
|
333
|
+
**Pattern 1: "입력 + 전송 버튼" 합성** → `ChatInput` 컴포넌트 사용
|
|
334
|
+
```tsx
|
|
335
|
+
import { ChatInput } from "@x-plat/design-system/components";
|
|
336
|
+
|
|
337
|
+
<ChatInput
|
|
338
|
+
placeholder="메시지 입력"
|
|
339
|
+
onSubmit={(value) => handleSend(value)}
|
|
340
|
+
/>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Pattern 2: Input 우측 아이콘/소형 액션** → Input 의 `suffix` 슬롯 사용
|
|
344
|
+
```tsx
|
|
345
|
+
<Input
|
|
346
|
+
placeholder="검색"
|
|
347
|
+
suffix={<Icon name="search" />}
|
|
348
|
+
/>
|
|
349
|
+
```
|
|
350
|
+
> `suffix` 는 작은 아이콘/버튼 1개용. 일반 `<Button>` 컴포넌트 넣지 말 것 (사이즈 충돌).
|
|
351
|
+
|
|
352
|
+
**Pattern 3: 폼 요소 + 액션 버튼** → 형제 배치, wrapper 에서 gap
|
|
353
|
+
```tsx
|
|
354
|
+
<div style={{ display: "flex", gap: "var(--spacing-space-2)" }}>
|
|
355
|
+
<Input placeholder="이메일" />
|
|
356
|
+
<Button type="primary">확인</Button>
|
|
357
|
+
</div>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Pattern 4: 폼 요소들 묶음** → 모두 형제, 부모에서 gap
|
|
361
|
+
```tsx
|
|
362
|
+
<form style={{
|
|
363
|
+
display: "flex",
|
|
364
|
+
flexDirection: "column",
|
|
365
|
+
gap: "var(--spacing-space-4)",
|
|
366
|
+
}}>
|
|
367
|
+
<Input label="이름" />
|
|
368
|
+
<TextArea label="자기소개" />
|
|
369
|
+
<Select label="국가" options={...} />
|
|
370
|
+
<Button type="primary">제출</Button>
|
|
371
|
+
</form>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
#### 폼 요소별 children/슬롯 정책
|
|
375
|
+
|
|
376
|
+
| 컴포넌트 | children 받음? | 슬롯 |
|
|
377
|
+
|---|---|---|
|
|
378
|
+
| `Input` | ❌ | `suffix` (아이콘 1개) |
|
|
379
|
+
| `TextArea` | ❌ | 없음 |
|
|
380
|
+
| `Select` | ❌ | `options` prop 사용 (children 아님) |
|
|
381
|
+
| `CheckBox` / `Radio` / `Switch` | ❌ | `label` prop |
|
|
382
|
+
| `DatePicker` / `Calendar` | ❌ | controlled value/onChange |
|
|
383
|
+
| `Button` | ✅ | 텍스트/아이콘만 |
|
|
384
|
+
| `ChatInput` | ❌ | 입력+버튼 합성 컴포넌트 |
|
|
385
|
+
|
|
386
|
+
> "입력 안에 무언가를 넣고 싶다" 는 욕구가 들면 → **다른 형태의 합성 컴포넌트가 있는지 먼저 확인**. 없으면 **wrapper + 형제** 패턴 사용.
|
|
387
|
+
|
|
388
|
+
### 케이스별 권장 spacing 값
|
|
389
|
+
|
|
390
|
+
| 케이스 | 권장 값 |
|
|
391
|
+
|---|---|
|
|
392
|
+
| 폼 필드 간 (vertical) | `space-4` (16px) |
|
|
393
|
+
| 카드 목록 간 (vertical) | `space-3 ~ space-4` (12-16px) |
|
|
394
|
+
| 그리드형 카드 (gap) | `space-4` (16px) |
|
|
395
|
+
| 액션 버튼 간 (horizontal) | `space-2` (8px) |
|
|
396
|
+
| 섹션 간 | `space-6 ~ space-8` (24-32px) |
|
|
397
|
+
| 카드/박스 내부 padding | `space-4 ~ space-5` (16-20px) |
|
|
398
|
+
| 라벨 - 입력 (Input 내부 자동 처리됨) | DS 컴포넌트가 처리 |
|
|
399
|
+
| 페이지 padding | `space-6` (24px) |
|
|
400
|
+
|
|
401
|
+
### ❌ 금지 패턴
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
// 매직 넘버 (토큰 안 씀)
|
|
405
|
+
<div style={{ padding: 16, gap: 8 }}>...</div>
|
|
406
|
+
|
|
407
|
+
// 컴포넌트 사이 spacing 없음 (요소들이 붙어버림)
|
|
408
|
+
<div>
|
|
409
|
+
<Button>A</Button>
|
|
410
|
+
<Button>B</Button>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
// primitive 직접 참조 (semantic 만 허용)
|
|
414
|
+
<div style={{ padding: "var(--spacing-size-4)" }}>...</div>
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### 체크리스트 (코드 작성 후 확인)
|
|
418
|
+
|
|
419
|
+
- [ ] 모든 wrapper `<div>` 에 명시적 `gap` 또는 `padding` 이 있는가
|
|
420
|
+
- [ ] spacing 값이 모두 `var(--spacing-space-*)` 등 토큰인가 (숫자 리터럴 금지)
|
|
421
|
+
- [ ] 카드/박스 컴포넌트 내부에 적절한 padding 이 있는가
|
|
422
|
+
- [ ] 섹션 사이에 가시적인 간격이 있는가
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
161
426
|
## 컴포넌트 사용 패턴
|
|
162
427
|
|
|
163
428
|
### ✅ 올바른 사용
|