@hivcd-printshop/design-system 1.1.0 → 1.2.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.
package/dist/index.js CHANGED
@@ -22,19 +22,47 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
24
  BackButton: () => BackButton,
25
+ BasicFormField: () => BasicFormField,
25
26
  CTA: () => CTA,
26
27
  CategorySegment: () => CategorySegment,
27
28
  CategorySegmentedControl: () => CategorySegmentedControl,
29
+ Column: () => Column,
30
+ ColumnContentContainer: () => ColumnContentContainer,
31
+ ColumnLabel: () => ColumnLabel,
32
+ ColumnSubTitle: () => ColumnSubTitle,
33
+ ColumnText: () => ColumnText,
34
+ ColumnTextStyle: () => ColumnTextStyle,
35
+ Dropdown: () => Dropdown,
36
+ FormCheckBox: () => FormCheckBox,
37
+ FormColor: () => FormColor,
38
+ FormFile: () => FormFile,
39
+ FormNumber: () => FormNumber,
40
+ FormPassword: () => FormPassword,
41
+ FormSelectBox: () => FormSelectBox,
42
+ FormTel: () => FormTel,
43
+ FormText: () => FormText,
44
+ FormTextArea: () => FormTextArea,
45
+ IconCheck: () => IconCheck,
46
+ IconChevronDownXS: () => IconChevronDownXS,
47
+ IconInfo: () => IconInfo,
48
+ List: () => List,
49
+ Modal: () => Modal,
50
+ ModalText: () => ModalText,
51
+ ModalTextStyle: () => ModalTextStyle,
52
+ MultiColumn: () => MultiColumn,
28
53
  NoticeBadge: () => NoticeBadge,
29
54
  NoticeBar: () => NoticeBar,
30
55
  SearchBar: () => SearchBar,
56
+ SelectBoxOption: () => SelectBoxOption,
31
57
  SideRadioAccordion: () => SideRadioAccordion,
32
58
  TNB: () => TNB,
33
59
  Tag: () => Tag,
60
+ TextButton: () => TextButton,
34
61
  Timetable: () => Timetable,
35
62
  TitleInput: () => TitleInput,
36
63
  Toast: () => Toast,
37
- WeekNavigation: () => WeekNavigation
64
+ WeekNavigation: () => WeekNavigation,
65
+ getInputStyles: () => getInputStyles
38
66
  });
39
67
  module.exports = __toCommonJS(index_exports);
40
68
 
@@ -92,6 +120,20 @@ var IconChevronDownXS = ({
92
120
  }
93
121
  );
94
122
  };
123
+ var IconCheck = ({ className, stroke }) => {
124
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
125
+ "svg",
126
+ {
127
+ className,
128
+ width: "13",
129
+ height: "13",
130
+ viewBox: "0 0 13 13",
131
+ fill: "none",
132
+ xmlns: "http://www.w3.org/2000/svg",
133
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.05566 7.0555L4.83344 9.8333L10.9446 3.72217", stroke: stroke || "#121212" })
134
+ }
135
+ );
136
+ };
95
137
  var IconSearch = ({ className, stroke }) => {
96
138
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
97
139
  "svg",
@@ -126,6 +168,32 @@ var IconSearch = ({ className, stroke }) => {
126
168
  }
127
169
  );
128
170
  };
