@bigtablet/design-system 1.9.0 → 1.10.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.css CHANGED
@@ -76,7 +76,7 @@
76
76
  align-items: center;
77
77
  justify-content: center;
78
78
  z-index: 10000;
79
- animation: fade_in 0.2s ease-out;
79
+ animation: fade_in 0.2s ease-in-out;
80
80
  }
81
81
  .alert_modal {
82
82
  background: #ffffff;
@@ -84,7 +84,7 @@
84
84
  min-width: 320px;
85
85
  max-width: 480px;
86
86
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
87
- animation: slide_up 0.3s ease-out;
87
+ animation: slide_up 0.3s ease-in-out;
88
88
  overflow: hidden;
89
89
  }
90
90
  .alert_modal_title {
@@ -126,10 +126,10 @@
126
126
  border: 1px solid transparent;
127
127
  line-height: 1.5;
128
128
  transition:
129
- background 0.2s ease-out,
130
- border-color 0.2s ease-out,
131
- box-shadow 0.2s ease-out,
132
- transform 0.2s ease-out;
129
+ background 0.2s ease-in-out,
130
+ border-color 0.2s ease-in-out,
131
+ box-shadow 0.2s ease-in-out,
132
+ transform 0.2s ease-in-out;
133
133
  }
134
134
  .alert_modal_button:focus-visible {
135
135
  outline: none;
@@ -216,7 +216,6 @@
216
216
 
217
217
  /* src/ui/general/button/style.scss */
218
218
  .button {
219
- width: 100%;
220
219
  display: inline-flex;
221
220
  align-items: center;
222
221
  justify-content: center;
@@ -226,10 +225,10 @@
226
225
  cursor: pointer;
227
226
  font-weight: 500;
228
227
  transition:
229
- background 0.2s ease-out,
230
- color 0.2s ease-out,
231
- box-shadow 0.2s ease-out,
232
- transform 0.2s ease-out;
228
+ background 0.2s ease-in-out,
229
+ color 0.2s ease-in-out,
230
+ box-shadow 0.2s ease-in-out,
231
+ transform 0.2s ease-in-out;
233
232
  }
234
233
  .button:disabled {
235
234
  cursor: not-allowed;
@@ -318,9 +317,9 @@
318
317
  border-radius: 6px;
319
318
  background: #ffffff;
320
319
  transition:
321
- background 0.2s ease-out,
322
- border-color 0.2s ease-out,
323
- box-shadow 0.2s ease-out;
320
+ background 0.2s ease-in-out,
321
+ border-color 0.2s ease-in-out,
322
+ box-shadow 0.2s ease-in-out;
324
323
  display: inline-block;
325
324
  position: relative;
326
325
  }
@@ -408,9 +407,9 @@
408
407
  line-height: 1.5;
409
408
  color: #1a1a1a;
410
409
  transition:
411
- background 0.2s ease-out,
412
- border-color 0.2s ease-out,
413
- box-shadow 0.2s ease-out;
410
+ background 0.2s ease-in-out,
411
+ border-color 0.2s ease-in-out,
412
+ box-shadow 0.2s ease-in-out;
414
413
  }
415
414
  .file_input:hover .file_input_label {
416
415
  border-color: rgba(0, 0, 0, 0.08);
@@ -457,9 +456,9 @@
457
456
  border-radius: 9999px;
458
457
  background: #ffffff;
459
458
  transition:
460
- background 0.2s ease-out,
461
- border-color 0.2s ease-out,
462
- box-shadow 0.2s ease-out;
459
+ background 0.2s ease-in-out,
460
+ border-color 0.2s ease-in-out,
461
+ box-shadow 0.2s ease-in-out;
463
462
  position: relative;
464
463
  display: inline-block;
465
464
  }
@@ -538,9 +537,9 @@
538
537
  background: #ffffff;
539
538
  border-radius: 8px;
540
539
  transition:
541
- border-color 0.2s ease-out,
542
- box-shadow 0.2s ease-out,
543
- background 0.2s ease-out;
540
+ border-color 0.2s ease-in-out,
541
+ box-shadow 0.2s ease-in-out,
542
+ background 0.2s ease-in-out;
544
543
  font-size: 0.9375rem;
545
544
  line-height: 1.5;
546
545
  }
@@ -623,11 +622,12 @@
623
622
  .select_list {
624
623
  position: absolute;
625
624
  z-index: 10000;
626
- top: 0.25rem;
625
+ top: 100%;
627
626
  left: 0;
628
627
  width: 100%;
629
628
  min-width: 100%;
630
629
  box-sizing: border-box;
630
+ margin-top: 0.25rem;
631
631
  background: #ffffff;
632
632
  border: 1px solid #e5e5e5;
633
633
  border-radius: 8px;
@@ -680,7 +680,7 @@
680
680
  padding: 2px;
681
681
  border-radius: 9999px;
682
682
  background: #e5e5e5;
683
- transition: background 0.2s ease-out;
683
+ transition: background 0.2s ease-in-out;
684
684
  cursor: pointer;
685
685
  }
