sellmate-design-system-react 9.0.0-beta.83 → 9.0.0-beta.86

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/AGENTS.md CHANGED
@@ -1418,6 +1418,7 @@ tableRef.current.scrollToRow(row); // 복원
1418
1418
  | **`default`**(기본) | `#888888` | **대부분** — 닫기, 복사, 드래그, 펼치기, 행 삭제 |
1419
1419
  | `action` | `#025497` | 진입·추가처럼 **의미 있는 조작** — 편집 모드 진입, 항목 추가, 행 수정 |
1420
1420
  | `subAction` | `#5CB0F3` | `action` 다음 계층 — `action`·`default` 사이의 중의적 의미 + 부분 강조 |
1421
+ | `complete` | `#00973C` | **완료**를 나타내는 조작 — 완료 처리(`check`) 등 |
1421
1422
  | `danger` | `#E30000` | **되돌릴 수 없는 삭제** |
1422
1423
  | `inverse` | `#FFFFFF` | 어두운 배경 위 (GNB 접기 등) |
1423
1424
 
@@ -3357,14 +3358,17 @@ export default function SalesDashboardPage() {
3357
3358
  <SCard key={s.label} bordered className="flex flex-col gap-sd-4 p-sd-16">
3358
3359
  <span className="typo-body-sm-default text-fg-secondary">{s.label}</span>
3359
3360
  {/* 카드 안의 메인 수치는 typo-heading-xl — 제목에는 쓰지 않는다 (§2-1) */}
3360
- <span className="typo-heading-xl">
3361
+ <span className="typo-heading-xl text-right">
3361
3362
  {s.value.toLocaleString()}
3362
3363
  {s.unit}
3363
3364
  </span>
3364
- {/* 증감은 숫자 앞 부호와 색으로 함께 말한다 — 색만으로 뜻을 싣지 않는다 */}
3365
- <span className="typo-body-sm-default text-fg-tertiary">
3365
+ {/* 증감은 숫자 앞 부호와 색으로 함께 말한다 — 색만으로 뜻을 싣지 않는다.
3366
+ 문장 안 강조라 굵기는 typo-body-sm-medium 프리셋으로 준다 (§2-1) */}
3367
+ <span className="typo-body-sm-default text-right text-fg-tertiary">
3366
3368
  전기간 대비{' '}
3367
- <span className={s.change >= 0 ? 'text-fg-success' : 'text-fg-danger'}>
3369
+ <span
3370
+ className={`typo-body-sm-medium ${s.change >= 0 ? 'text-fg-success' : 'text-fg-danger'}`}
3371
+ >
3368
3372
  {s.change >= 0 ? '+' : ''}
3369
3373
  {s.change.toLocaleString()}%
3370
3374
  </span>
@@ -3375,16 +3379,20 @@ export default function SalesDashboardPage() {
3375
3379
 
3376
3380
  {/* 차트는 섹션 카드에 담는다. 차트 이름은 카드의 title 이 맡으므로 차트의 title 은 주지 않는다 —
3377
3381
  둘 다 주면 제목이 두 줄이 된다. 단위(unit)는 차트가 오른쪽 위에 적는다.
3378
- 한 페이지에 섹션 카드가 여럿이라 marker 를 켠다 (§4-5) */}
3382
+ 한 페이지 안의 카드 헤더(titleSize·배경)는 전부 같게 간다 (§4-5) */}
3379
3383
  {/* 날짜순 흐름이라 선 — 순서가 있는 항목의 추이 (§3-0 B). 항목(날짜)이 많아 한 줄을 다 쓴다 */}
3380
- <SSectionHeaderCard title="일별 매출" marker>
3384
+ <SSectionHeaderCard
3385
+ title="일별 매출"
3386
+ titleSize="sm"
3387
+ headerClassName="bg-bg-neutralBright!"
3388
+ padding="none"
3389
+ >
3381
3390
  <SLineChart
3382
3391
  categories={daily.categories}
3383
3392
  series={[{ name: '매출', data: daily.data }]}
3384
3393
  unit="원"
3385
3394
  area
3386
3395
  showLegend={false}
3387
- height={280}
3388
3396
  formatValue={won}
3389
3397
  // 차트는 "어느 쪽이 큰가" 까지만 답한다 — 정확한 값은 그날의 주문 목록에서 본다
3390
3398
  onPointClick={p => navigate(`/orders?date=${daily.dates[p.categoryIndex]}`)}
@@ -3393,26 +3401,36 @@ export default function SalesDashboardPage() {
3393
3401
 
3394
3402
  <div className="grid grid-cols-2 gap-sd-12">
3395
3403
  {/* 합계는 도넛 구멍이 아니라 카드 subtitle 에 적는다 (§3-0 B) */}
3396
- <SSectionHeaderCard title="판매처별 비중" marker subtitle={`합계 ${won(channelTotal)}`}>
3404
+ <SSectionHeaderCard
3405
+ title="판매처별 비중"
3406
+ titleSize="sm"
3407
+ subtitle={`합계 ${won(channelTotal)}`}
3408
+ padding="none"
3409
+ headerClassName="bg-bg-neutralBright!"
3410
+ >
3397
3411
  <SDonutChart
3398
3412
  data={channelShare}
3399
3413
  legendPosition="bottom"
3400
3414
  formatValue={won}
3401
3415
  // 그림은 폭만큼의 정사각형이다 — 칸을 다 채우면 옆 카드보다 훨씬 길어지므로
3402
3416
  // 화면 고유 치수로 폭을 묶는다 (§1-2)
3403
- className="mx-auto max-w-[280px]"
3417
+ className="mx-auto max-w-[310px]"
3404
3418
  />
3405
3419
  </SSectionHeaderCard>
3406
3420
 
3407
3421
  {/* 항목끼리 크기를 견주므로 막대. 상품명이 길어 가로 막대로 눕힌다 */}
3408
- <SSectionHeaderCard title="상품별 판매량 TOP 5" marker>
3422
+ <SSectionHeaderCard
3423
+ title="상품별 판매량 TOP 5"
3424
+ titleSize="sm"
3425
+ padding="none"
3426
+ headerClassName="bg-bg-neutralBright!"
3427
+ >
3409
3428
  <SBarChart
3410
3429
  categories={topProducts.categories}
3411
3430
  series={[{ name: '판매량', data: topProducts.data }]}
3412
3431
  unit="개"
3413
3432
  direction="horizontal"
3414
3433
  showLegend={false}
3415
- height={320}
3416
3434
  formatValue={v => `${v.toLocaleString()}개`}
3417
3435
  />
3418
3436
  </SSectionHeaderCard>
@@ -38,7 +38,8 @@ export type SGhostButtonSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg';
38
38
  ### SGhostButtonIntent
39
39
 
40
40
  ```ts
41
- export type SGhostButtonIntent = 'default' | 'danger' | 'action' | 'subAction' | 'inverse';
41
+ export type SGhostButtonIntent =
42
+ 'default' | 'danger' | 'action' | 'subAction' | 'complete' | 'inverse';
42
43
  ```
43
44
 
44
45
  ## Dependencies
@@ -1,7 +1,7 @@
1
1
  import { type CSSProperties, type MouseEvent } from 'react';
2
2
  import { type SIconName } from '../SIcon';
3
3
  export type SGhostButtonSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg';
4
- export type SGhostButtonIntent = 'default' | 'danger' | 'action' | 'subAction' | 'inverse';
4
+ export type SGhostButtonIntent = 'default' | 'danger' | 'action' | 'subAction' | 'complete' | 'inverse';
5
5
  export interface SGhostButtonProps {
6
6
  /** 표시할 아이콘명 (필수) */
7
7
  icon: SIconName;
@@ -12,6 +12,7 @@
12
12
  | `value?` | `string \| number \| null` | — | 현재 선택 값 |
13
13
  | `size?` | `SRadioButtonSize` | `'sm'` | 크기 |
14
14
  | `disabled?` | `boolean` | `false` | 전체 비활성 |
15
+ | `readOnly?` | `boolean` | `false` | 읽기 전용. 선택 값을 보여주기만 하고 바꿀 수 없다. 모든 버튼이 readOnly 배경, 선택된 버튼만 readOnly 테두리·기본 글자색으로 구분하고 선택되지 않은 버튼의 글자는 disabled 색이다. 포커스·툴팁은 유지된다. `disabled`(그룹·옵션)가 함께 오면 disabled 가 우선한다. |
15
16
  | `name?` | `string` | — | input name 속성 |
16
17
  | `showTooltip?` | `boolean` | `false` | 옵션 호버 시 툴팁 노출. 문구는 옵션별 `tooltip` 으로 주며, 문구가 없는 옵션은 툴팁을 띄우지 않는다. 툴팁 타입은 `accent` 고정이다. |
17
18
  | `className?` | `string` | — | |
@@ -16,6 +16,13 @@ export interface SRadioButtonProps {
16
16
  size?: SRadioButtonSize;
17
17
  /** 전체 비활성 */
18
18
  disabled?: boolean;
19
+ /**
20
+ * 읽기 전용. 선택 값을 보여주기만 하고 바꿀 수 없다.
21
+ * 모든 버튼이 readOnly 배경, 선택된 버튼만 readOnly 테두리·기본 글자색으로 구분하고
22
+ * 선택되지 않은 버튼의 글자는 disabled 색이다.
23
+ * 포커스·툴팁은 유지된다. `disabled`(그룹·옵션)가 함께 오면 disabled 가 우선한다.
24
+ */
25
+ readOnly?: boolean;
19
26
  /** input name 속성 */
20
27
  name?: string;
21
28
  /**
@@ -30,6 +37,6 @@ export interface SRadioButtonProps {
30
37
  }
31
38
  /**
32
39
  * SRadioButton — sd-radio-button 포팅. 세그먼트형 라디오(버튼 그룹).
33
- * default(흰 배경/회색, hover 파란 채움) · select(흰 배경/파란 테두리·글자) · disabled.
40
+ * default(흰 배경/회색, hover 파란 채움) · select(흰 배경/파란 테두리·글자) · disabled · readOnly.
34
41
  */
35
- export declare function SRadioButton({ options, value, size, disabled, name, showTooltip, onValueChange, className, style, }: SRadioButtonProps): import("react").JSX.Element;
42
+ export declare function SRadioButton({ options, value, size, disabled, readOnly, name, showTooltip, onValueChange, className, style, }: SRadioButtonProps): import("react").JSX.Element;
package/dist/index.cjs CHANGED
@@ -4875,8 +4875,22 @@ function useFormField(field) {
4875
4875
  }, [ctx, name]);
4876
4876
  }
4877
4877
  var FIELD_SIZE = {
4878
- sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
4879
- md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
4878
+ sm: {
4879
+ height: 28,
4880
+ radius: 4,
4881
+ fieldGap: 12,
4882
+ labelGap: 6,
4883
+ labelTypo: "typo-field-sm-bold",
4884
+ icon: 16
4885
+ },
4886
+ md: {
4887
+ height: 36,
4888
+ radius: 6,
4889
+ fieldGap: 16,
4890
+ labelGap: 8,
4891
+ labelTypo: "typo-field-md-bold",
4892
+ icon: 16
4893
+ }
4880
4894
  };
4881
4895
  var ALIGN = {
4882
4896
  start: "flex-start",
@@ -4986,7 +5000,10 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
4986
5000
  label !== "" && /* @__PURE__ */ jsxRuntime.jsxs(
4987
5001
  "div",
4988
5002
  {
4989
- className: "flex shrink-0 items-center font-bold text-(--sys-color-field-text-default)",
5003
+ className: cn(
5004
+ "flex shrink-0 items-center text-(--sys-color-field-text-default)",
5005
+ s.labelTypo
5006
+ ),
4990
5007
  style: {
4991
5008
  // 위에 선 라벨은 제 글줄 높이만 차지한다 — 컨트롤 높이에 맞춰 늘리면
4992
5009
  // 라벨과 컨트롤 사이가 간격 토큰보다 넓어진다. 줄 세우기용 labelWidth 도
@@ -4995,9 +5012,7 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
4995
5012
  height: cssLen(labelHeight) ?? s.height,
4996
5013
  marginRight: s.fieldGap,
4997
5014
  width: cssLen(labelWidth)
4998
- },
4999
- fontSize: s.fontSize,
5000
- lineHeight: `${s.lineHeight}px`
5015
+ }
5001
5016
  },
5002
5017
  children: [
5003
5018
  icon && /* @__PURE__ */ jsxRuntime.jsx(
@@ -18800,7 +18815,8 @@ function SCalendar({
18800
18815
  className: cn(
18801
18816
  "flex h-[var(--cmp-datepicker-calendar-day-circle-size)] w-[var(--cmp-datepicker-calendar-day-circle-size)] items-center justify-center rounded-[var(--cmp-datepicker-calendar-day-circle-radius)] border border-solid border-transparent text-[14px] leading-[var(--cmp-datepicker-calendar-day-circle-size)]",
18802
18817
  selected ? "bg-[var(--cmp-datepicker-calendar-day-select-bg)] text-[color:var(--cmp-datepicker-calendar-day-select-text)]" : disabled ? "text-[color:var(--cmp-datepicker-calendar-day-disabled-text)]" : "text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:border-[color:var(--cmp-datepicker-calendar-day-hover-border)] hover:text-[color:var(--cmp-datepicker-calendar-day-hover-text)]",
18803
- isToday && !disabled && "font-bold"
18818
+ // 오늘: datepicker.calendar.day.typography.bold (→ typography.field.md.bold) 의 굵기
18819
+ isToday && !disabled && "font-semibold"
18804
18820
  ),
18805
18821
  children: cell.inCurrentMonth ? cell.day : ""
18806
18822
  }
@@ -19528,7 +19544,8 @@ function RangeCalendar({
19528
19544
  "relative z-[1] flex h-[var(--cmp-datepicker-calendar-day-circle-size)] w-[var(--cmp-datepicker-calendar-day-circle-size)] items-center justify-center rounded-[var(--cmp-datepicker-calendar-day-circle-radius)] border border-solid border-transparent bg-transparent text-[14px]",
19529
19545
  !cell.inCurrentMonth ? "invisible pointer-events-none" : disabled ? "cursor-not-allowed" : "cursor-pointer",
19530
19546
  isEdge ? "bg-[var(--cmp-datepicker-calendar-day-select-bg)] text-[color:var(--cmp-datepicker-calendar-day-select-text)]" : disabled ? "text-[color:var(--cmp-datepicker-calendar-day-disabled-text)]" : "text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:border-[color:var(--cmp-datepicker-calendar-day-hover-border)] hover:text-[color:var(--cmp-datepicker-calendar-day-hover-text)]",
19531
- isToday && !disabled && "font-bold"
19547
+ // 오늘: datepicker.calendar.day.typography.bold (→ typography.field.md.bold) 의 굵기
19548
+ isToday && !disabled && "font-semibold"
19532
19549
  ),
19533
19550
  children: cell.inCurrentMonth ? cell.day : ""
19534
19551
  }
@@ -21760,32 +21777,57 @@ var SLineChart = /* @__PURE__ */ react.forwardRef(function SLineChart2({
21760
21777
  `area-gradient-${seriesIndex}`
21761
21778
  )) });
21762
21779
  }, [area, plotted, colorAt, gradientId]);
21763
- const lines = react.useMemo(
21780
+ const linePaths = react.useMemo(
21764
21781
  () => plotted.flatMap(
21765
- (row, seriesIndex) => runsOf(row).map((run, runIndex) => {
21782
+ (row, seriesIndex) => runsOf(row).flatMap((run, runIndex) => {
21766
21783
  const d = linePath(run, smooth);
21767
- if (!d) return null;
21768
- return /* @__PURE__ */ jsxRuntime.jsx(
21784
+ return d ? [{ key: `${seriesIndex}-${runIndex}`, seriesIndex, d }] : [];
21785
+ })
21786
+ ),
21787
+ [plotted, smooth]
21788
+ );
21789
+ const strokeShape = {
21790
+ strokeWidth: LINE_WIDTH,
21791
+ strokeDasharray: lineStyle === "dashed" ? LINE_DASH : void 0
21792
+ };
21793
+ const lines = linePaths.map(({ key, seriesIndex, d }) => /* @__PURE__ */ jsxRuntime.jsx(
21794
+ "path",
21795
+ {
21796
+ "aria-hidden": true,
21797
+ d,
21798
+ fill: "none",
21799
+ strokeLinecap: "round",
21800
+ strokeLinejoin: "round",
21801
+ style: { ...strokeShape, stroke: colorAt(seriesIndex), strokeOpacity: LINE_OPACITY }
21802
+ },
21803
+ `line-${key}`
21804
+ ));
21805
+ const areaMaskId = `${gradientId}-area-mask`;
21806
+ const areaMask = area ? /* @__PURE__ */ jsxRuntime.jsxs(
21807
+ "mask",
21808
+ {
21809
+ id: areaMaskId,
21810
+ maskUnits: "userSpaceOnUse",
21811
+ x: 0,
21812
+ y: 0,
21813
+ width: size.width,
21814
+ height: size.height,
21815
+ children: [
21816
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: 0, y: 0, width: size.width, height: size.height, style: { fill: "white" } }),
21817
+ linePaths.map(({ key, d }) => /* @__PURE__ */ jsxRuntime.jsx(
21769
21818
  "path",
21770
21819
  {
21771
- "aria-hidden": true,
21772
21820
  d,
21773
21821
  fill: "none",
21774
21822
  strokeLinecap: "round",
21775
21823
  strokeLinejoin: "round",
21776
- style: {
21777
- stroke: colorAt(seriesIndex),
21778
- strokeWidth: LINE_WIDTH,
21779
- strokeOpacity: LINE_OPACITY,
21780
- strokeDasharray: lineStyle === "dashed" ? LINE_DASH : void 0
21781
- }
21824
+ style: { ...strokeShape, stroke: "black" }
21782
21825
  },
21783
- `line-${seriesIndex}-${runIndex}`
21784
- );
21785
- })
21786
- ),
21787
- [plotted, smooth, colorAt, lineStyle]
21788
- );
21826
+ `mask-${key}`
21827
+ ))
21828
+ ]
21829
+ }
21830
+ ) : null;
21789
21831
  const { pointHalos, pointDots } = react.useMemo(() => {
21790
21832
  const halos = [];
21791
21833
  const dots = [];
@@ -21959,7 +22001,8 @@ var SLineChart = /* @__PURE__ */ react.forwardRef(function SLineChart2({
21959
22001
  }
21960
22002
  ),
21961
22003
  areaGradients,
21962
- areas,
22004
+ areaMask,
22005
+ area && /* @__PURE__ */ jsxRuntime.jsx("g", { mask: `url(#${areaMaskId})`, children: areas }),
21963
22006
  lines,
21964
22007
  pointHalos,
21965
22008
  pointDots,
@@ -22908,6 +22951,7 @@ function SRadioButton({
22908
22951
  value,
22909
22952
  size = "sm",
22910
22953
  disabled = false,
22954
+ readOnly = false,
22911
22955
  name,
22912
22956
  showTooltip = false,
22913
22957
  onValueChange,
@@ -22925,11 +22969,13 @@ function SRadioButton({
22925
22969
  {
22926
22970
  role: "radiogroup",
22927
22971
  "aria-disabled": disabled,
22972
+ "aria-readonly": readOnly || void 0,
22928
22973
  className: cn("inline-flex isolate", className),
22929
22974
  style,
22930
22975
  children: options.map((o, i) => {
22931
22976
  const selected = o.value === value;
22932
22977
  const od = disabled || o.disabled;
22978
+ const ro = readOnly && !od;
22933
22979
  const first = i === 0;
22934
22980
  const last = i === options.length - 1;
22935
22981
  const tooltip = showTooltip && o.tooltip ? o.tooltip : void 0;
@@ -22941,7 +22987,7 @@ function SRadioButton({
22941
22987
  name,
22942
22988
  "aria-checked": selected,
22943
22989
  disabled: od,
22944
- onClick: () => !od && !selected && onValueChange?.(o.value),
22990
+ onClick: () => !od && !ro && !selected && onValueChange?.(o.value),
22945
22991
  onMouseEnter: tooltip ? () => setHoveredValue(o.value) : void 0,
22946
22992
  onMouseLeave: tooltip ? () => setHoveredValue(null) : void 0,
22947
22993
  onFocus: tooltip ? () => setHoveredValue(o.value) : void 0,
@@ -22950,8 +22996,8 @@ function SRadioButton({
22950
22996
  "relative box-border grow inline-flex items-center justify-center whitespace-nowrap border border-solid font-medium transition-colors",
22951
22997
  first ? "" : "-ml-px",
22952
22998
  selected ? "z-10" : "",
22953
- !od && !selected && "hover:!bg-[var(--cmp-radioButton-bg-hover)] hover:!text-[color:var(--cmp-radioButton-content-hover)] hover:!border-[color:var(--cmp-radioButton-bg-hover)] hover:z-10",
22954
- od ? "cursor-not-allowed" : "cursor-pointer"
22999
+ !od && !ro && !selected && "hover:!bg-[var(--cmp-radioButton-bg-hover)] hover:!text-[color:var(--cmp-radioButton-content-hover)] hover:!border-[color:var(--cmp-radioButton-bg-hover)] hover:z-10",
23000
+ od ? "cursor-not-allowed" : ro ? "cursor-default" : "cursor-pointer"
22955
23001
  ),
22956
23002
  style: {
22957
23003
  height: `var(--cmp-radioButton-${size}-height)`,
@@ -22964,9 +23010,9 @@ function SRadioButton({
22964
23010
  last ? s.radius : "0",
22965
23011
  first ? s.radius : "0"
22966
23012
  ].join(" "),
22967
- background: od ? "var(--cmp-radioButton-bg-disabled)" : "var(--cmp-radioButton-bg-default)",
22968
- borderColor: od ? "var(--cmp-radioButton-border-disabled)" : selected ? "var(--cmp-radioButton-border-select)" : "var(--cmp-radioButton-border-default)",
22969
- color: od ? "var(--cmp-radioButton-content-disabled)" : selected ? "var(--cmp-radioButton-content-select)" : "var(--cmp-radioButton-content-default)"
23013
+ background: od ? "var(--cmp-radioButton-bg-disabled)" : ro ? "var(--cmp-radioButton-bg-readOnly)" : "var(--cmp-radioButton-bg-default)",
23014
+ borderColor: od ? "var(--cmp-radioButton-border-disabled)" : selected ? ro ? "var(--cmp-radioButton-border-readOnly)" : "var(--cmp-radioButton-border-select)" : "var(--cmp-radioButton-border-default)",
23015
+ color: od || ro && !selected ? "var(--cmp-radioButton-content-disabled)" : selected && !ro ? "var(--cmp-radioButton-content-select)" : "var(--cmp-radioButton-content-default)"
22970
23016
  },
22971
23017
  children: o.label
22972
23018
  },