171
+ var IconInfo = ({ className, stroke }) => {
172
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
173
+ "svg",
174
+ {
175
+ className,
176
+ width: "16",
177
+ height: "16",
178
+ viewBox: "0 0 16 16",
179
+ fill: "none",
180
+ xmlns: "http://www.w3.org/2000/svg",
181
+ children: [
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6.25", stroke: stroke || "#8b94a1", strokeWidth: "1.5" }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
184
+ "path",
185
+ {
186
+ d: "M8 7.25V11.25",
187
+ stroke: stroke || "#8b94a1",
188
+ strokeWidth: "1.5",
189
+ strokeLinecap: "round"
190
+ }
191
+ ),
192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "5", r: "0.9", fill: stroke || "#8b94a1" })
193
+ ]
194
+ }
195
+ );
196
+ };
129
197
  var IconGoBack = ({ className, stroke }) => {
130
198
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
131
199
  "svg",
@@ -176,46 +244,732 @@ var BackButton = ({ onClick, className }) => {
176
244
  // src/components/CTA/CTA.tsx
177
245
  var import_jsx_runtime3 = require("react/jsx-runtime");
178
246
  var CTA = ({
179
- primary = true,
180
- variable = "default",
247
+ primary = false,
248
+ alert = false,
249
+ disabled = false,
181
250
  label,
182
251
  onClick,
183
- className
252
+ className,
253
+ forModal = false,
254
+ ...props
184
255
  }) => {
185
256
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
186
257
  "button",
187
258
  {
188
259
  type: "button",
260
+ disabled,
189
261
  className: cn(
190
- "w-full max-w-360 h-12 p-2.5 text-center text-head3-medium",
191
- `${primary ? "border-0" : "border border-gray02"}`,
192
- /** Disabled */
193
- `${variable == "disabled" ? primary ? "bg-gray01 text-gray04 border border-gray01" : "bg-gray02 text-gray04" : ""}`,
194
- /** Default */
195
- `${variable == "default" ? primary ? "bg-black text-white" : "bg-white text-strong" : ""}`,
196
- /** Hover */
197
- `${variable != "disabled" ? primary ? "hover:bg-gray08 hover:text-white" : "hover:bg-gray01 hover:text-strong" : ""}`,
198
- /** Hover */
199
- `${variable != "disabled" && "hover:cursor-pointer"}`,
200
- /** Pressed */
201
- `${variable == "pressed" ? primary ? "bg-gray09 text-white" : "bg-gray02 text-strong" : ""}`,
262
+ forModal ? "h-14.5" : "h-12",
263
+ "w-full max-w-360 px-4",
264
+ "flex items-center justify-center",
265
+ "border-t box-border border-gray02",
266
+ primary && "border-none",
267
+ "text-head3-medium text-strong",
268
+ primary && "text-white",
269
+ alert && "text-danger",
270
+ disabled && "text-quaternary",
271
+ "bg-white hover:bg-gray00 active:bg-gray01 disabled:bg-gray02",
272
+ primary && "bg-black hover:bg-gray08 active:bg-gray09 disabled:bg-gray01",
273
+ "cursor-pointer disabled:cursor-default",
202
274
  className
203
275
  ),
204
276
  onClick,
277
+ ...props,
205
278
  children: label
206
279
  }
207
280
  );
208
281
  };
209
282
 
210
- // src/components/createPost/CategorySegment.tsx
283
+ // src/components/Column/MultiColumn.tsx
211
284
  var import_jsx_runtime4 = require("react/jsx-runtime");
285
+ var MultiColumnMain = ({ children, className }) => {
286
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: cn("flex flex-col w-full h-full", className), children });
287
+ };
288
+ var Columns = ({ children, className }) => {
289
+ return (
290
+ // flex 래퍼를 사용하고 하위 Column들이 flex-1을 가지게 하거나,
291
+ // grid를 사용하여 열을 나눌 수 있어. 여기서는 flex를 활용한 균등 분할 예시야.
292
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: cn("flex w-full h-full divide-x divide-gray02", className), children })
293
+ );
294
+ };
295
+ var CTAs = ({ children, className }) => {
296
+ if (!children) return null;
297
+ return (
298
+ // CTA 버튼들을 중앙에 배치하는 예시 (우측 정렬이 필요하면 justify-end 사용)
299
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: cn("flex flex-col w-full h-fit", className), children })
300
+ );
301
+ };
302
+ var MultiColumn = Object.assign(MultiColumnMain, {
303
+ Columns,
304
+ CTAs
305
+ });
306
+
307
+ // src/components/Column/Column.tsx
308
+ var import_jsx_runtime5 = require("react/jsx-runtime");
309
+ var Column = ({ className, children, header }) => {
310
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("flex flex-col gap-5", "h-full w-full px-5 pt-3 pb-8", className), children: [
311
+ header && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { className: cn("text-head3-medium text-primary font-bold"), children: header }),
312
+ children
313
+ ] });
314
+ };
315
+
316
+ // src/components/Column/ColumnContentContainer.tsx
317
+ var import_jsx_runtime6 = require("react/jsx-runtime");
318
+ var ColumnContentContainer = ({ className, children }) => {
319
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("flex flex-col gap-1.5 w-full h-fit", className), children });
320
+ };
321
+
322
+ // src/components/Column/ColumnLabel.tsx
323
+ var import_jsx_runtime7 = require("react/jsx-runtime");
324
+ var ColumnLabel = ({ className, children, subLabel, htmlFor }) => {
325
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { htmlFor, className: cn("flex gap-1.5 items-end", className), children: [
326
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: cn("text-body2-regular text-tertiary"), children }),
327
+ subLabel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: cn("text-caption-book text-quaternary"), children: subLabel })
328
+ ] });
329
+ };
330
+
331
+ // src/components/Column/ColumnText.tsx
332
+ var import_jsx_runtime8 = require("react/jsx-runtime");
333
+ var ColumnTextStyle = "text-body3-book text-strong";
334
+ var ModalTextStyle = "text-body1-regular text-primary";
335
+ var ColumnText = ({ className, alert = false, children, onClick }) => {
336
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: cn(ColumnTextStyle, alert && "text-danger", className), onClick, children: children ? children : "-" });
337
+ };
338
+
339
+ // src/components/Column/ColumnSubTitle.tsx
340
+ var import_jsx_runtime9 = require("react/jsx-runtime");
341
+ var ColumnSubTitle = ({
342
+ className,
343
+ children,
344
+ action,
345
+ as: Tag2 = "h2"
346
+ }) => {
347
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Tag2, { className: cn("flex items-center justify-between gap-1.5", "w-full", className), children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: cn("text-head3-medium text-strong"), children }),
349
+ action && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "flex shrink-0 items-center", children: action })
350
+ ] });
351
+ };
352
+
353
+ // src/components/Form/BasicFormField.tsx
354
+ var import_jsx_runtime10 = require("react/jsx-runtime");
355
+ var BasicFormField = ({
356
+ children,
357
+ isDanger,
358
+ dangerText,
359
+ className
360
+ }) => {
361
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("flex flex-col gap-1.5", className), children: [
362
+ children,
363
+ isDanger && dangerText && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-caption-book text-danger", children: dangerText })
364
+ ] });
365
+ };
366
+ var getInputStyles = (isDanger, additionalClass) => {
367
+ return cn(
368
+ "w-full h-10 px-3",
369
+ "box-border border border-gray02 focus:outline-none focus:border-black",
370
+ "bg-white hover:bg-gray00 transition-colors focus:hover:bg-white",
371
+ ColumnTextStyle,
372
+ "text-primary placeholder:text-quaternary",
373
+ isDanger && "border-danger",
374
+ // 에러 시 빨간 테두리
375
+ additionalClass
376
+ // 각 인풋별 추가 스타일 (예: textarea의 h-30)
377
+ );
378
+ };
379
+
380
+ // src/components/Form/FormText.tsx
381
+ var import_jsx_runtime11 = require("react/jsx-runtime");
382
+ var FormText = ({ className, isDanger, dangerText, ...props }) => {
383
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "text", className: getInputStyles(isDanger, className), ...props }) });
384
+ };
385
+
386
+ // src/components/Form/FormTextArea.tsx
387
+ var import_jsx_runtime12 = require("react/jsx-runtime");
388
+ var FormTextArea = ({ className, isDanger, dangerText, ...props }) => {
389
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
390
+ "textarea",
391
+ {
392
+ className: getInputStyles(isDanger, "resize-none h-30 py-2.5 " + (className || "")),
393
+ ...props
394
+ }
395
+ ) });
396
+ };
397
+
398
+ // src/components/Form/FormNumber.tsx
399
+ var import_jsx_runtime13 = require("react/jsx-runtime");
400
+ var FormNumber = ({ className, isDanger, dangerText, ...props }) => {
401
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("input", { type: "number", className: getInputStyles(isDanger, className), ...props }) });
402
+ };
403
+
404
+ // src/components/Form/FormPassword.tsx
405
+ var import_jsx_runtime14 = require("react/jsx-runtime");
406
+ var FormPassword = ({ className, isDanger, dangerText, ...props }) => {
407
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { type: "password", className: getInputStyles(isDanger, className), ...props }) });
408
+ };
409
+
410
+ // src/components/Form/FormTel.tsx
411
+ var import_jsx_runtime15 = require("react/jsx-runtime");
412
+ var FormTel = ({ className, isDanger, dangerText, onChange, ...props }) => {
413
+ const handlePhoneChange = (e) => {
414
+ const rawValue = e.target.value.replace(/[^0-9]/g, "");
415
+ let formattedValue = "";
416
+ if (rawValue.length < 4) formattedValue = rawValue;
417
+ else if (rawValue.length < 8) formattedValue = `${rawValue.slice(0, 3)}-${rawValue.slice(3)}`;
418
+ else
419
+ formattedValue = `${rawValue.slice(0, 3)}-${rawValue.slice(3, 7)}-${rawValue.slice(7, 11)}`;
420
+ e.target.value = formattedValue;
421
+ if (onChange) onChange(e);
422
+ };
423
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
424
+ "input",
425
+ {
426
+ type: "tel",
427
+ className: getInputStyles(isDanger, className),
428
+ onChange: handlePhoneChange,
429
+ maxLength: 13,
430
+ ...props
431
+ }
432
+ ) });
433
+ };
434
+
435
+ // src/components/Form/FormColor.tsx
436
+ var import_react = require("react");
437
+ var import_jsx_runtime16 = require("react/jsx-runtime");
438
+ var FormColor = ({
439
+ className,
440
+ isDanger,
441
+ dangerText,
442
+ value,
443
+ defaultValue = "#000000",
444
+ onChange,
445
+ ...props
446
+ }) => {
447
+ const [internalColor, setInternalColor] = (0, import_react.useState)(
448
+ value || defaultValue || "#000000"
449
+ );
450
+ const color = value !== void 0 ? value : internalColor;
451
+ const handleColorChange = (e) => {
452
+ setInternalColor(e.target.value);
453
+ onChange?.(e);
454
+ };
455
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
456
+ "label",
457
+ {
458
+ className: getInputStyles(
459
+ isDanger,
460
+ // 💡 focus-within을 통해 내부 <input>이 포커스될 때 부모(label)의 테두리와 배경 변경!
461
+ "flex items-center gap-2 relative focus-within:border-black focus-within:hover:bg-white" + (className || "")
462
+ ),
463
+ children: [
464
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-6 h-6 shrink-0 overflow-hidden", style: { backgroundColor: color }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
465
+ "input",
466
+ {
467
+ type: "color",
468
+ value: color,
469
+ onChange: handleColorChange,
470
+ className: "absolute inset-0 w-full h-full opacity-0",
471
+ ...props
472
+ }
473
+ ) }),
474
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-primary text-body3-regular uppercase flex-1 select-none", children: color })
475
+ ]
476
+ }
477
+ ) });
478
+ };
479
+
480
+ // src/components/Form/FormFile.tsx
481
+ var import_react2 = require("react");
482
+ var import_jsx_runtime17 = require("react/jsx-runtime");
483
+ var FormFile = ({
484
+ className,
485
+ isDanger,
486
+ dangerText,
487
+ placeholder = "\uD30C\uC77C\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694",
488
+ fileUrl,
489
+ filename,
490
+ onChange,
491
+ ...props
492
+ }) => {
493
+ const [selectedFile, setSelectedFile] = (0, import_react2.useState)(null);
494
+ const handleFileChange = (e) => {
495
+ const originalFile = e.target.files?.[0];
496
+ if (!originalFile) {
497
+ setSelectedFile(null);
498
+ onChange?.(e);
499
+ return;
500
+ }
501
+ let finalFile = originalFile;
502
+ if (filename) {
503
+ const lastDotIndex = originalFile.name.lastIndexOf(".");
504
+ const extension = lastDotIndex !== -1 ? originalFile.name.slice(lastDotIndex) : "";
505
+ const newFileName = filename.endsWith(extension) ? filename : `${filename}${extension}`;
506
+ finalFile = new File([originalFile], newFileName, {
507
+ type: originalFile.type,
508
+ lastModified: originalFile.lastModified
509
+ });
510
+ try {
511
+ const dataTransfer = new DataTransfer();
512
+ dataTransfer.items.add(finalFile);
513
+ e.target.files = dataTransfer.files;
514
+ } catch (err) {
515
+ console.warn("DataTransfer\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE0C\uB77C\uC6B0\uC800\uC785\uB2C8\uB2E4.", err);
516
+ }
517
+ }
518
+ setSelectedFile(finalFile);
519
+ onChange?.(e);
520
+ };
521
+ const handleDownload = (e) => {
522
+ e.preventDefault();
523
+ e.stopPropagation();
524
+ if (selectedFile) {
525
+ const url = URL.createObjectURL(selectedFile);
526
+ const a = document.createElement("a");
527
+ a.href = url;
528
+ a.download = selectedFile.name;
529
+ document.body.appendChild(a);
530
+ a.click();
531
+ document.body.removeChild(a);
532
+ URL.revokeObjectURL(url);
533
+ } else if (fileUrl) {
534
+ const a = document.createElement("a");
535
+ a.href = fileUrl;
536
+ a.download = fileUrl.split("/").pop() || "download";
537
+ a.target = "_blank";
538
+ document.body.appendChild(a);
539
+ a.click();
540
+ document.body.removeChild(a);
541
+ }
542
+ };
543
+ const displayFileName = selectedFile?.name || (fileUrl ? fileUrl.split("/").pop() : null);
544
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
545
+ "div",
546
+ {
547
+ className: getInputStyles(
548
+ isDanger,
549
+ "active:bg-gray00 flex items-center gap-2 justify-between " + (className || "")
550
+ ),
551
+ children: [
552
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
553
+ "div",
554
+ {
555
+ className: `truncate flex-1 max-w-50 w-full ${displayFileName ? "text-primary" : "text-quaternary"}`,
556
+ children: displayFileName ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
557
+ "span",
558
+ {
559
+ onClick: handleDownload,
560
+ className: "w-full underline text-primary cursor-pointer hover:text-black transition-colors",
561
+ title: `${displayFileName} \uB2E4\uC6B4\uB85C\uB4DC`,
562
+ children: displayFileName
563
+ }
564
+ ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "w-full", children: placeholder })
565
+ }
566
+ ),
567
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "px-1 -mx-1 cursor-pointer", children: [
568
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-primary text-caption-regular underline", children: "\uD30C\uC77C \uCCA8\uBD80" }),
569
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { type: "file", className: "hidden", onChange: handleFileChange, ...props })
570
+ ] })
571
+ ]
572
+ }
573
+ ) });
574
+ };
575
+
576
+ // src/components/Form/FormCheckBox.tsx
577
+ var import_jsx_runtime18 = require("react/jsx-runtime");
578
+ var FormCheckBox = ({
579
+ isChecked,
580
+ setIsChecked,
581
+ children,
582
+ className,
583
+ textStyle,
584
+ ...props
585
+ }) => {
586
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
587
+ "label",
588
+ {
589
+ className: cn(
590
+ "w-fit h-fit pr-1",
591
+ "flex items-center gap-1",
592
+ "hover:bg-gray00 active:bg-gray01 transition-colors",
593
+ "cursor-pointer select-none",
594
+ // 💡 1. 텍스트 드래그 방지 추가
595
+ className
596
+ ),
597
+ children: [
598
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
599
+ "input",
600
+ {
601
+ type: "checkbox",
602
+ checked: isChecked,
603
+ onChange: (e) => setIsChecked(e.target.checked),
604
+ className: "sr-only peer",
605
+ ...props
606
+ }
607
+ ),
608
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center justify-center w-5 h-5 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
609
+ "div",
610
+ {
611
+ className: cn(
612
+ "w-[15px] h-[15px]",
613
+ "box-border border",
614
+ "flex items-center justify-center transition-colors duration-200",
615
+ // 💡 3. 키보드 Tab 이동 시 접근성 향상 (포커스 링 표시)
616
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-black peer-focus-visible:ring-offset-1"
617
+ ),
618
+ children: isChecked && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconCheck, {})
619
+ }
620
+ ) }),
621
+ children && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: cn(ColumnTextStyle, textStyle), children })
622
+ ]
623
+ }
624
+ );
625
+ };
626
+
627
+ // src/components/Form/FormSelectBox.tsx
628
+ var import_react3 = require("react");
629
+
630
+ // src/components/Form/SelectBoxOption.tsx
631
+ var import_jsx_runtime19 = require("react/jsx-runtime");
632
+ var SelectBoxOption = ({
633
+ label,
634
+ onClick,
635
+ // selectedLabel,
636
+ className
637
+ }) => {
638
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
639
+ "li",
640
+ {
641
+ onClick,
642
+ className: cn(
643
+ "w-full h-11 px-3 shrink-0",
644
+ "flex items-center justify-start",
645
+ // "box-border border-b border-gray02",
646
+ "text-body3-book text-primary",
647
+ "bg-white hover:bg-gray00 active:bg-gray01",
648
+ // isSelected
649
+ // ? "bg-black text-white"
650
+ // : "",
651
+ className
652
+ ),
653
+ children: label
654
+ }
655
+ );
656
+ };
657
+
658
+ // src/components/Form/FormSelectBox.tsx
659
+ var import_jsx_runtime20 = require("react/jsx-runtime");
660
+ var FormSelectBox = ({
661
+ options,
662
+ value,
663
+ onChange,
664
+ placeholder = "\uC120\uD0DD\uD574\uC8FC\uC138\uC694",
665
+ isDanger,
666
+ dangerText,
667
+ className
668
+ }) => {
669
+ const [isOpen, setIsOpen] = (0, import_react3.useState)(false);
670
+ const containerRef = (0, import_react3.useRef)(null);
671
+ (0, import_react3.useEffect)(() => {
672
+ const handleClickOutside = (event) => {
673
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
674
+ setIsOpen(false);
675
+ }
676
+ };
677
+ document.addEventListener("mousedown", handleClickOutside);
678
+ return () => {
679
+ document.removeEventListener("mousedown", handleClickOutside);
680
+ };
681
+ }, []);
682
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BasicFormField, { isDanger, dangerText, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { ref: containerRef, className: "relative w-full", children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
684
+ "div",
685
+ {
686
+ tabIndex: 0,
687
+ className: getInputStyles(
688
+ isDanger,
689
+ cn(
690
+ "flex items-center justify-between cursor-pointer select-none",
691
+ isOpen && "border-black",
692
+ // 💡 열려있을 때는 테두리를 검은색으로 고정 (포커스 효과)
693
+ className
694
+ )
695
+ ),
696
+ onClick: (e) => {
697
+ if (isOpen) {
698
+ e.currentTarget.blur();
699
+ }
700
+ setIsOpen((prev) => !prev);
701
+ },
702
+ children: [
703
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: value ? "text-primary" : "text-quaternary", children: value || placeholder }),
704
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
705
+ IconChevronDownXS,
706
+ {
707
+ className: cn("text-gray06 transition-transform", isOpen && "rotate-180"),
708
+ stroke: "currentColor"
709
+ }
710
+ )
711
+ ]
712
+ }
713
+ ),
714
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
715
+ "ul",
716
+ {
717
+ className: cn(
718
+ "absolute top-full left-0 right-0 z-10",
719
+ // 💡 Input 바로 아래에 띄움
720
+ "box-border border border-gray02 bg-white flex flex-col",
721
+ "max-h-50 overflow-y-auto shadow-sm"
722
+ // 💡 항목이 너무 많으면 스크롤 생성
723
+ ),
724
+ children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
725
+ SelectBoxOption,
726
+ {
727
+ label: option,
728
+ onClick: () => {
729
+ onChange(option);
730
+ setIsOpen(false);
731
+ }
732
+ },
733
+ option
734
+ ))
735
+ }
736
+ )
737
+ ] }) });
738
+ };
739
+
740
+ // src/components/TextButton/TextButton.tsx
741
+ var import_jsx_runtime21 = require("react/jsx-runtime");
742
+ var TextButton = ({
743
+ children,
744
+ icon,
745
+ iconPosition = "left",
746
+ alert = false,
747
+ underline = true,
748
+ className,
749
+ ...props
750
+ }) => {
751
+ const content = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
752
+ icon && iconPosition === "left" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex shrink-0 items-center", children: icon }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: cn(underline && "underline underline-offset-2"), children }),
754
+ icon && iconPosition === "right" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex shrink-0 items-center", children: icon })
755
+ ] });
756
+ const classes = cn(
757
+ "inline-flex w-fit items-center gap-1 px-1 -mx-1",
758
+ "text-caption-book",
759
+ alert ? "text-danger" : "text-tertiary",
760
+ "hover:bg-gray00 active:bg-gray01 transition-colors",
761
+ alert ? "hover:text-danger" : "hover:text-primary",
762
+ "cursor-pointer select-none",
763
+ "disabled:cursor-default disabled:text-quaternary disabled:hover:bg-transparent",
764
+ "aria-disabled:cursor-default aria-disabled:text-quaternary aria-disabled:hover:bg-transparent",
765
+ className
766
+ );
767
+ if (props.href !== void 0) {
768
+ const { href, ...rest } = props;
769
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("a", { href, className: classes, ...rest, children: content });
770
+ }
771
+ const buttonProps = props;
772
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", className: classes, ...buttonProps, children: content });
773
+ };
774
+
775
+ // src/components/Dropdown/Dropdown.tsx
776
+ var import_react4 = require("react");
777
+ var import_jsx_runtime22 = require("react/jsx-runtime");
778
+ var Dropdown = ({ selectedLabel, setSelectedLabel, labels, className }) => {
779
+ const [isOpen, setIsOpen] = (0, import_react4.useState)(false);
780
+ const containerRef = (0, import_react4.useRef)(null);
781
+ (0, import_react4.useEffect)(() => {
782
+ const handleClickOutside = (event) => {
783
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
784
+ setIsOpen(false);
785
+ }
786
+ };
787
+ document.addEventListener("mousedown", handleClickOutside);
788
+ return () => {
789
+ document.removeEventListener("mousedown", handleClickOutside);
790
+ };
791
+ }, []);
792
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { ref: containerRef, className: "relative", children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
794
+ "div",
795
+ {
796
+ className: cn(
797
+ "w-fit h-fit px-2 py-1",
798
+ "flex items-center justify-center gap-1.5",
799
+ "text-body3-book text-strong",
800
+ "bg-white hover:bg-gray00 active:bg-gray01",
801
+ "cursor-pointer",
802
+ className
803
+ ),
804
+ onClick: () => setIsOpen((prev) => !prev),
805
+ children: [
806
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { children: selectedLabel }),
807
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
808
+ IconChevronDownXS,
809
+ {
810
+ className: cn("text-gray06 transition-transform", isOpen && "rotate-180"),
811
+ stroke: "currentColor"
812
+ }
813
+ )
814
+ ]
815
+ }
816
+ ),
817
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn("absolute top-full right-0 z-10", "box-border border border-gray02"), children: labels.map((label) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
818
+ SelectBoxOption,
819
+ {
820
+ label,
821
+ onClick: () => {
822
+ setSelectedLabel(label);
823
+ setIsOpen(false);
824
+ },
825
+ className: "w-44 h-11 justify-center"
826
+ },
827
+ label
828
+ )) })
829
+ ] });
830
+ };
831
+
832
+ // src/components/ListView/List.tsx
833
+ var import_jsx_runtime23 = require("react/jsx-runtime");
834
+ function List({
835
+ className,
836
+ label1,
837
+ label2,
838
+ label2OnClick,
839
+ label3,
840
+ disabled = false,
841
+ selected = false,
842
+ onClick,
843
+ children,
844
+ ...props
845
+ }) {
846
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
847
+ "li",
848
+ {
849
+ className: cn(
850
+ "w-full min-w-fit h-13 pl-4 pr-6",
851
+ "grid grid-cols-[4fr_1fr_1fr] items-center gap-4",
852
+ "bg-white hover:bg-gray00 hover:has-[.has-clickable:hover]:bg-white active:bg-gray01",
853
+ "text-primary select-none [&_*]:cursor-pointer cursor-pointer",
854
+ disabled && "opacity-40 [&_*]:cursor-pointer cursor-pointer",
855
+ selected && "bg-black text-white hover:bg-black active:bg-black hover:has-[.has-clickable:hover]:bg-black",
856
+ className
857
+ ),
858
+ ...props,
859
+ onClick,
860
+ children: [
861
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("section", { className: cn("flex items-center gap-4"), children: [
862
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("label", { className: cn("text-body2-regular"), children: label1 ? label1 : "-" }),
863
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn("flex items-center gap-2"), children })
864
+ ] }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
866
+ "label",
867
+ {
868
+ className: cn(
869
+ "text-body3-book truncate p-1 w-fit",
870
+ label2OnClick && "underline has-clickable",
871
+ label2OnClick && !selected && "hover:bg-gray00"
872
+ ),
873
+ onClick: label2OnClick,
874
+ children: label2 ? label2 : "-"
875
+ }
876
+ ),
877
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("label", { className: cn("text-body3-regular text-right"), children: label3 ? label3 : "-" })
878
+ ]
879
+ }
880
+ );
881
+ }
882
+
883
+ // src/components/Modal/Modal.tsx
884
+ var import_jsx_runtime24 = require("react/jsx-runtime");
885
+ var ModalBase = ({ isVisible, header, children, className }) => {
886
+ if (!isVisible) return null;
887
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("fixed inset-0 z-50 pointer-events-auto"), children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
888
+ "div",
889
+ {
890
+ className: cn("fixed inset-0 z-1001 flex items-center justify-center pointer-events-auto"),
891
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
892
+ "main",
893
+ {
894
+ className: cn(
895
+ "w-110 pt-8 h-fit",
896
+ "flex flex-col items-center",
897
+ "border border-gray02",
898
+ "bg-white shadow-[0 0 30px 0 rgba(0, 0, 0, 0.08);]",
899
+ className
900
+ ),
901
+ children: [
902
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h1", { className: cn("text-strong font-[500] text-[24px] leading-[32px]"), children: header }),
903
+ children
904
+ ]
905
+ }
906
+ )
907
+ }
908
+ ) });
909
+ };
910
+ var Contents = ({ children, className }) => {
911
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
912
+ "div",
913
+ {
914
+ className: cn(
915
+ "flex flex-col items-center gap-8",
916
+ "w-full h-fit",
917
+ "px-14 pt-8 pb-9",
918
+ className
919
+ ),
920
+ children
921
+ }
922
+ );
923
+ };
924
+ var CTAs2 = ({ children, className, row = false }) => {
925
+ if (!children) return null;
926
+ return (
927
+ // CTA 버튼들을 중앙에 배치하는 예시 (우측 정렬이 필요하면 justify-end 사용)
928
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
929
+ "div",
930
+ {
931
+ className: cn(
932
+ "flex flex-col w-full h-fit",
933
+ row && "flex-row divide-x divide-gray02",
934
+ className
935
+ ),
936
+ children
937
+ }
938
+ )
939
+ );
940
+ };
941
+ var Modal = Object.assign(ModalBase, {
942
+ Contents,
943
+ CTAs: CTAs2
944
+ });
945
+
946
+ // src/components/Modal/ModalText.tsx
947
+ var import_jsx_runtime25 = require("react/jsx-runtime");
948
+ var ModalText = ({ children, className }) => {
949
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
950
+ "div",
951
+ {
952
+ className: cn(
953
+ "flex flex-col items-center",
954
+ "mt-[-20px] mb-[-4px]",
955
+ ModalTextStyle,
956
+ "text-tertiary text-center",
957
+ className
958
+ ),
959
+ children
960
+ }
961
+ );
962
+ };
963
+
964
+ // src/components/createPost/CategorySegment.tsx
965
+ var import_jsx_runtime26 = require("react/jsx-runtime");
212
966
  var CategorySegment = ({
213
967
  selected = false,
214
968
  label,
215
969
  className,
216
970
  onClick
217
971
  }) => {
218
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
972
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
219
973
  "div",
220
974
  {
221
975
  className: cn(
@@ -235,7 +989,7 @@ var CategorySegment = ({
235
989
  };
236
990
 
237
991
  // src/components/createPost/CategorySegmentedControl.tsx
238
- var import_jsx_runtime5 = require("react/jsx-runtime");
992
+ var import_jsx_runtime27 = require("react/jsx-runtime");
239
993
  var categorySegments = {
240
994
  info: ["\uACF5\uC6A9 \uBB3C\uD488", "\uC77C\uBC18 \uAE30\uC790\uC7AC", "\uD2B9\uC218 \uAE30\uC790\uC7AC"],
241
995
  archive: ["\uD504\uB9B0\uD2B8\uC2E4", "\uD64D\uC775\uC2DC\uB514"],
@@ -249,8 +1003,8 @@ var CategorySegmentedControl = ({
249
1003
  className
250
1004
  }) => {
251
1005
  const segments = categorySegments[variable];
252
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("w-full h-fit flex items-center justify-start", className), children: [
253
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1006
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("w-full h-fit flex items-center justify-start", className), children: [
1007
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
254
1008
  "div",
255
1009
  {
256
1010
  className: cn(
@@ -263,7 +1017,7 @@ var CategorySegmentedControl = ({
263
1017
  children: variable === "infoTag" ? "\uD0DC\uADF8 \uC120\uD0DD" : "\uCE74\uD14C\uACE0\uB9AC \uC120\uD0DD"
264
1018
  }
265
1019
  ),
266
- segments.map((label) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1020
+ segments.map((label) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
267
1021
  CategorySegment,
268
1022
  {
269
1023
  label,
@@ -276,9 +1030,9 @@ var CategorySegmentedControl = ({
276
1030
  };
277
1031
 
278
1032
  // src/components/createPost/TitleInput.tsx
279
- var import_jsx_runtime6 = require("react/jsx-runtime");
1033
+ var import_jsx_runtime28 = require("react/jsx-runtime");
280
1034
  var TitleInput = ({ title, onChange, className }) => {
281
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1035
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
282
1036
  "input",
283
1037
  {
284
1038
  type: "text",
@@ -298,9 +1052,9 @@ var TitleInput = ({ title, onChange, className }) => {
298
1052
  };
299
1053
 
300
1054
  // src/components/notice/NoticeBadge.tsx
301
- var import_jsx_runtime7 = require("react/jsx-runtime");
1055
+ var import_jsx_runtime29 = require("react/jsx-runtime");
302
1056
  var NoticeBadge = ({ variable, className, onClick }) => {
303
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1057
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
304
1058
  "button",
305
1059
  {
306
1060
  type: "button",
@@ -321,7 +1075,7 @@ var NoticeBadge = ({ variable, className, onClick }) => {
321
1075
  };
322
1076
 
323
1077
  // src/components/notice/NoticeBar.tsx
324
- var import_jsx_runtime8 = require("react/jsx-runtime");
1078
+ var import_jsx_runtime30 = require("react/jsx-runtime");
325
1079
  var NoticeBar = ({
326
1080
  variable = "default",
327
1081
  badgeVariable,
@@ -330,7 +1084,7 @@ var NoticeBar = ({
330
1084
  className,
331
1085
  onClick
332
1086
  }) => {
333
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1087
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
334
1088
  "div",
335
1089
  {
336
1090
  className: cn(
@@ -342,20 +1096,20 @@ var NoticeBar = ({
342
1096
  ),
343
1097
  onClick,
344
1098
  children: [
345
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "flex items-center gap-3", children: [
346
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NoticeBadge, { variable: badgeVariable }),
347
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { className: "text-head1-medium text-strong", children: title })
1099
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("section", { className: "flex items-center gap-3", children: [
1100
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(NoticeBadge, { variable: badgeVariable }),
1101
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h1", { className: "text-head1-medium text-strong", children: title })
348
1102
  ] }),
349
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-body3-book text-tertiary", children: date })
1103
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-body3-book text-tertiary", children: date })
350
1104
  ]
351
1105
  }
352
1106
  );
353
1107
  };
354
1108
 
355
1109
  // src/components/SearchBar/SearchBar.tsx
356
- var import_jsx_runtime9 = require("react/jsx-runtime");
1110
+ var import_jsx_runtime31 = require("react/jsx-runtime");
357
1111
  var SearchBar = ({ value, onChange, className }) => {
358
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1112
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
359
1113
  "label",
360
1114
  {
361
1115
  className: cn(
@@ -366,8 +1120,8 @@ var SearchBar = ({ value, onChange, className }) => {
366
1120
  className
367
1121
  ),
368
1122
  children: [
369
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IconSearch, {}),
370
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1123
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(IconSearch, {}),
1124
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
371
1125
  "input",
372
1126
  {
373
1127
  type: "text",
@@ -386,7 +1140,22 @@ var SearchBar = ({ value, onChange, className }) => {
386
1140
  };
387
1141
 
388
1142
  // src/components/SideRadioAccordion/SideRadioAccordion.tsx
389
- var import_react = require("react");
1143
+ var import_react6 = require("react");
1144
+
1145
+ // src/lib/useControllableState.ts
1146
+ var import_react5 = require("react");
1147
+ function useControllableState(controlled, defaultValue) {
1148
+ const isControlled = controlled !== void 0;
1149
+ const [uncontrolled, setUncontrolled] = (0, import_react5.useState)(defaultValue);
1150
+ const value = isControlled ? controlled : uncontrolled;
1151
+ const setValue = (0, import_react5.useCallback)(
1152
+ (next) => {
1153
+ if (!isControlled) setUncontrolled(next);
1154
+ },
1155
+ [isControlled]
1156
+ );
1157
+ return [value, setValue, isControlled];
1158
+ }
390
1159
 
391
1160
  // src/components/SideRadioAccordion/Exclamation.svg
392
1161
  var Exclamation_default = "./Exclamation-IUYZYYC4.svg";
@@ -401,7 +1170,7 @@ var RadioSelected_default = "./RadioSelected-DOLVV7ZM.svg";
401
1170
  var RadioUnselected_default = "./RadioUnselected-QN74EU46.svg";
402
1171
 
403
1172
  // src/components/SideRadioAccordion/SideRadioAccordion.tsx
404
- var import_jsx_runtime10 = require("react/jsx-runtime");
1173
+ var import_jsx_runtime32 = require("react/jsx-runtime");
405
1174
  var printColorBgClass = {
406
1175
  Plotter: "bg-print-plotter",
407
1176
  Printer: "bg-print-printer",
@@ -411,72 +1180,93 @@ var printColorBgClass = {
411
1180
  "Seet Cutting": "bg-print-seet-cutting",
412
1181
  "Laser Cutting": "bg-print-laser-cutting"
413
1182
  };
414
- var SideRadioAccordion = ({
415
- step,
416
- title,
417
- radioToggleList,
418
- defaultSelected,
419
- onSelect,
420
- quantityLimitLabel,
421
- onClickQuantityLimit,
422
- className
423
- }) => {
424
- const [isOpen, setIsOpen] = (0, import_react.useState)(true);
425
- const [selected, setSelected] = (0, import_react.useState)(defaultSelected);
426
- const handleSelect = (itemTitle) => {
427
- setSelected(itemTitle);
428
- onSelect?.(itemTitle);
429
- };
430
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("w-full", className), children: [
431
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
432
- Stepper,
433
- {
434
- step,
435
- title,
436
- isOpen,
437
- onClick: () => setIsOpen((prev) => !prev)
438
- }
439
- ),
440
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col", children: [
441
- radioToggleList.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
442
- RadioToggleItem,
443
- {
444
- ...item,
445
- open: selected === item.title,
446
- onSelect: () => handleSelect(item.title)
447
- },
448
- item.title
449
- )),
450
- quantityLimitLabel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
451
- TextButton,
1183
+ var itemKey = (item) => item.value ?? item.title;
1184
+ var SideRadioAccordion = (0, import_react6.forwardRef)(
1185
+ function SideRadioAccordion2({
1186
+ step,
1187
+ title,
1188
+ radioToggleList,
1189
+ selected,
1190
+ defaultSelected,
1191
+ onSelect,
1192
+ stickyHeader = false,
1193
+ collapsible = true,
1194
+ defaultOpen = true,
1195
+ open,
1196
+ onOpenChange,
1197
+ quantityLimitLabel,
1198
+ onClickQuantityLimit,
1199
+ className
1200
+ }, ref) {
1201
+ const [selectedValue, setSelectedValue] = useControllableState(
1202
+ selected,
1203
+ defaultSelected ?? null
1204
+ );
1205
+ const [isOpen, setIsOpen] = useControllableState(open, defaultOpen);
1206
+ const expanded = collapsible ? isOpen : true;
1207
+ const handleSelect = (value) => {
1208
+ setSelectedValue(value);
1209
+ onSelect?.(value);
1210
+ };
1211
+ const toggleOpen = () => {
1212
+ if (!collapsible) return;
1213
+ const next = !isOpen;
1214
+ setIsOpen(next);
1215
+ onOpenChange?.(next);
1216
+ };
1217
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { ref, className: cn("w-full", className), children: [
1218
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1219
+ Stepper,
452
1220
  {
453
- label: quantityLimitLabel,
454
- onClick: onClickQuantityLimit,
455
- className: "mt-2"
1221
+ step,
1222
+ title,
1223
+ isOpen: expanded,
1224
+ collapsible,
1225
+ onClick: toggleOpen,
1226
+ className: cn(stickyHeader && "sticky top-0 z-10")
456
1227
  }
457
- )
458
- ] })
1228
+ ),
1229
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col", children: [
1230
+ radioToggleList.map((item) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1231
+ RadioToggleItem,
1232
+ {
1233
+ ...item,
1234
+ open: selectedValue === itemKey(item),
1235
+ onSelect: () => handleSelect(itemKey(item))
1236
+ },
1237
+ itemKey(item)
1238
+ )),
1239
+ quantityLimitLabel && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1240
+ TextButton2,
1241
+ {
1242
+ label: quantityLimitLabel,
1243
+ onClick: onClickQuantityLimit,
1244
+ className: "mt-2"
1245
+ }
1246
+ )
1247
+ ] })
1248
+ ] });
1249
+ }
1250
+ );
1251
+ var Stepper = ({ step, title, isOpen, collapsible, onClick, className }) => {
1252
+ const label = /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { children: [
1253
+ step,
1254
+ " | ",
1255
+ title
459
1256
  ] });
460
- };
461
- var Stepper = ({ step, title, isOpen, onClick, className }) => {
462
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1257
+ const base = "w-full flex items-center justify-between px-3 py-2.5 text-body1-medium text-strong text-left bg-white";
1258
+ if (!collapsible) {
1259
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cn(base, className), children: label });
1260
+ }
1261
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
463
1262
  "button",
464
1263
  {
465
1264
  type: "button",
466
1265
  onClick,
467
- className: cn(
468
- "w-full flex items-center justify-between px-3 py-2.5",
469
- "text-body1-medium text-strong text-left cursor-pointer",
470
- "bg-white hover:bg-gray00 active:bg-gray01",
471
- className
472
- ),
1266
+ className: cn(base, "cursor-pointer hover:bg-gray00 active:bg-gray01", className),
473
1267
  children: [
474
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
475
- step,
476
- " | ",
477
- title
478
- ] }),
479
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1268
+ label,
1269
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
480
1270
  "img",
481
1271
  {
482
1272
  src: ChevronDown_default,
@@ -493,34 +1283,39 @@ var RadioToggleItem = ({
493
1283
  content,
494
1284
  badges,
495
1285
  printColor,
1286
+ color,
1287
+ className,
496
1288
  labelTextButton,
497
1289
  onClickTextButton,
498
1290
  open,
499
- onSelect,
500
- className
1291
+ onSelect
501
1292
  }) => {
502
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("w-full", className), children: [
503
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1293
+ const rowBg = !open ? "bg-white hover:bg-gray00" : color ? void 0 : printColor ? printColorBgClass[printColor] : "bg-gray00";
1294
+ const style = open && color ? { backgroundColor: color } : void 0;
1295
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "w-full", children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
504
1297
  "button",
505
1298
  {
506
1299
  type: "button",
507
1300
  onClick: onSelect,
1301
+ style,
508
1302
  className: cn(
509
1303
  "w-full flex items-center gap-1.5 px-3 py-2.5",
510
1304
  "text-body2-regular text-primary text-left cursor-pointer",
511
- open ? printColor ? printColorBgClass[printColor] : "bg-gray00" : "bg-white hover:bg-gray00"
1305
+ rowBg,
1306
+ className
512
1307
  ),
513
1308
  children: [
514
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Radio, { selected: open }),
515
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: title })
1309
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Radio, { selected: open }),
1310
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { children: title })
516
1311
  ]
517
1312
  }
518
1313
  ),
519
- open && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2 px-3 py-3 bg-white", children: [
520
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-caption-regular text-secondary", children: content }),
521
- badges && badges.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex gap-1", children: badges.map((badge) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { label: badge }, badge)) }),
522
- labelTextButton && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
523
- TextButton,
1314
+ open && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-2 px-3 py-3 bg-white", children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-caption-regular text-secondary", children: content }),
1316
+ badges && badges.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex gap-1", children: badges.map((badge) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Badge, { label: badge }, badge)) }),
1317
+ labelTextButton && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1318
+ TextButton2,
524
1319
  {
525
1320
  label: labelTextButton,
526
1321
  onClick: onClickTextButton,
@@ -532,7 +1327,7 @@ var RadioToggleItem = ({
532
1327
  ] });
533
1328
  };
534
1329
  var Radio = ({ selected = false, className }) => {
535
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1330
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
536
1331
  "img",
537
1332
  {
538
1333
  src: selected ? RadioSelected_default : RadioUnselected_default,
@@ -541,8 +1336,8 @@ var Radio = ({ selected = false, className }) => {
541
1336
  }
542
1337
  );
543
1338
  };
544
- var TextButton = ({ label, onClick, showIcon = false, className }) => {
545
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1339
+ var TextButton2 = ({ label, onClick, showIcon = false, className }) => {
1340
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
546
1341
  "button",
547
1342
  {
548
1343
  type: "button",
@@ -555,14 +1350,14 @@ var TextButton = ({ label, onClick, showIcon = false, className }) => {
555
1350
  className
556
1351
  ),
557
1352
  children: [
558
- showIcon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { src: Exclamation_default, alt: "" }),
1353
+ showIcon && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("img", { src: Exclamation_default, alt: "" }),
559
1354
  label
560
1355
  ]
561
1356
  }
562
1357
  );
563
1358
  };
564
1359
  var Badge = ({ label, className }) => {
565
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1360
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
566
1361
  "div",
567
1362
  {
568
1363
  className: cn(
@@ -576,7 +1371,7 @@ var Badge = ({ label, className }) => {
576
1371
  };
577
1372
 
578
1373
  // src/components/Tag/Tag.tsx
579
- var import_jsx_runtime11 = require("react/jsx-runtime");
1374
+ var import_jsx_runtime33 = require("react/jsx-runtime");
580
1375
  var Tag = ({
581
1376
  className,
582
1377
  children,
@@ -586,7 +1381,7 @@ var Tag = ({
586
1381
  onClick,
587
1382
  ...props
588
1383
  }) => {
589
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1384
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
590
1385
  "button",
591
1386
  {
592
1387
  className: cn(
@@ -596,7 +1391,7 @@ var Tag = ({
596
1391
  "box-border border border-gray02",
597
1392
  "bg-white",
598
1393
  // bgColor && `bg-[${bgColor}] border-none`,
599
- onClick && "cursor-pointer hover:bg-gray00 active:bg-gray01 cursor-pointer",
1394
+ onClick && "cursor-pointer hover:bg-gray00 active:bg-gray01 cursor-pointer has-clickable",
600
1395
  selected && "text-white bg-black border-black hover:bg-black active:bg-black",
601
1396
  "disabled:text-gray04 disabled:bg-gray02 disabled:border-gray02 disabled:cursor-not-allowed",
602
1397
  className
@@ -605,11 +1400,17 @@ var Tag = ({
605
1400
  disabled,
606
1401
  style: bgColor ? { backgroundColor: bgColor, border: "none" } : void 0,
607
1402
  ...props,
608
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children })
1403
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { children })
609
1404
  }
610
1405
  );
611
1406
  };
612
1407
 
1408
+ // src/components/Timetable/Timetable.tsx
1409
+ var import_react8 = require("react");
1410
+
1411
+ // src/components/Timetable/WeekNavigation.tsx
1412
+ var import_react7 = require("react");
1413
+
613
1414
  // src/components/Timetable/dateUtils.ts
614
1415
  var KOREAN_WEEKDAYS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
615
1416
  var parseISODate = (date) => {
@@ -641,49 +1442,42 @@ var formatWeekRange = (weekStart) => {
641
1442
  };
642
1443
 
643
1444
  // src/components/Timetable/WeekNavigation.tsx
644
- var import_jsx_runtime12 = require("react/jsx-runtime");
1445
+ var import_jsx_runtime34 = require("react/jsx-runtime");
645
1446
  var weekLabel = {
646
1447
  thisWeek: "\uC774\uBC88 \uC8FC",
647
1448
  nextWeek: "\uB2E4\uC74C \uC8FC",
648
1449
  twoWeeksLater: "\uB2E4\uB2E4\uC74C \uC8FC"
649
1450
  };
650
- var WeekNavigation = ({
651
- date,
652
- week,
653
- onPrevWeek,
654
- onNextWeek,
655
- className
656
- }) => {
657
- const showPrev = week !== "thisWeek";
658
- const showNext = week !== "twoWeeksLater";
659
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
660
- "div",
661
- {
662
- className: cn(
663
- "flex items-center justify-between",
664
- "w-full h-11 px-3",
665
- "bg-white border border-gray02",
666
- "text-body2-regular text-primary",
667
- className
668
- ),
669
- children: [
670
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ArrowButton, { direction: "left", visible: showPrev, onClick: onPrevWeek }),
671
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
672
- weekLabel[week],
673
- " (",
674
- formatWeekRange(date),
675
- ")"
676
- ] }),
677
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ArrowButton, { direction: "right", visible: showNext, onClick: onNextWeek })
678
- ]
679
- }
680
- );
681
- };
1451
+ var WeekNavigation = (0, import_react7.forwardRef)(
1452
+ function WeekNavigation2({ date, week, label, canPrev, canNext, onPrevWeek, onNextWeek, className }, ref) {
1453
+ const showPrev = canPrev ?? (week ? week !== "thisWeek" : true);
1454
+ const showNext = canNext ?? (week ? week !== "twoWeeksLater" : true);
1455
+ const text = label ?? (week ? `${weekLabel[week]} (${formatWeekRange(date)})` : formatWeekRange(date));
1456
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
1457
+ "div",
1458
+ {
1459
+ ref,
1460
+ className: cn(
1461
+ "flex items-center justify-between",
1462
+ "w-full h-11 px-3",
1463
+ "bg-white border border-gray02",
1464
+ "text-body2-regular text-primary",
1465
+ className
1466
+ ),
1467
+ children: [
1468
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArrowButton, { direction: "left", visible: showPrev, onClick: onPrevWeek }),
1469
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: text }),
1470
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArrowButton, { direction: "right", visible: showNext, onClick: onNextWeek })
1471
+ ]
1472
+ }
1473
+ );
1474
+ }
1475
+ );
682
1476
  var ArrowButton = ({ direction, visible, onClick }) => {
683
1477
  if (!visible) {
684
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-6 h-6" });
1478
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "w-6 h-6" });
685
1479
  }
