@ihabdevteam/core 0.12.0 → 0.12.2

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.
@@ -3001,7 +3001,7 @@ export declare interface PairStats {
3001
3001
  total: number;
3002
3002
  }
3003
3003
 
3004
- export declare function Panel({ title, disc, actions, size, minHeight, maxWidth, className, scrollable, useBorder, useBg, useRowBorder, labelWidth, form, loading, empty, children }: PanelProps): JSX.Element;
3004
+ export declare function Panel({ title, icon, disc, actions, size, minHeight, maxWidth, className, scrollable, useBorder, useBg, useRowBorder, labelWidth, form, loading, empty, children }: PanelProps): JSX.Element;
3005
3005
 
3006
3006
  /** @public */
3007
3007
  export declare type PanelElement = HTMLDivElement & {
@@ -3017,6 +3017,14 @@ export declare function PanelGrid({ cols, children }: {
3017
3017
 
3018
3018
  declare interface PanelProps {
3019
3019
  title?: ReactNode;
3020
+ /**
3021
+ * 제목 앞에 서는 lucide 아이콘 이름. Section 의 `icon` 과 같은 자리다.
3022
+ *
3023
+ * 제목 노드 안에 직접 넣지 말 것 — 그러면 설명(`disc`)이 아이콘 밑으로
3024
+ * 들어가 제목 글자와 왼쪽 선이 어긋난다. 여기로 주면 아이콘이 제목·설명
3025
+ * 묶음 밖에 서서 둘의 왼쪽이 맞는다.
3026
+ */
3027
+ icon?: string;
3020
3028
  /**
3021
3029
  * 제목 아래 한 줄 설명. Section 의 `disc` 와 같은 자리다.
3022
3030
  *
@@ -35,31 +35,56 @@
35
35
  box-sizing: border-box;
36
36
  border-bottom: 1px solid var(--color-darkWhite);
37
37
  }
38
+ /* 머리 밑에 아무것도 없으면 가르는 선도 없다 — 판넬 자신의 테두리와 1px 을
39
+ 사이에 두고 겹쳐 두 줄로 보인다(실측: 머리만 있는 판넬 높이 58px). */
40
+ .panel__header:last-child {
41
+ border-bottom: none;
42
+ }
38
43
  /* header 높이도 size 스케일을 따름 — small 48 / medium 56 / large 64 */
39
44
  .panel--small .panel__header { min-height: 48px; }
40
45
  .panel--medium .panel__header { min-height: 56px; }
41
46
  .panel--large .panel__header { min-height: 64px; }
47
+ /* 제목 앞 아이콘 — Section 과 같은 크기·같은 색. 제목·설명 묶음 밖에 서므로
48
+ 설명도 제목 글자와 같은 왼쪽 선에서 시작한다. */
49
+ .panel__icon {
50
+ width: 20px;
51
+ height: 20px;
52
+ flex-shrink: 0;
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ margin-right: var(--spacing-s);
57
+ color: var(--t-item-fg, var(--color-main));
58
+ }
59
+
42
60
  /* 제목 + 설명 한 덩이. 설명이 없으면 한 줄짜리라 예전과 같은 모양이다. */
43
61
  .panel__title-group {
44
62
  display: flex;
45
63
  flex-direction: column;
46
64
  gap: 4px;
47
65
  min-width: 0;
66
+ /* 남는 자리를 이 묶음이 먹는다 — 머리가 space-between 이라, 아이콘이 서면
67
+ 묶음이 오른쪽 끝까지 밀려 아이콘과 제목 사이가 벌어졌다(실측: actions 없이
68
+ 219px, actions 와 함께 653px). 묶음이 자리를 먹으면 제목은 아이콘 옆
69
+ 20px 에 서고 actions 는 오른쪽 20px 을 그대로 지킨다. 아이콘이 없는
70
+ 머리 19개는 이 규칙 앞뒤로 위치가 완전히 같다(실측). */
71
+ margin-right: auto;
48
72
  }
49
73
  .panel__title {
50
74
  color: var(--color-darkGray);
51
75
  margin: 0;
52
76
  }
53
- /* Section disc 와 같은 색·같은 */
77
+ /* 색은 행의 부제(panel-row__sub)같다. 급도 같은 caption-1 쓴다 —
78
+ 머리와 행의 곁글이 한 화면에 함께 서므로(Section 의 disc 는 body-3 이지만
79
+ 그쪽은 판넬 밖 제목이라 옆에 견줄 곁글이 없다). */
54
80
  .panel__disc {
55
81
  color: var(--color-gray);
56
82
  margin: 0;
57
83
  }
58
- /* 설명이 붙은 머리는 줄이라 size min-height 넘어선다
59
- 위아래 여백을 줘서 글이 테두리에 붙지 않게 한다. */
60
- .panel__header--with-disc {
61
- padding-block: var(--spacing-s);
62
- }
84
+ /* 설명이 붙어도 머리 높이는 size 스케일 그대로다 위아래 여백을 따로 주면
85
+ 그만큼 머리만 커져서, 부제(sub)가 붙어도 높이가 그대로인 행과 어긋난다
86
+ (실측: padding 12px 을 줬더니 56 → 74). 제목+설명 두 줄은 49px 이라
87
+ medium(56)·large(64) 안에 들어간다. small(48) 에서만 살짝 넘친다. */
63
88
  .panel__actions {
64
89
  display: flex;
65
90
  gap: 6px;
@@ -4,6 +4,14 @@ import './Panel.css';
4
4
  type PanelSize = 'small' | 'medium' | 'large';
5
5
  interface PanelProps {
6
6
  title?: ReactNode;
7
+ /**
8
+ * 제목 앞에 서는 lucide 아이콘 이름. Section 의 `icon` 과 같은 자리다.
9
+ *
10
+ * 제목 노드 안에 직접 넣지 말 것 — 그러면 설명(`disc`)이 아이콘 밑으로
11
+ * 들어가 제목 글자와 왼쪽 선이 어긋난다. 여기로 주면 아이콘이 제목·설명
12
+ * 묶음 밖에 서서 둘의 왼쪽이 맞는다.
13
+ */
14
+ icon?: string;
7
15
  /**
8
16
  * 제목 아래 한 줄 설명. Section 의 `disc` 와 같은 자리다.
9
17
  *
@@ -37,7 +45,7 @@ interface PanelProps {
37
45
  empty?: ReactNode;
38
46
  children?: ReactNode;
39
47
  }
40
- export default function Panel({ title, disc, actions, size, minHeight, maxWidth, className, scrollable, useBorder, useBg, useRowBorder, labelWidth, form, loading, empty, children }: PanelProps): import("react").JSX.Element;
48
+ export default function Panel({ title, icon, disc, actions, size, minHeight, maxWidth, className, scrollable, useBorder, useBg, useRowBorder, labelWidth, form, loading, empty, children }: PanelProps): import("react").JSX.Element;
41
49
  /** @public */
42
50
  export declare function PanelGrid({ cols, children }: {
43
51
  cols?: number;
@@ -8,12 +8,13 @@ import SegmentedControl from './SegmentedControl';
8
8
  import Dropdown from './Dropdown';
9
9
  import Button from './Button';
10
10
  import Badge from './Badge';
11
+ import Icon from './Icon';
11
12
  import { deviceKindVariant } from '../utils/deviceKind';
12
13
  import TextField from './TextField';
13
14
  import Loader from './Loader';
14
15
  import './Panel.css';
15
16
  const noop = () => { };
16
- export default function Panel({ title, disc, actions, size = 'medium', minHeight, maxWidth, className, scrollable, useBorder = true, useBg = true, useRowBorder = true, labelWidth, form = false, loading, empty, children }) {
17
+ export default function Panel({ title, icon, disc, actions, size = 'medium', minHeight, maxWidth, className, scrollable, useBorder = true, useBg = true, useRowBorder = true, labelWidth, form = false, loading, empty, children }) {
17
18
  const cls = cx('panel', `panel--${size}`, form ? 'panel--form' : '',
18
19
  /* minHeight 패널은 마지막 행 아래 빈 공간이 생길 수 있으므로 행 하단 구분선 사용 */
19
20
  minHeight !== undefined ? 'panel--fill' : '', !useBorder ? 'panel--no-border' : '', !useBg ? 'panel--no-bg' : '', !useRowBorder ? 'panel--no-row-border' : '', className);
@@ -24,7 +25,7 @@ export default function Panel({ title, disc, actions, size = 'medium', minHeight
24
25
  style.maxWidth = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth;
25
26
  if (labelWidth !== undefined)
26
27
  style['--panel-label-width'] = typeof labelWidth === 'number' ? `${labelWidth}px` : labelWidth;
27
- return (_jsxs("div", { className: cls, style: Object.keys(style).length ? style : undefined, children: [(title != null || disc != null) && (_jsxs("div", { className: cx('panel__header', disc != null ? 'panel__header--with-disc' : ''), children: [_jsxs("div", { className: "panel__title-group", children: [title != null && _jsx("h2", { className: "panel__title h5b", children: title }), disc != null && _jsx("p", { className: "panel__disc body-3", children: disc })] }), actions && _jsx("div", { className: "panel__actions", children: actions })] })), loading ? (_jsx(PanelRow, { variant: "loading" })) : empty != null && (children == null || (Array.isArray(children) && children.length === 0)) ? (_jsx(PanelRow, { variant: "empty", children: empty })) : scrollable ? (_jsx("div", { className: "panel__body--scroll", children: children })) : children] }));
28
+ return (_jsxs("div", { className: cls, style: Object.keys(style).length ? style : undefined, children: [(title != null || disc != null) && (_jsxs("div", { className: cx('panel__header', disc != null ? 'panel__header--with-disc' : ''), children: [icon && (_jsx(Icon, { icon: icon, label: typeof title === 'string' ? title : undefined, className: "panel__icon" })), _jsxs("div", { className: "panel__title-group", children: [title != null && _jsx("h2", { className: "panel__title h5b", children: title }), disc != null && _jsx("p", { className: "panel__disc caption-1", children: disc })] }), actions && _jsx("div", { className: "panel__actions", children: actions })] })), loading ? (_jsx(PanelRow, { variant: "loading" })) : empty != null && (children == null || (Array.isArray(children) && children.length === 0)) ? (_jsx(PanelRow, { variant: "empty", children: empty })) : scrollable ? (_jsx("div", { className: "panel__body--scroll", children: children })) : children] }));
28
29
  }
29
30
  // ── PanelGrid ──────────────────────────────────────────────────────────────
30
31
  /** @public */
@@ -155,6 +155,7 @@ import WrenchIcon from 'lucide-react/dist/esm/icons/wrench.mjs';
155
155
  import XIcon from 'lucide-react/dist/esm/icons/x.mjs';
156
156
  import ZapIcon from 'lucide-react/dist/esm/icons/zap.mjs';
157
157
  import AnchorIcon from 'lucide-react/dist/esm/icons/anchor.mjs';
158
+ import ArmchairIcon from 'lucide-react/dist/esm/icons/armchair.mjs';
158
159
  import ArrowUpAZIcon from 'lucide-react/dist/esm/icons/arrow-up-a-z.mjs';
159
160
  import BarChart2Icon from 'lucide-react/dist/esm/icons/bar-chart-2.mjs';
160
161
  import CalendarIcon from 'lucide-react/dist/esm/icons/calendar.mjs';
@@ -194,6 +195,7 @@ export const STATIC_LUCIDE_ICONS = {
194
195
  'alert-triangle': AlertTriangleIcon,
195
196
  'align-start-vertical': AlignStartVerticalIcon,
196
197
  'anchor': AnchorIcon,
198
+ 'armchair': ArmchairIcon,
197
199
  'arrow-down': ArrowDownIcon,
198
200
  'arrow-left': ArrowLeftIcon,
199
201
  'arrow-right': ArrowRightIcon,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ihabdevteam/core",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "private": false,
5
5
  "main": "dist/ihabdevteam-core.cjs.js",
6
6
  "module": "dist/ihabdevteam-core.esm.js",