686
686
  .switch_thumb {
@@ -688,7 +688,7 @@
688
688
  height: 18px;
689
689
  background: #ffffff;
690
690
  border-radius: 9999px;
691
- transition: transform 0.2s ease-out;
691
+ transition: transform 0.2s ease-in-out;
692
692
  transform: translateX(0);
693
693
  }
694
694
  .switch_on {
@@ -782,9 +782,9 @@
782
782
  width: 100%;
783
783
  border-radius: 8px;
784
784
  transition:
785
- border-color 0.2s ease-out,
786
- box-shadow 0.2s ease-out,
787
- background 0.2s ease-out;
785
+ border-color 0.2s ease-in-out,
786
+ box-shadow 0.2s ease-in-out,
787
+ background 0.2s ease-in-out;
788
788
  font-size: 0.9375rem;
789
789
  line-height: 1.5;
790
790
  color: #1a1a1a;
@@ -868,6 +868,21 @@
868
868
 
869
869
  /* src/ui/form/date-picker/style.scss */
870
870
  .date_picker {
871
+ display: flex;
872
+ flex-direction: column;
873
+ gap: 0.25rem;
874
+ }
875
+ .date_picker_label {
876
+ font-family: "Pretendard", sans-serif;
877
+ font-size: 0.875rem;
878
+ line-height: 1.5;
879
+ color: #666666;
880
+ }
881
+ .date_picker_required {
882
+ margin-left: 0.25rem;
883
+ color: #ef4444;
884
+ }
885
+ .date_picker_fields {
871
886
  display: flex;
872
887
  gap: 0.5rem;
873
888
  }
@@ -884,9 +899,9 @@
884
899
  border-radius: 6px;
885
900
  cursor: pointer;
886
901
  transition:
887
- border-color 0.1s ease-out,
888
- box-shadow 0.1s ease-out,
889
- background-color 0.1s ease-out;
902
+ border-color 0.1s ease-in-out,
903
+ box-shadow 0.1s ease-in-out,
904
+ background-color 0.1s ease-in-out;
890
905
  appearance: none;
891
906
  background-image:
892
907
  linear-gradient(
@@ -920,20 +935,26 @@
920
935
  .date_picker select option {
921
936
  color: #1a1a1a;
922
937
  }
923
- .date_picker select:nth-child(1) {
924
- min-width: 96px;
938
+ .date_picker_full {
939
+ width: 100%;
940
+ }
941
+ .date_picker_full .date_picker_fields {
942
+ width: 100%;
943
+ }
944
+ .date_picker_full select {
945
+ flex: 1;
946
+ min-width: 0;
925
947
  }
926
- .date_picker select:nth-child(2),
927
- .date_picker select:nth-child(3) {
928
- min-width: 72px;
948
+ .date_picker_disabled .date_picker_label {
949
+ color: #9ca3af;
929
950
  }
930
951
  @media (max-width: 480px) {
931
- .date_picker {
952
+ .date_picker_fields {
932
953
  gap: 0.25rem;
933
954
  }
934
955
  .date_picker select {
935
- min-width: 0;
936
956
  flex: 1;
957
+ min-width: 0;
937
958
  }
938
959
  }
939
960
 
@@ -954,7 +975,7 @@
954
975
  font-size: 0.875rem;
955
976
  line-height: 1.5;
956
977
  color: #1a1a1a;
957
- transition: background 0.2s ease-out, border-color 0.2s ease-out;
978
+ transition: background 0.2s ease-in-out, border-color 0.2s ease-in-out;
958
979
  }
959
980
  .pagination_item:hover:not(:disabled) {
960
981
  background: #fafafa;
@@ -981,7 +1002,7 @@
981
1002
  font-size: 0.875rem;
982
1003
  line-height: 1.5;
983
1004
  color: #666666;
984
- transition: background 0.2s ease-out, color 0.2s ease-out;
1005
+ transition: background 0.2s ease-in-out, color 0.2s ease-in-out;
985
1006
  }
986
1007
  .pagination_pageButton:hover {
987
1008
  background: #fafafa;
package/dist/index.d.ts CHANGED
@@ -53,8 +53,9 @@ declare const useToast: (containerId?: string) => {
53
53
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
54
54
  variant?: "primary" | "secondary" | "ghost" | "danger";
55
55
  size?: "sm" | "md" | "lg";
56
+ width?: string;
56
57
  }
57
- declare const Button: ({ variant, size, className, ...props }: ButtonProps) => react_jsx_runtime.JSX.Element;
58
+ declare const Button: ({ variant, size, width, className, ...props }: ButtonProps) => react_jsx_runtime.JSX.Element;
58
59
 
59
60
  interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
60
61
  label?: React.ReactNode;
@@ -125,6 +126,8 @@ declare const TextField: React.ForwardRefExoticComponent<TextFieldProps & React.
125
126
 
126
127
  type DatePickerMode = "year-month" | "year-month-day";
127
128
  interface DatePickerProps {
129
+ label?: string;
130
+ required?: boolean;
128
131
  value?: string;
129
132
  onChange: (value: string) => void;
130
133
  mode?: DatePickerMode;
@@ -132,7 +135,7 @@ interface DatePickerProps {
132
135
  endYear?: number;
133
136
  disabled?: boolean;
134
137
  }
135
- declare const DatePicker: ({ value, onChange, mode, startYear, endYear, disabled, }: DatePickerProps) => react_jsx_runtime.JSX.Element;
138
+ declare const DatePicker: ({ label, required, value, onChange, mode, startYear, endYear, disabled, }: DatePickerProps) => react_jsx_runtime.JSX.Element;
136
139
 
137
140
  interface PaginationProps {
138
141
  page: number;
package/dist/index.js CHANGED
@@ -168,6 +168,7 @@ var useToast = (containerId = "default") => {
168
168
  var Button = ({
169
169
  variant = "primary",
170
170
  size = "md",
171
+ width = "100%",
171
172
  className,
172
173
  ...props
173
174
  }) => {
@@ -177,7 +178,7 @@ var Button = ({
177
178
  `button_size_${size}`,
178
179
  className ?? ""
179
180
  ].filter(Boolean).join(" ");
180
- return /* @__PURE__ */ jsx("button", { className: buttonClassName, ...props });
181
+ return /* @__PURE__ */ jsx("button", { className: buttonClassName, style: { width }, ...props });
181
182
  };
182
183
  var Checkbox = ({
183
184
  label,
@@ -279,7 +280,9 @@ var Select = ({
279
280
  React3.useEffect(() => {
280
281
  const onDocClick = (e) => {
281
282
  if (!wrapperRef.current) return;
282
- if (!wrapperRef.current.contains(e.target)) setIsOpen(false);
283
+ if (!wrapperRef.current.contains(e.target)) {
284
+ setIsOpen(false);
285
+ }
283
286
  };
284
287
  document.addEventListener("mousedown", onDocClick);
285
288
  return () => document.removeEventListener("mousedown", onDocClick);
@@ -327,7 +330,9 @@ var Select = ({
327
330
  case "Home":
328
331
  e.preventDefault();
329
332
  setIsOpen(true);
330
- setActiveIndex(options.findIndex((o) => !o.disabled));
333
+ setActiveIndex(
334
+ options.findIndex((o) => !o.disabled)
335
+ );
331
336
  break;
332
337
  case "End":
333
338
  e.preventDefault();
@@ -347,8 +352,15 @@ var Select = ({
347
352
  };
348
353
  React3.useEffect(() => {
349
354
  if (!isOpen) return;
350
- const idx = options.findIndex((o) => o.value === currentValue && !o.disabled);
351
- setActiveIndex(idx >= 0 ? idx : Math.max(0, options.findIndex((o) => !o.disabled)));
355
+ const idx = options.findIndex(
356
+ (o) => o.value === currentValue && !o.disabled
357
+ );
358
+ setActiveIndex(
359
+ idx >= 0 ? idx : Math.max(
360
+ 0,
361
+ options.findIndex((o) => !o.disabled)
362
+ )
363
+ );
352
364
  }, [isOpen, options, currentValue]);
353
365
  const rootClassName = ["select", className ?? ""].filter(Boolean).join(" ");
354
366
  const controlClassName = [
@@ -365,7 +377,14 @@ var Select = ({
365
377
  className: rootClassName,
366
378
  style: fullWidth ? { width: "100%" } : void 0,
367
379
  children: [
368
- label ? /* @__PURE__ */ jsx("label", { htmlFor: selectId, className: "select_label", children: label }) : null,
380
+ label && /* @__PURE__ */ jsx(
381
+ "label",
382
+ {
383
+ htmlFor: selectId,
384
+ className: "select_label",
385
+ children: label
386
+ }
387
+ ),
369
388
  /* @__PURE__ */ jsxs(
370
389
  "button",
371
390
  {
@@ -379,12 +398,18 @@ var Select = ({
379
398
  onKeyDown,
380
399
  disabled,
381
400
  children: [
382
- /* @__PURE__ */ jsx("span", { className: currentOption ? "select_value" : "select_placeholder", children: currentOption ? currentOption.label : placeholder }),
401
+ /* @__PURE__ */ jsx(
402
+ "span",
403
+ {
404
+ className: currentOption ? "select_value" : "select_placeholder",
405
+ children: currentOption ? currentOption.label : placeholder
406
+ }
407
+ ),
383
408
  /* @__PURE__ */ jsx("span", { className: "select_icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDown, { size: 16 }) })
384
409
  ]
385
410
  }
386
411
  ),
387
- isOpen ? /* @__PURE__ */ jsx(
412
+ isOpen && /* @__PURE__ */ jsx(
388
413
  "ul",
389
414
  {
390
415
  id: `${selectId}_listbox`,
@@ -413,14 +438,20 @@ var Select = ({
413
438
  },
414
439
  children: [
415
440
  /* @__PURE__ */ jsx("span", { children: opt.label }),
416
- selected ? /* @__PURE__ */ jsx(Check, { size: 16, "aria-hidden": "true" }) : null
441
+ selected && /* @__PURE__ */ jsx(
442
+ Check,
443
+ {
444
+ size: 16,
445
+ "aria-hidden": "true"
446
+ }
447
+ )
417
448
  ]
418
449
  },
419
450
  opt.value
420
451
  );
421
452
  })
422
453
  }
423
- ) : null
454
+ )
424
455
  ]
425
456
  }
426
457
  );
@@ -527,6 +558,8 @@ TextField.displayName = "TextField";
527
558
  var pad = (n) => String(n).padStart(2, "0");
528
559
  var getDaysInMonth = (year, month) => new Date(year, month, 0).getDate();
529
560
  var DatePicker = ({
561
+ label,
562
+ required,
530
563
  value,
531
564
  onChange,
532
565
  mode = "year-month-day",
@@ -543,49 +576,64 @@ var DatePicker = ({
543
576
  const result = mode === "year-month" ? `${yy}-${pad(mm)}-01` : `${yy}-${pad(mm)}-${pad(dd ?? 1)}`;
544
577
  onChange(result);
545
578
  };
546
- return /* @__PURE__ */ jsxs("div", { className: "date_picker", children: [
547
- /* @__PURE__ */ jsxs(
548
- "select",
549
- {
550
- value: year,
551
- disabled,
552
- onChange: (e) => emit(Number(e.target.value), month || 1, day || 1),
553
- children: [
554
- /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
555
- Array.from(
556
- { length: endYear - startYear + 1 },
557
- (_, i) => startYear + i
558
- ).map((y2) => /* @__PURE__ */ jsx("option", { value: y2, children: y2 }, y2))
559
- ]
560
- }
561
- ),
562
- /* @__PURE__ */ jsxs(
563
- "select",
564
- {
565
- value: month,
566
- disabled: disabled || !year,
567
- onChange: (e) => emit(year, Number(e.target.value), day || 1),
568
- children: [
569
- /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
570
- Array.from({ length: 12 }, (_, i) => i + 1).map((m2) => /* @__PURE__ */ jsx("option", { value: m2, children: pad(m2) }, m2))
571
- ]
572
- }
573
- ),
574
- mode === "year-month-day" && /* @__PURE__ */ jsxs(
575
- "select",
576
- {
577
- value: day,
578
- disabled: disabled || !month,
579
- onChange: (e) => emit(year, month, Number(e.target.value)),
580
- children: [
581
- /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
582
- Array.from({ length: days }, (_, i) => i + 1).map(
583
- (d2) => /* @__PURE__ */ jsx("option", { value: d2, children: pad(d2) }, d2)
579
+ return /* @__PURE__ */ jsxs(
580
+ "div",
581
+ {
582
+ className: [
583
+ "date_picker",
584
+ disabled && "date_picker_disabled"
585
+ ].filter(Boolean).join(" "),
586
+ children: [
587
+ label && /* @__PURE__ */ jsxs("label", { className: "date_picker_label", children: [
588
+ label,
589
+ required && /* @__PURE__ */ jsx("span", { className: "date_picker_required", children: "*" })
590
+ ] }),
591
+ /* @__PURE__ */ jsxs("div", { className: "date_picker_fields", children: [
592
+ /* @__PURE__ */ jsxs(
593
+ "select",
594
+ {
595
+ value: year,
596
+ disabled,
597
+ onChange: (e) => emit(Number(e.target.value), month || 1, day || 1),
598
+ children: [
599
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
600
+ Array.from(
601
+ { length: endYear - startYear + 1 },
602
+ (_, i) => startYear + i
603
+ ).map((y2) => /* @__PURE__ */ jsx("option", { value: y2, children: y2 }, y2))
604
+ ]
605
+ }
606
+ ),
607
+ /* @__PURE__ */ jsxs(
608
+ "select",
609
+ {
610
+ value: month,
611
+ disabled: disabled || !year,
612
+ onChange: (e) => emit(year, Number(e.target.value), day || 1),
613
+ children: [
614
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
615
+ Array.from({ length: 12 }, (_, i) => i + 1).map((m2) => /* @__PURE__ */ jsx("option", { value: m2, children: pad(m2) }, m2))
616
+ ]
617
+ }
618
+ ),
619
+ mode === "year-month-day" && /* @__PURE__ */ jsxs(
620
+ "select",
621
+ {
622
+ value: day,
623
+ disabled: disabled || !month,
624
+ onChange: (e) => emit(year, month, Number(e.target.value)),
625
+ children: [
626
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
627
+ Array.from({ length: days }, (_, i) => i + 1).map(
628
+ (d2) => /* @__PURE__ */ jsx("option", { value: d2, children: pad(d2) }, d2)
629
+ )
630
+ ]
631
+ }
584
632
  )
585
- ]
586
- }
587
- )
588
- ] });
633
+ ] })
634
+ ]
635
+ }
636
+ );
589
637
  };
590
638
  var range = (start, end) => {
591
639
  const out = [];
package/dist/next.css CHANGED
@@ -2,12 +2,17 @@
2
2
  .sidebar {
3
3
  display: flex;
4
4
  flex-direction: column;
5
- box-sizing: border-box;
6
5
  height: 100%;
6
+ width: 240px;
7
7
  background: #ffffff;
8
8
  border-right: 1px solid #e5e5e5;
9
9
  padding: 0.5rem;
10
10
  gap: 0.5rem;
11
+ overflow: hidden;
12
+ transition: width 0.3s ease-in-out;
13
+ }
14
+ .sidebar.is_closed {
15
+ width: 56px;
11
16
  }
12
17
  .sidebar_brand {
13
18
  padding: 0.5rem;
@@ -19,95 +24,87 @@
19
24
  align-items: center;
20
25
  justify-content: space-between;
21
26
  align-items: center;
22
- width: 100%;
23
- }
24
- .sidebar_brand_img {
25
- object-fit: contain;
26
27
  }
27
28
  .sidebar_nav {
28
29
  display: grid;
29
30
  gap: 0.25rem;
30
- padding: 0.5rem 0;
31
31
  }
32
32
  .sidebar_group {
33
33
  display: grid;
34
34
  gap: 0.25rem;
35
35
  }
36
36
  .sidebar_item {
37
- display: grid;
38
- grid-template-columns: 24px 1fr auto;
37
+ border: none;
38
+ display: flex;
39
39
  align-items: center;
40
- gap: 0.5rem;
40
+ justify-content: space-between;
41
41
  padding: 0.5rem 0.75rem;
42
42
  border-radius: 8px;
43
43
  color: #6b7280;
44
- background: transparent;
45
- border: 1px solid transparent;
46
44
  cursor: pointer;
47
- font-size: 0.9375rem;
48
- line-height: 1.5;
49
- transition:
50
- background-color 0.2s ease-out,
51
- color 0.2s ease-out,
52
- border-color 0.2s ease-out;
45
+ background: transparent;
46
+ transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
53
47
  }
54
48
  .sidebar_item:hover {
55
49
  background: #fafafa;
56
50
  color: #1f2937;
57
51
  }
58
- .sidebar_item:hover .sidebar_icon {
59
- color: #1f2937;
60
- }
61
52
  .sidebar_item.is_active {
62
- position: relative;
63
53
  background: #ffffff;
64
- border-color: #e5e5e5;
65
54
  color: #1f2937;
66
55
  box-shadow: inset 0 0 0 1px #e5e5e5;
67
56
  }
68
- .sidebar_item.is_active::before {
69
- content: "";
70
- position: absolute;
71
- left: 0;
72
- top: 6px;
73
- bottom: 6px;
74
- width: 3px;
75
- border-radius: 9999px;
76
- background: #000000;
57
+ .sidebar_item_left {
58
+ display: flex;
59
+ align-items: center;
60
+ gap: 0.5rem;
61
+ }
62
+ .sidebar_item_right {
63
+ display: flex;
64
+ align-items: center;
77
65
  }
78
66
  .sidebar_icon {
79
- display: inline-grid;
67
+ display: grid;
80
68
  place-items: center;
81
- color: #6b7280;
82
- transition: color 0.2s ease-out;
83
- }
84
- .sidebar_label {
85
- white-space: nowrap;
86
- text-overflow: ellipsis;
87
- text-align: left;
88
- }
89
- .sidebar_label.is_active {
90
- color: #000000;
91
- font-weight: 500;
92
69
  }
93
70
  .sidebar_chevron {
94
- display: inline-grid;
95
- place-items: center;
96
- color: #6b7280;
97
- transition: transform 0.2s ease-out;
71
+ transition: transform 0.2s ease-in-out;
98
72
  }
99
73
  .sidebar_chevron.is_open {
100
74
  transform: rotate(180deg);
101
75
  }
76
+ .sidebar_label {
77
+ font-size: 1rem;
78
+ white-space: nowrap;
79
+ }
102
80
  .sidebar_sub {
103
81
  display: grid;
104
82
  gap: 0.25rem;
105
- padding-left: 1rem;
83
+ padding-left: 0.75rem;
84
+ max-height: 0;
85
+ opacity: 0;
86
+ overflow: hidden;
87
+ transform: translateY(-4px);
88
+ transition:
89
+ max-height 0.3s ease-in-out,
90
+ opacity 0.2s ease-in-out,
91
+ transform 0.2s ease-in-out;
92
+ }
93
+ .sidebar_sub.is_open {
94
+ max-height: 500px;
95
+ opacity: 1;
96
+ transform: translateY(0);
106
97
  }
107
98
  .sidebar_sub_item {
99
+ display: flex;
100
+ align-items: center;
101
+ gap: 0.75rem;
108
102
  padding: 0.25rem 0.75rem;
109
- border-radius: 6px;
103
+ border-radius: 8px;
110
104
  color: #6b7280;
105
+ cursor: pointer;
106
+ background: transparent;
107
+ transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
111
108
  }
112
109
  .sidebar_sub_item:hover {
113
110
  background: #fafafa;
@@ -117,3 +114,12 @@
117
114
  color: #000000;
118
115
  font-weight: 500;
119
116
  }
117
+ .sidebar_sub_icon {
118
+ display: grid;
119
+ place-items: center;
120
+ color: #6b7280;
121
+ }
122
+ .sidebar_sub_label {
123
+ font-size: 0.875rem;
124
+ white-space: nowrap;
125
+ }
package/dist/next.d.ts CHANGED
@@ -3,14 +3,12 @@ import * as React from 'react';
3
3
  import { LucideIcon } from 'lucide-react';
4
4
 
5
5
  type MatchMode = "startsWith" | "exact";
6
- /** 단일 링크 메뉴 */
7
6
  interface SidebarLinkItem {
8
7
  type?: "link";
9
8
  href: string;
10
9
  label: React.ReactNode;
11
10
  icon?: LucideIcon;
12
11
  }
13
- /** 서브 메뉴 그룹 */
14
12
  interface SidebarGroupItem {
15
13
  type: "group";
16
14
  id: string;
@@ -23,12 +21,11 @@ interface SidebarProps {
23
21
  items?: SidebarItem[];
24
22
  activePath?: string;
25
23
  onItemSelect?: (href: string) => void;
26
- width?: number;
27
24
  className?: string;
28
25
  style?: React.CSSProperties;
29
26
  match?: MatchMode;
30
27
  brandHref?: string;
31
28
  }
32
- declare const Sidebar: ({ items, activePath, onItemSelect, width, className, style, match, brandHref, }: SidebarProps) => react_jsx_runtime.JSX.Element;
29
+ declare const Sidebar: ({ items, activePath, onItemSelect, className, style, match, brandHref, }: SidebarProps) => react_jsx_runtime.JSX.Element;
33
30
 
34
31
  export { Sidebar, type SidebarItem, type SidebarProps };
package/dist/next.js CHANGED
@@ -3,7 +3,7 @@ import './next.css';
3
3
  import * as React from 'react';
4
4
  import Link from 'next/link';
5
5
  import Image from 'next/image';
6
- import { ChevronDown } from 'lucide-react';
6
+ import { ChevronDown, CornerDownRight } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
 
9
9
  // src/ui/navigation/sidebar/index.tsx
@@ -11,7 +11,6 @@ var Sidebar = ({
11
11
  items = [],
12
12
  activePath,
13
13
  onItemSelect,
14
- width = 240,
15
14
  className,
16
15
  style,
17
16
  match = "startsWith",
@@ -23,17 +22,17 @@ var Sidebar = ({
23
22
  };
24
23
  const [openGroups, setOpenGroups] = React.useState([]);
25
24
  const [isOpen, setIsOpen] = React.useState(true);
26
- const handleToggleSidebar = (value) => {
27
- setIsOpen(value);
28
- localStorage.setItem("isOpen", JSON.stringify(value));
25
+ const toggleSidebar = (next) => {
26
+ setIsOpen(next);
27
+ localStorage.setItem("isOpen", JSON.stringify(next));
29
28
  };
30
29
  React.useEffect(() => {
31
30
  if (!activePath) return;
32
- const autoOpenIds = items.filter(
33
- (item) => item.type === "group" && item.children.some((child) => isActive(child.href))
34
- ).map((group) => group.id);
31
+ const autoOpen = items.filter(
32
+ (item) => item.type === "group" && item.children.some((c) => isActive(c.href))
33
+ ).map((g) => g.id);
35
34
  setOpenGroups(
36
- (prev) => Array.from(/* @__PURE__ */ new Set([...prev, ...autoOpenIds]))
35
+ (prev) => Array.from(/* @__PURE__ */ new Set([...prev, ...autoOpen]))
37
36
  );
38
37
  }, [activePath, items]);
39
38
  const toggleGroup = (id) => {
@@ -41,94 +40,108 @@ var Sidebar = ({
41
40
  (prev) => prev.includes(id) ? prev.filter((v) => v !== id) : [...prev, id]
42
41
  );
43
42
  };
44
- const rootClassName = ["sidebar", className ?? ""].filter(Boolean).join(" ");
45
43
  return /* @__PURE__ */ jsx(
46
44
  "aside",
47
45
  {
48
- className: rootClassName,
49
- style: { width: isOpen ? `${width}px` : "56px", ...style },
50
- "aria-label": "\uC0AC\uC774\uB4DC \uB0B4\uBE44\uAC8C\uC774\uC158",
46
+ className: [
47
+ "sidebar",
48
+ isOpen ? "is_open" : "is_closed",
49
+ className ?? ""
50
+ ].filter(Boolean).join(" "),
51
+ style,
51
52
  children: isOpen ? /* @__PURE__ */ jsxs(Fragment, { children: [
52
- /* @__PURE__ */ jsx("div", { className: "sidebar_brand", children: /* @__PURE__ */ jsxs(
53
- Link,
54
- {
55
- href: brandHref,
56
- className: "sidebar_brand_link",
57
- "aria-label": "Bigtablet \uD648\uC73C\uB85C",
58
- children: [
59
- /* @__PURE__ */ jsx(
60
- Image,
61
- {
62
- src: "/images/logo/bigtablet.png",
63
- alt: "Bigtablet",
64
- width: 96,
65
- height: 30,
66
- priority: true,
67
- className: "sidebar_brand_img"
68
- }
69
- ),
70
- /* @__PURE__ */ jsx(
71
- Image,
72
- {
73
- src: "/images/sidebar/arrow-close.svg",
74
- alt: "ArrowClose",
75
- width: 24,
76
- height: 24,
77
- onClick: () => handleToggleSidebar(false)
78
- }
79
- )
80
- ]
81
- }
82
- ) }),
53
+ /* @__PURE__ */ jsx("div", { className: "sidebar_brand", children: /* @__PURE__ */ jsxs(Link, { href: brandHref, className: "sidebar_brand_link", children: [
54
+ /* @__PURE__ */ jsx("div", {}),
55
+ /* @__PURE__ */ jsx(
56
+ Image,
57
+ {
58
+ src: "/images/logo/bigtablet.png",
59
+ alt: "Bigtablet",
60
+ width: 96,
61
+ height: 30,
62
+ priority: true
63
+ }
64
+ ),
65
+ /* @__PURE__ */ jsx(
66
+ Image,
67
+ {
68
+ src: "/images/sidebar/arrow-close.svg",
69
+ alt: "Close",
70
+ width: 24,
71
+ height: 24,
72
+ onClick: () => toggleSidebar(false)
73
+ }
74
+ )
75
+ ] }) }),
83
76
  /* @__PURE__ */ jsx("nav", { className: "sidebar_nav", children: items.map((item) => {
84
77
  if (item.type === "group") {
85
- const isOpen2 = openGroups.includes(item.id);
78
+ const open = openGroups.includes(item.id);
86
79
  return /* @__PURE__ */ jsxs("div", { className: "sidebar_group", children: [
87
80
  /* @__PURE__ */ jsxs(
88
81
  "button",
89
82
  {
90
83
  type: "button",
91
- className: [
92
- "sidebar_item",
93
- isOpen2 && "is_open"
94
- ].filter(Boolean).join(" "),
84
+ className: "sidebar_item",
95
85
  onClick: () => toggleGroup(item.id),
96
86
  children: [
97
- item.icon && /* @__PURE__ */ jsx("span", { className: "sidebar_icon", children: /* @__PURE__ */ jsx(item.icon, { size: 16 }) }),
98
- /* @__PURE__ */ jsx("span", { className: "sidebar_label", children: item.label }),
99
- /* @__PURE__ */ jsx(
100
- "span",
87
+ /* @__PURE__ */ jsxs("div", { className: "sidebar_item_left", children: [
88
+ item.icon && /* @__PURE__ */ jsx("span", { className: "sidebar_icon", children: /* @__PURE__ */ jsx(item.icon, { size: 16 }) }),
89
+ /* @__PURE__ */ jsx("span", { className: "sidebar_label", children: item.label })
90
+ ] }),
91
+ /* @__PURE__ */ jsx("span", { className: "sidebar_item_right", children: /* @__PURE__ */ jsx(
92
+ ChevronDown,
101
93
  {
94
+ size: 16,
102
95
  className: [
103
96
  "sidebar_chevron",
104
- isOpen2 && "is_open"
105
- ].filter(Boolean).join(" "),
106
- children: /* @__PURE__ */ jsx(ChevronDown, { size: 16 })
97
+ open && "is_open"
98
+ ].filter(Boolean).join(" ")
107
99
  }
108
- )
100
+ ) })
109
101
  ]
110
102
  }
111
103
  ),
112
- isOpen2 && /* @__PURE__ */ jsx("div", { className: "sidebar_sub", children: item.children.map((child) => {
113
- const active2 = isActive(child.href);
114
- return /* @__PURE__ */ jsx(
115
- Link,
116
- {
117
- href: child.href,
118
- className: [
119
- "sidebar_sub_item",
120
- active2 && "is_active"
121
- ].filter(Boolean).join(" "),
122
- onClick: () => onItemSelect?.(child.href),
123
- children: child.label
124
- },
125
- child.href
126
- );
127
- }) })
104
+ /* @__PURE__ */ jsx(
105
+ "div",
106
+ {
107
+ className: [
108
+ "sidebar_sub",
109
+ open && "is_open"
110
+ ].filter(Boolean).join(" "),
111
+ children: item.children.map((child) => {
112
+ const active2 = isActive(
113
+ child.href
114
+ );
115
+ return /* @__PURE__ */ jsxs(
116
+ Link,
117
+ {
118
+ href: child.href,
119
+ className: [
120
+ "sidebar_sub_item",
121
+ active2 && "is_active"
122
+ ].filter(Boolean).join(" "),
123
+ onClick: () => onItemSelect?.(
124
+ child.href
125
+ ),
126
+ children: [
127
+ /* @__PURE__ */ jsx("span", { className: "sidebar_sub_icon", children: /* @__PURE__ */ jsx(
128
+ CornerDownRight,
129
+ {
130
+ size: 14
131
+ }
132
+ ) }),
133
+ /* @__PURE__ */ jsx("span", { className: "sidebar_sub_label", children: child.label })
134
+ ]
135
+ },
136
+ child.href
137
+ );
138
+ })
139
+ }
140
+ )
128
141
  ] }, item.id);
129
142
  }
130
143
  const active = isActive(item.href);
131
- return /* @__PURE__ */ jsxs(
144
+ return /* @__PURE__ */ jsx(
132
145
  Link,
133
146
  {
134
147
  href: item.href,
@@ -137,33 +150,24 @@ var Sidebar = ({
137
150
  active && "is_active"
138
151
  ].filter(Boolean).join(" "),
139
152
  onClick: () => onItemSelect?.(item.href),
140
- children: [
153
+ children: /* @__PURE__ */ jsxs("div", { className: "sidebar_item_left", children: [
141
154
  item.icon && /* @__PURE__ */ jsx("span", { className: "sidebar_icon", children: /* @__PURE__ */ jsx(item.icon, { size: 16 }) }),
142
- /* @__PURE__ */ jsx(
143
- "span",
144
- {
145
- className: [
146
- "sidebar_label",
147
- active && "is_active"
148
- ].filter(Boolean).join(" "),
149
- children: item.label
150
- }
151
- )
152
- ]
155
+ /* @__PURE__ */ jsx("span", { className: "sidebar_label", children: item.label })
156
+ ] })
153
157
  },
154
158
  item.href
155
159
  );
156
160
  }) })
157
- ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
161
+ ] }) : /* @__PURE__ */ jsx(
158
162
  Image,
159
163
  {
160
164
  src: "/images/sidebar/menu.svg",
161
- alt: "menu",
165
+ alt: "Open",
162
166
  width: 24,
163
167
  height: 24,
164
- onClick: () => handleToggleSidebar(true)
168
+ onClick: () => toggleSidebar(true)
165
169
  }
166
- ) })
170
+ )
167
171
  }
168
172
  );
169
173
  };
@@ -1,4 +1,12 @@
1
- $transition_fast: 0.1s ease-out;
2
- $transition_base: 0.2s ease-out;
3
- $transition_slow: 0.3s ease-out;
4
- $transition_bounce: 0.3s cubic-bezier(0.16, 1, 0.3, 1);
1
+ $transition_fast: 0.1s ease-in-out; // hover, icon
2
+ $transition_base: 0.2s ease-in-out; // 메뉴 열림
3
+ $transition_slow: 0.3s ease-in-out; // 레이아웃 변화
4
+
5
+ $transition_emphasized: 0.25s cubic-bezier(0.4, 0, 0.2, 1); // Material 계열
6
+ $transition_bounce: 0.3s cubic-bezier(0.16, 1, 0.3, 1); // accordion, toast
7
+
8
+ $transition_fade: 0.15s ease-in-out; // opacity
9
+ $transition_slide: 0.25s ease-in-out; // translate
10
+ $transition_scale: 0.2s cubic-bezier(0.2, 0.8, 0.2, 1);
11
+
12
+ $transition_state: 0.18s ease-in-out; // disabled → enabled
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigtablet/design-system",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Bigtablet Design System UI Components",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",
@@ -1,4 +1,12 @@
1
- $transition_fast: 0.1s ease-out;
2
- $transition_base: 0.2s ease-out;
3
- $transition_slow: 0.3s ease-out;
4
- $transition_bounce: 0.3s cubic-bezier(0.16, 1, 0.3, 1);
1
+ $transition_fast: 0.1s ease-in-out; // hover, icon
2
+ $transition_base: 0.2s ease-in-out; // 메뉴 열림
3
+ $transition_slow: 0.3s ease-in-out; // 레이아웃 변화
4
+
5
+ $transition_emphasized: 0.25s cubic-bezier(0.4, 0, 0.2, 1); // Material 계열
6
+ $transition_bounce: 0.3s cubic-bezier(0.16, 1, 0.3, 1); // accordion, toast
7
+
8
+ $transition_fade: 0.15s ease-in-out; // opacity
9
+ $transition_slide: 0.25s ease-in-out; // translate
10
+ $transition_scale: 0.2s cubic-bezier(0.2, 0.8, 0.2, 1);
11
+
12
+ $transition_state: 0.18s ease-in-out; // disabled → enabled