686
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1480
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
687
1481
  "button",
688
1482
  {
689
1483
  type: "button",
@@ -693,7 +1487,7 @@ var ArrowButton = ({ direction, visible, onClick }) => {
693
1487
  "text-gray06 cursor-pointer",
694
1488
  "hover:bg-gray00 active:bg-gray01"
695
1489
  ),
696
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1490
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
697
1491
  IconChevronDownXS,
698
1492
  {
699
1493
  className: direction === "left" ? "rotate-90" : "-rotate-90",
@@ -705,7 +1499,7 @@ var ArrowButton = ({ direction, visible, onClick }) => {
705
1499
  };
706
1500
 
707
1501
  // src/components/Timetable/Timetable.tsx
708
- var import_jsx_runtime13 = require("react/jsx-runtime");
1502
+ var import_jsx_runtime35 = require("react/jsx-runtime");
709
1503
  var printColorHoverClass = {
710
1504
  Plotter: "hover:bg-print-plotter",
711
1505
  Printer: "hover:bg-print-printer",
@@ -715,66 +1509,87 @@ var printColorHoverClass = {
715
1509
  "Seet Cutting": "hover:bg-print-seet-cutting",
716
1510
  "Laser Cutting": "hover:bg-print-laser-cutting"
717
1511
  };
718
- var Timetable = ({
1512
+ var Timetable = (0, import_react8.forwardRef)(function Timetable2({
719
1513
  weekStart,
720
1514
  week,
721
1515
  days,
722
1516
  onPrevWeek,
723
1517
  onNextWeek,
724
1518
  onSelectSlot,
1519
+ fluid = false,
1520
+ hideWeekNavigation = false,
1521
+ canPrevWeek,
1522
+ canNextWeek,
725
1523
  className
726
- }) => {
727
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn(className), children: [
728
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1524
+ }, ref) {
1525
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { ref, className: cn(fluid && "w-full", className), children: [
1526
+ !hideWeekNavigation && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
729
1527
  WeekNavigation,
730
1528
  {
731
1529
  date: weekStart,
732
1530
  week,
1531
+ canPrev: canPrevWeek,
1532
+ canNext: canNextWeek,
733
1533
  onPrevWeek,
734
1534
  onNextWeek
735
1535
  }
736
1536
  ),
737
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex", children: days.map((day) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1537
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: cn("flex", fluid && "w-full"), children: days.map((day) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
738
1538
  DailyTimetable,
739
1539
  {
740
1540
  date: day.date,
741
1541
  slots: day.slots,
1542
+ fluid,
742
1543
  onSelectTime: onSelectSlot ? (time) => onSelectSlot(day.date, time) : void 0
743
1544
  },
744
1545
  day.date
745
1546
  )) })
746
1547
  ] });
747
- };
748
- var DailyTimetable = ({ date, slots, onSelectTime, className }) => {
749
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("flex flex-col", className), children: [
750
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "h-13 flex items-center justify-center text-body3-medium text-secondary", children: formatDayLabel(date) }),
751
- slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1548
+ });
1549
+ var DailyTimetable = ({ date, slots, fluid, onSelectTime, className }) => {
1550
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cn("flex flex-col", fluid ? "flex-1 min-w-0" : "w-18", className), children: [
1551
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "h-13 flex items-center justify-center text-body3-medium text-secondary", children: formatDayLabel(date) }),
1552
+ slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
752
1553
  TimeItem,
753
1554
  {
754
1555
  state: slot.state,
755
1556
  label: slot.time,
756
1557
  printColor: slot.printColor,
1558
+ color: slot.color,
1559
+ fluid,
1560
+ className: slot.className,
757
1561
  onClick: onSelectTime ? () => onSelectTime(slot.time) : void 0
758
1562
  },
759
1563
  slot.time
760
1564
  ))
761
1565
  ] });
762
1566
  };
763
- var TimeItem = ({ state, label, printColor, onClick, className }) => {
1567
+ var TimeItem = ({
1568
+ state,
1569
+ label,
1570
+ printColor,
1571
+ color,
1572
+ fluid,
1573
+ onClick,
1574
+ className
1575
+ }) => {
764
1576
  const clickable = (state === "default" || state === "selected") && !!onClick;
765
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1577
+ const style = state === "default" && color ? { "--tt-hover": color } : void 0;
1578
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
766
1579
  "div",
767
1580
  {
768
1581
  onClick: clickable ? onClick : void 0,
1582
+ style,
769
1583
  className: cn(
770
- "w-18 h-18 flex items-center justify-center",
1584
+ "h-18 flex items-center justify-center",
1585
+ fluid ? "w-full" : "w-18",
771
1586
  "text-body1-medium text-center bg-white text-primary",
772
1587
  state === "none" && "bg-gray00 text-transparent",
773
1588
  state === "selected" && "bg-black text-gray02 hover:cursor-pointer",
774
1589
  state === "disabled" && "bg-gray00 text-gray03",
775
1590
  state === "default" && [
776
1591
  "hover:cursor-pointer",
777
- printColor ? printColorHoverClass[printColor] : "hover:bg-gray00"
1592
+ color ? "hover:bg-(--tt-hover)" : printColor ? printColorHoverClass[printColor] : "hover:bg-gray00"
778
1593
  ],
779
1594
  className
780
1595
  ),
@@ -784,81 +1599,177 @@ var TimeItem = ({ state, label, printColor, onClick, className }) => {
784
1599
  };
785
1600
 
786
1601
  // src/components/TNB/TNB.tsx
787
- var import_react2 = require("react");
788
- var import_jsx_runtime14 = require("react/jsx-runtime");
789
- var TNB = ({
1602
+ var import_react9 = require("react");
1603
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1604
+ function useControllableState2(controlled, defaultValue) {
1605
+ const isControlled = controlled !== void 0;
1606
+ const [uncontrolled, setUncontrolled] = (0, import_react9.useState)(defaultValue);
1607
+ const value = isControlled ? controlled : uncontrolled;
1608
+ const setValue = (0, import_react9.useCallback)(
1609
+ (next) => {
1610
+ if (!isControlled) setUncontrolled(next);
1611
+ },
1612
+ [isControlled]
1613
+ );
1614
+ return [value, setValue, isControlled];
1615
+ }
1616
+ var TNB = (0, import_react9.forwardRef)(function TNB2({
790
1617
  logo,
791
1618
  upperPartItemList,
792
1619
  upperPartIconList,
1620
+ selected,
793
1621
  defaultSelected,
1622
+ onSelectedChange,
1623
+ selectedLower,
1624
+ defaultSelectedLower,
1625
+ onSelectedLowerChange,
794
1626
  onSelectItem,
1627
+ linkComponent,
1628
+ "aria-label": ariaLabel = "\uC8FC\uC694 \uBA54\uB274",
795
1629
  className
796
- }) => {
797
- const [selected, setSelected] = (0, import_react2.useState)(defaultSelected ?? upperPartItemList[0]?.label);
798
- const [selectedLower, setSelectedLower] = (0, import_react2.useState)(
799
- upperPartItemList.find((item) => item.label === selected)?.lowerPartItemList[0]?.label
1630
+ }, ref) {
1631
+ const [upperValue, setUpperValue] = useControllableState2(
1632
+ selected,
1633
+ defaultSelected ?? null
1634
+ );
1635
+ const [lowerValueRaw, setLowerValue, lowerIsControlled] = useControllableState2(
1636
+ selectedLower,
1637
+ defaultSelectedLower ?? null
800
1638
  );
1639
+ const currentUpper = upperPartItemList.find((item) => item.label === upperValue);
1640
+ const currentLowerItemList = currentUpper?.lowerPartItemList ?? [];
1641
+ const lowerValue = lowerIsControlled ? lowerValueRaw ?? void 0 : currentLowerItemList.some((item) => item.label === lowerValueRaw) ? lowerValueRaw : currentLowerItemList[0]?.label ?? void 0;
801
1642
  const handleSelectUpper = (label) => {
802
- setSelected(label);
803
- setSelectedLower(
804
- upperPartItemList.find((item) => item.label === label)?.lowerPartItemList[0]?.label
805
- );
1643
+ setUpperValue(label);
1644
+ if (!lowerIsControlled) {
1645
+ const nextLower = upperPartItemList.find((item) => item.label === label)?.lowerPartItemList[0]?.label ?? null;
1646
+ setLowerValue(nextLower);
1647
+ }
1648
+ onSelectedChange?.(label);
806
1649
  onSelectItem?.(label);
807
1650
  };
808
- const currentLowerItemList = upperPartItemList.find((item) => item.label === selected)?.lowerPartItemList ?? [];
809
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("nav", { className: cn("w-full bg-white", className), children: [
810
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1651
+ const handleSelectLower = (label) => {
1652
+ setLowerValue(label);
1653
+ onSelectedLowerChange?.(label);
1654
+ };
1655
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("nav", { ref, "aria-label": ariaLabel, className: cn("w-full bg-white", className), children: [
1656
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
811
1657
  UpperPart,
812
1658
  {
813
1659
  logo,
814
1660
  upperPartItemList,
815
1661
  upperPartIconList,
816
- selected,
817
- onSelectItem: handleSelectUpper
1662
+ selected: upperValue ?? void 0,
1663
+ onSelectItem: handleSelectUpper,
1664
+ linkComponent
818
1665
  }
819
1666
  ),
820
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1667
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
821
1668
  LowerPart,
822
1669
  {
823
1670
  lowerPartItemList: currentLowerItemList,
824
- selected: selectedLower,
825
- onSelectItem: setSelectedLower
1671
+ selected: lowerValue,
1672
+ onSelectItem: handleSelectLower,
1673
+ linkComponent
826
1674
  }
827
1675
  )
828
1676
  ] });
829
- };
1677
+ });
1678
+ var NavClickable = (0, import_react9.forwardRef)(function NavClickable2({ href, onClick, linkComponent: Link, current, disabled, className, children }, ref) {
1679
+ const ariaCurrent = current ? "page" : void 0;
1680
+ if (href && !disabled) {
1681
+ if (Link) {
1682
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1683
+ Link,
1684
+ {
1685
+ href,
1686
+ onClick,
1687
+ "aria-current": ariaCurrent,
1688
+ className,
1689
+ ref,
1690
+ children
1691
+ }
1692
+ );
1693
+ }
1694
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1695
+ "a",
1696
+ {
1697
+ href,
1698
+ onClick,
1699
+ "aria-current": ariaCurrent,
1700
+ className,
1701
+ ref,
1702
+ children
1703
+ }
1704
+ );
1705
+ }
1706
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1707
+ "button",
1708
+ {
1709
+ type: "button",
1710
+ onClick,
1711
+ disabled,
1712
+ "aria-current": ariaCurrent,
1713
+ className,
1714
+ ref,
1715
+ children
1716
+ }
1717
+ );
1718
+ });
830
1719
  var UpperPart = ({
831
1720
  logo,
832
1721
  upperPartItemList,
833
1722
  upperPartIconList,
834
1723
  selected,
835
1724
  onSelectItem,
1725
+ linkComponent,
836
1726
  className
837
1727
  }) => {
838
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn("w-full h-13 px-4 flex items-center justify-between", className), children: [
839
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-6", children: [
840
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(NavIconButton, { ...logo }),
841
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center gap-4", children: upperPartItemList.map((item) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1728
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cn("w-full h-13 px-4 flex items-center justify-between", className), children: [
1729
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-6", children: [
1730
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(NavIconButton, { ...logo, linkComponent }),
1731
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex items-center gap-4", children: upperPartItemList.map((item) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
842
1732
  UpperPartTab,
843
1733
  {
844
1734
  label: item.label,
1735
+ href: item.href,
845
1736
  selected: item.label === selected,
846
- onClick: () => onSelectItem(item.label)
1737
+ linkComponent,
1738
+ onClick: () => {
1739
+ item.onClick?.();
1740
+ onSelectItem(item.label);
1741
+ }
847
1742
  },
848
- item.label
1743
+ item.id ?? item.label
849
1744
  )) })
850
1745
  ] }),
851
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center gap-2", children: upperPartIconList.map((icon) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(NavIconButton, { ...icon }, icon.src)) })
1746
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex items-center gap-2", children: upperPartIconList.map((icon, index) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1747
+ NavIconButton,
1748
+ {
1749
+ ...icon,
1750
+ linkComponent
1751
+ },
1752
+ icon.id ?? icon.src ?? icon.label ?? index
1753
+ )) })
852
1754
  ] });
853
1755
  };
854
- var UpperPartTab = ({ label, selected, onClick, className }) => {
855
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
856
- "button",
1756
+ var UpperPartTab = ({
1757
+ label,
1758
+ href,
1759
+ selected,
1760
+ onClick,
1761
+ linkComponent,
1762
+ className
1763
+ }) => {
1764
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1765
+ NavClickable,
857
1766
  {
858
- type: "button",
1767
+ href,
859
1768
  onClick,
1769
+ linkComponent,
1770
+ current: selected,
860
1771
  className: cn(
861
- "px-1 py-1.5 text-body2-regular cursor-pointer",
1772
+ "inline-flex px-1 py-1.5 text-body2-regular cursor-pointer no-underline",
862
1773
  selected ? "text-strong font-medium underline underline-offset-4" : "text-quaternary hover:text-secondary",
863
1774
  className
864
1775
  ),
@@ -866,54 +1777,92 @@ var UpperPartTab = ({ label, selected, onClick, className }) => {
866
1777
  }
867
1778
  );
868
1779
  };
869
- var NavIconButton = ({ src, alt, onClick, selected, className }) => {
870
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
871
- "button",
872
- {
873
- type: "button",
874
- onClick,
875
- disabled: !onClick,
876
- className: cn(
877
- "w-8 h-8 flex items-center justify-center rounded-full",
878
- onClick && "cursor-pointer",
879
- selected ? "bg-black" : "bg-transparent hover:bg-gray00 active:bg-gray01",
880
- className
881
- ),
882
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("img", { src, alt: alt ?? "", className: "w-5 h-5" })
883
- }
884
- );
885
- };
886
- var LowerPart = ({ lowerPartItemList, selected, onSelectItem, className }) => {
1780
+ var NavIconButton = (0, import_react9.forwardRef)(
1781
+ function NavIconButton2({ src, icon, alt, label, href, onClick, selected, badge, className, linkComponent }, forwardedRef) {
1782
+ const interactive = Boolean(onClick || href);
1783
+ const showBadge = badge !== void 0 && badge !== null && badge !== false;
1784
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
1785
+ NavClickable,
1786
+ {
1787
+ ref: forwardedRef,
1788
+ href,
1789
+ onClick,
1790
+ linkComponent,
1791
+ current: selected,
1792
+ disabled: !interactive,
1793
+ className: cn(
1794
+ "relative inline-flex h-8 min-w-8 items-center justify-center gap-1.5 rounded-full px-1.5",
1795
+ interactive && "cursor-pointer",
1796
+ selected ? "bg-black text-white" : "bg-transparent hover:bg-gray00 active:bg-gray01",
1797
+ className
1798
+ ),
1799
+ children: [
1800
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "inline-flex h-5 w-5 items-center justify-center", children: icon }) : src ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("img", { src, alt: alt ?? label ?? "", className: "h-5 w-5" }) : null,
1801
+ label ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: cn("text-body2-regular", selected ? "text-white" : "text-secondary"), children: label }) : null,
1802
+ showBadge ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1803
+ "span",
1804
+ {
1805
+ "aria-hidden": badge === true,
1806
+ className: cn(
1807
+ "absolute -right-0.5 -top-0.5 flex items-center justify-center rounded-full bg-danger text-white",
1808
+ badge === true ? "h-2 w-2" : "h-4 min-w-4 px-1 text-caption-regular leading-none"
1809
+ ),
1810
+ children: badge === true ? null : badge
1811
+ }
1812
+ ) : null
1813
+ ]
1814
+ }
1815
+ );
1816
+ }
1817
+ );
1818
+ var LowerPart = ({
1819
+ lowerPartItemList,
1820
+ selected,
1821
+ onSelectItem,
1822
+ linkComponent,
1823
+ className
1824
+ }) => {
887
1825
  if (lowerPartItemList.length === 0) {
888
1826
  return null;
889
1827
  }
890
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1828
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
891
1829
  "div",
892
1830
  {
893
1831
  className: cn("w-full h-9 px-4 flex items-center gap-4 border-t border-gray02", className),
894
- children: lowerPartItemList.map((item) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1832
+ children: lowerPartItemList.map((item) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
895
1833
  LowerPartItemButton,
896
1834
  {
897
1835
  label: item.label,
1836
+ href: item.href,
898
1837
  selected: item.label === selected,
1838
+ linkComponent,
899
1839
  onClick: () => {
900
1840
  item.onClick?.();
901
1841
  onSelectItem(item.label);
902
1842
  }
903
1843
  },
904
- item.label
1844
+ item.id ?? item.label
905
1845
  ))
906
1846
  }
907
1847
  );
908
1848
  };
909
- var LowerPartItemButton = ({ label, selected, onClick, className }) => {
910
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
911
- "button",
1849
+ var LowerPartItemButton = ({
1850
+ label,
1851
+ href,
1852
+ selected,
1853
+ onClick,
1854
+ linkComponent,
1855
+ className
1856
+ }) => {
1857
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1858
+ NavClickable,
912
1859
  {
913
- type: "button",
1860
+ href,
914
1861
  onClick,
1862
+ linkComponent,
1863
+ current: selected,
915
1864
  className: cn(
916
- "px-1.5 py-1 text-caption-regular cursor-pointer",
1865
+ "inline-flex px-1.5 py-1 text-caption-regular cursor-pointer no-underline",
917
1866
  "hover:bg-gray00 active:bg-gray01",
918
1867
  selected ? "text-strong font-medium" : "text-quaternary",
919
1868
  className
@@ -924,36 +1873,102 @@ var LowerPartItemButton = ({ label, selected, onClick, className }) => {
924
1873
  };
925
1874
 
926
1875
  // src/components/Toast/Toast.tsx
927
- var import_jsx_runtime15 = require("react/jsx-runtime");
928
- var Toast = ({ message, className }) => {
929
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1876
+ var import_react10 = require("react");
1877
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1878
+ var EXIT_MS = 300;
1879
+ var Toast = (0, import_react10.forwardRef)(function Toast2({ message, open, duration = 3e3, onClose, position = "none", className }, ref) {
1880
+ const controlled = open !== void 0;
1881
+ const [mounted, setMounted] = (0, import_react10.useState)(!controlled || open);
1882
+ const [visible, setVisible] = (0, import_react10.useState)(!controlled || open);
1883
+ (0, import_react10.useEffect)(() => {
1884
+ if (!controlled) return;
1885
+ if (open) {
1886
+ setMounted(true);
1887
+ const raf = requestAnimationFrame(() => setVisible(true));
1888
+ return () => cancelAnimationFrame(raf);
1889
+ }
1890
+ setVisible(false);
1891
+ const timer = setTimeout(() => setMounted(false), EXIT_MS);
1892
+ return () => clearTimeout(timer);
1893
+ }, [controlled, open]);
1894
+ (0, import_react10.useEffect)(() => {
1895
+ if (!controlled || !open || !duration) return;
1896
+ const timer = setTimeout(() => onClose?.(), duration);
1897
+ return () => clearTimeout(timer);
1898
+ }, [controlled, open, duration, onClose]);
1899
+ if (!mounted) return null;
1900
+ const banner = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
930
1901
  "div",
931
1902
  {
1903
+ ref,
1904
+ role: "status",
1905
+ "aria-live": "polite",
932
1906
  className: cn(
933
- `bg-gray06 w-dvw h-fit py-2.5`,
934
- `font-pretendard text-white text-center font-semibold text-[17px]`,
935
- `shadow-[0_2px_15px_rgba(0,0,0,0.18)]`,
1907
+ "w-full h-fit py-2.5 bg-gray06",
1908
+ "font-pretendard text-white text-center font-semibold text-[17px]",
1909
+ "shadow-[0_2px_15px_rgba(0,0,0,0.18)]",
1910
+ controlled && "transition-opacity duration-300",
1911
+ controlled && (visible ? "opacity-100" : "opacity-0"),
936
1912
  className
937
1913
  ),
938
1914
  children: message
939
1915
  }
940
1916
  );
941
- };
1917
+ if (position === "none") return banner;
1918
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1919
+ "div",
1920
+ {
1921
+ className: cn(
1922
+ "fixed inset-x-0 z-5000 pointer-events-none",
1923
+ position === "top" ? "top-0" : "bottom-0"
1924
+ ),
1925
+ children: banner
1926
+ }
1927
+ );
1928
+ });
942
1929
  // Annotate the CommonJS export names for ESM import in node:
943
1930
  0 && (module.exports = {
944
1931
  BackButton,
1932
+ BasicFormField,
945
1933
  CTA,
946
1934
  CategorySegment,
947
1935
  CategorySegmentedControl,
1936
+ Column,
1937
+ ColumnContentContainer,
1938
+ ColumnLabel,
1939
+ ColumnSubTitle,
1940
+ ColumnText,
1941
+ ColumnTextStyle,
1942
+ Dropdown,
1943
+ FormCheckBox,
1944
+ FormColor,
1945
+ FormFile,
1946
+ FormNumber,
1947
+ FormPassword,
1948
+ FormSelectBox,
1949
+ FormTel,
1950
+ FormText,
1951
+ FormTextArea,
1952
+ IconCheck,
1953
+ IconChevronDownXS,
1954
+ IconInfo,
1955
+ List,
1956
+ Modal,
1957
+ ModalText,
1958
+ ModalTextStyle,
1959
+ MultiColumn,
948
1960
  NoticeBadge,
949
1961
  NoticeBar,
950
1962
  SearchBar,
1963
+ SelectBoxOption,
951
1964
  SideRadioAccordion,
952
1965
  TNB,
953
1966
  Tag,
1967
+ TextButton,
954
1968
  Timetable,
955
1969
  TitleInput,
956
1970
  Toast,
957
- WeekNavigation
1971
+ WeekNavigation,
1972
+ getInputStyles
958
1973
  });
959
1974
  //# sourceMappingURL=index.js.map