sellmate-design-system-react 9.0.0-beta.74 → 9.0.0-beta.76

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.
Files changed (40) hide show
  1. package/AGENTS.md +37 -2
  2. package/README.md +5 -5
  3. package/bin/sellmate-ds.mjs +91 -59
  4. package/dist/components/SCircleProgress/README.md +6 -3
  5. package/dist/components/SCircleProgress/SCircleProgress.d.ts +11 -3
  6. package/dist/components/SLauncherListBox/README.md +1 -1
  7. package/dist/components/SLauncherListBox/launcherListBox.config.d.ts +2 -2
  8. package/dist/components/SLinearProgress/README.md +2 -2
  9. package/dist/components/SLinearProgress/SLinearProgress.d.ts +8 -2
  10. package/dist/components/SLogo/README.md +1 -0
  11. package/dist/components/SLogo/logo.config.d.ts +1 -1
  12. package/dist/components/SSelect/README.md +18 -2
  13. package/dist/components/SSelect/SSelect.d.ts +28 -5
  14. package/dist/components/SSelect/index.d.ts +1 -1
  15. package/dist/components/STable/README.md +29 -20
  16. package/dist/components/STable/STable.d.ts +47 -31
  17. package/dist/components/STable/index.d.ts +1 -1
  18. package/dist/index.cjs +167 -50
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.js +167 -50
  21. package/dist/index.js.map +1 -1
  22. package/dist/llms-full.txt +94 -31
  23. package/dist/llms.txt +39 -3
  24. package/dist/styles.css +16 -0
  25. package/dist/theme.css +3 -0
  26. package/eslint/index.mjs +42 -42
  27. package/eslint/lib/class-names.mjs +23 -25
  28. package/eslint/lib/table-column.mjs +11 -11
  29. package/eslint/rules/component-group-gap.mjs +32 -30
  30. package/eslint/rules/divider-vertical-height.mjs +22 -22
  31. package/eslint/rules/field-width-grade.mjs +50 -50
  32. package/eslint/rules/no-arbitrary-class.mjs +63 -20
  33. package/eslint/rules/no-off-scale-spacing.mjs +61 -41
  34. package/eslint/rules/no-raw-html-control.mjs +38 -36
  35. package/eslint/rules/prefer-typo-preset.mjs +23 -17
  36. package/eslint/rules/require-locale-number.mjs +14 -19
  37. package/eslint/rules/table-column-width.mjs +21 -17
  38. package/eslint/rules/table-numeric-align.mjs +14 -14
  39. package/eslint/scale.gen.mjs +1 -1
  40. package/package.json +3 -3
@@ -7,17 +7,17 @@
7
7
  * 기본 대상 — 대응 컴포넌트가 명확해 오탐 여지가 거의 없는 요소.
8
8
  */
9
9
  const REPLACEMENTS = {
10
- button: "SButton / SGhostButton / SDropdownButton / STextLink",
11
- input: "SInput / SNumberInput / SCheckbox / SRadio / SFilePicker",
12
- select: "SSelect",
13
- textarea: "STextarea",
14
- table: "STable / SKeyValueTable",
15
- form: "SForm",
16
- dialog: "SModal.create(...) / SModal.confirm(...) / SPopup",
17
- hr: "SDivider",
18
- details: "SExpansionItem",
19
- summary: "SExpansionItem",
20
- progress: "SLinearProgress / SCircleProgress",
10
+ button: 'SButton / SGhostButton / SDropdownButton / STextLink',
11
+ input: 'SInput / SNumberInput / SCheckbox / SRadio / SFilePicker',
12
+ select: 'SSelect',
13
+ textarea: 'STextarea',
14
+ table: 'STable / SKeyValueTable',
15
+ form: 'SForm',
16
+ dialog: 'SModal.create(...) / SModal.confirm(...) / SPopup',
17
+ hr: 'SDivider',
18
+ details: 'SExpansionItem',
19
+ summary: 'SExpansionItem',
20
+ progress: 'SLinearProgress / SCircleProgress',
21
21
  };
22
22
 
23
23
  /**
@@ -27,53 +27,51 @@ const REPLACEMENTS = {
27
27
  * - label : DS 밖 컨트롤의 접근성 레이블
28
28
  */
29
29
  const STRICT_REPLACEMENTS = {
30
- ul: "SList + SListItem",
31
- ol: "SList + SListItem",
32
- li: "SListItem",
33
- svg: "SIcon",
34
- label: "SField 의 label prop",
30
+ ul: 'SList + SListItem',
31
+ ol: 'SList + SListItem',
32
+ li: 'SListItem',
33
+ svg: 'SIcon',
34
+ label: 'SField 의 label prop',
35
35
  };
36
36
 
37
37
  /** 시각 요소가 없어 대체 컴포넌트가 없는 input 타입 */
38
- const EXEMPT_INPUT_TYPES = new Set(["hidden"]);
38
+ const EXEMPT_INPUT_TYPES = new Set(['hidden']);
39
39
 
40
40
  /** JSX 속성에서 리터럴 문자열 값을 읽는다 (동적 값이면 undefined) */
41
41
  function literalAttr(node, name) {
42
- const attr = node.attributes.find(
43
- (a) => a.type === "JSXAttribute" && a.name?.name === name,
44
- );
42
+ const attr = node.attributes.find(a => a.type === 'JSXAttribute' && a.name?.name === name);
45
43
  const v = attr?.value;
46
- if (v?.type === "Literal" && typeof v.value === "string") return v.value;
44
+ if (v?.type === 'Literal' && typeof v.value === 'string') return v.value;
47
45
  return undefined;
48
46
  }
49
47
 
50
48
  /** 전역 함수 → 권장 대체 */
51
49
  const GLOBAL_CALLS = {
52
- alert: "SToast / SModal.confirm(...)",
53
- confirm: "SModal.confirm(...)",
50
+ alert: 'SToast / SModal.confirm(...)',
51
+ confirm: 'SModal.confirm(...)',
54
52
  };
55
53
 
56
54
  export default {
57
55
  meta: {
58
- type: "problem",
56
+ type: 'problem',
59
57
  docs: {
60
- description: "디자인 시스템 컴포넌트가 있는 생 HTML 컨트롤 사용 금지",
58
+ description: '디자인 시스템 컴포넌트가 있는 생 HTML 컨트롤 사용 금지',
61
59
  },
62
60
  schema: [
63
61
  {
64
- type: "object",
62
+ type: 'object',
65
63
  properties: {
66
64
  /** 예외로 허용할 요소명 (예: 로고용 svg) */
67
- allow: { type: "array", items: { type: "string" }, uniqueItems: true },
65
+ allow: { type: 'array', items: { type: 'string' }, uniqueItems: true },
68
66
  /** ul·ol·li·svg·label 까지 검사 (정당한 사용처가 있어 기본은 off) */
69
- strict: { type: "boolean" },
67
+ strict: { type: 'boolean' },
70
68
  },
71
69
  additionalProperties: false,
72
70
  },
73
71
  ],
74
72
  messages: {
75
- rawElement: "<{{name}}> 대신 {{replacement}} 를 사용하세요. (AGENTS.md §1-1)",
76
- rawCall: "{{name}}() 대신 {{replacement}} 를 사용하세요. (AGENTS.md §1-1)",
73
+ rawElement: '<{{name}}> 대신 {{replacement}} 를 사용하세요. (AGENTS.md §1-1)',
74
+ rawCall: '{{name}}() 대신 {{replacement}} 를 사용하세요. (AGENTS.md §1-1)',
77
75
  },
78
76
  },
79
77
 
@@ -86,7 +84,7 @@ export default {
86
84
 
87
85
  return {
88
86
  JSXOpeningElement(node) {
89
- if (node.name.type !== "JSXIdentifier") return;
87
+ if (node.name.type !== 'JSXIdentifier') return;
90
88
  const name = node.name.name;
91
89
  // 컴포넌트(대문자 시작)는 대상이 아니다
92
90
  if (name[0] !== name[0].toLowerCase()) return;
@@ -96,21 +94,25 @@ export default {
96
94
  if (!replacement) return;
97
95
 
98
96
  // <input type="hidden"> 처럼 시각 요소가 없는 경우는 대체 컴포넌트가 없다
99
- if (name === "input" && EXEMPT_INPUT_TYPES.has(literalAttr(node, "type"))) return;
97
+ if (name === 'input' && EXEMPT_INPUT_TYPES.has(literalAttr(node, 'type'))) return;
100
98
 
101
- context.report({ node: node.name, messageId: "rawElement", data: { name, replacement } });
99
+ context.report({ node: node.name, messageId: 'rawElement', data: { name, replacement } });
102
100
  },
103
101
 
104
102
  CallExpression(node) {
105
- if (node.callee.type !== "Identifier") return;
103
+ if (node.callee.type !== 'Identifier') return;
106
104
  const name = node.callee.name;
107
105
  const replacement = GLOBAL_CALLS[name];
108
106
  if (!replacement) return;
109
107
  // 지역 선언(사용자가 만든 confirm 등)은 제외
110
- if (context.sourceCode.getScope(node).references.some((r) => r.identifier === node.callee && r.resolved)) {
108
+ if (
109
+ context.sourceCode
110
+ .getScope(node)
111
+ .references.some(r => r.identifier === node.callee && r.resolved)
112
+ ) {
111
113
  return;
112
114
  }
113
- context.report({ node, messageId: "rawCall", data: { name, replacement } });
115
+ context.report({ node, messageId: 'rawCall', data: { name, replacement } });
114
116
  },
115
117
  };
116
118
  },
@@ -4,12 +4,19 @@
4
4
  *
5
5
  * AGENTS.md §1-3 / §2-1.
6
6
  */
7
- import { collectClassTokens, stripVariants, stripModifiers } from "../lib/class-names.mjs";
8
- import { TEXT_SIZES, TYPO_PRESETS } from "../scale.gen.mjs";
7
+ import { collectClassTokens, stripVariants, stripModifiers } from '../lib/class-names.mjs';
8
+ import { TEXT_SIZES, TYPO_PRESETS } from '../scale.gen.mjs';
9
9
 
10
10
  const FONT_WEIGHTS = new Set([
11
- "font-thin", "font-extralight", "font-light", "font-normal", "font-medium",
12
- "font-semibold", "font-bold", "font-extrabold", "font-black",
11
+ 'font-thin',
12
+ 'font-extralight',
13
+ 'font-light',
14
+ 'font-normal',
15
+ 'font-medium',
16
+ 'font-semibold',
17
+ 'font-bold',
18
+ 'font-extrabold',
19
+ 'font-black',
13
20
  ]);
14
21
 
15
22
  const TEXT_SIZE_SET = new Set(TEXT_SIZES.map(String));
@@ -17,27 +24,26 @@ const TEXT_SIZE_SET = new Set(TEXT_SIZES.map(String));
17
24
  /** 자주 쓰는 조합에 대한 추천 프리셋 (없으면 일반 안내) */
18
25
  function suggest(size) {
19
26
  const byRole = {
20
- 12: "typo-body-sm-*",
21
- 14: "typo-body-md-* / typo-heading-sm",
22
- 16: "typo-body-lg-* / typo-heading-md",
23
- 18: "typo-heading-lg",
24
- 11: "typo-body-xs-*",
27
+ 12: 'typo-body-sm-*',
28
+ 14: 'typo-body-md-* / typo-heading-sm',
29
+ 16: 'typo-body-lg-* / typo-heading-md',
30
+ 18: 'typo-heading-lg',
31
+ 11: 'typo-body-xs-*',
25
32
  };
26
- return byRole[size] ?? "typo-*";
33
+ return byRole[size] ?? 'typo-*';
27
34
  }
28
35
 
29
36
  export default {
30
37
  meta: {
31
- type: "suggestion",
38
+ type: 'suggestion',
32
39
  docs: {
33
- description: "폰트 크기·굵기 낱개 조합 대신 typo-* 프리셋 사용",
40
+ description: '폰트 크기·굵기 낱개 조합 대신 typo-* 프리셋 사용',
34
41
  },
35
42
  schema: [],
36
43
  messages: {
37
- fontSize:
38
- "`{{token}}` 대신 타이포그래피 프리셋을 쓰세요 — {{suggestion}}. (AGENTS.md §2-1)",
44
+ fontSize: '`{{token}}` 대신 타이포그래피 프리셋을 쓰세요 — {{suggestion}}. (AGENTS.md §2-1)',
39
45
  fontWeight:
40
- "`{{token}}` 을 단독으로 쓰지 말고 굵기가 포함된 typo-* 프리셋을 쓰세요. (AGENTS.md §2-1)",
46
+ '`{{token}}` 을 단독으로 쓰지 말고 굵기가 포함된 typo-* 프리셋을 쓰세요. (AGENTS.md §2-1)',
41
47
  },
42
48
  },
43
49
 
@@ -57,14 +63,14 @@ export default {
57
63
  if (sizeMatch && TEXT_SIZE_SET.has(sizeMatch[1])) {
58
64
  context.report({
59
65
  node: valueNode,
60
- messageId: "fontSize",
66
+ messageId: 'fontSize',
61
67
  data: { token, suggestion: suggest(Number(sizeMatch[1])) },
62
68
  });
63
69
  continue;
64
70
  }
65
71
 
66
72
  if (!hasPreset && FONT_WEIGHTS.has(util)) {
67
- context.report({ node: valueNode, messageId: "fontWeight", data: { token } });
73
+ context.report({ node: valueNode, messageId: 'fontWeight', data: { token } });
68
74
  }
69
75
  }
70
76
  },
@@ -7,34 +7,29 @@
7
7
  *
8
8
  * AGENTS.md §1-4 / §3-4.
9
9
  */
10
- import {
11
- NUMERIC_LABEL,
12
- isNonQuantityLabel,
13
- prop,
14
- asColumn,
15
- } from "../lib/table-column.mjs";
10
+ import { NUMERIC_LABEL, isNonQuantityLabel, prop, asColumn } from '../lib/table-column.mjs';
16
11
 
17
12
  export default {
18
13
  meta: {
19
- type: "problem",
14
+ type: 'problem',
20
15
  docs: {
21
- description: "금액·수량 등 숫자 표시에 toLocaleString 사용",
16
+ description: '금액·수량 등 숫자 표시에 toLocaleString 사용',
22
17
  },
23
18
  schema: [
24
19
  {
25
- type: "object",
20
+ type: 'object',
26
21
  properties: {
27
22
  /** 검사에서 뺄 컬럼 name */
28
- allow: { type: "array", items: { type: "string" }, uniqueItems: true },
23
+ allow: { type: 'array', items: { type: 'string' }, uniqueItems: true },
29
24
  },
30
25
  additionalProperties: false,
31
26
  },
32
27
  ],
33
28
  messages: {
34
29
  missingFormat:
35
- "숫자 컬럼 `{{label}}` 에 format 이 없습니다. `format: (v) => `${Number(v).toLocaleString()}{{unit}}`` 처럼 세 자리 구분을 넣으세요. (AGENTS.md §1-4)",
30
+ '숫자 컬럼 `{{label}}` 에 format 이 없습니다. `format: (v) => `${Number(v).toLocaleString()}{{unit}}`` 처럼 세 자리 구분을 넣으세요. (AGENTS.md §1-4)',
36
31
  missingLocale:
37
- "숫자 컬럼 `{{label}}` 의 format 이 toLocaleString 을 쓰지 않습니다. 세 자리 구분 없이 표시됩니다. (AGENTS.md §1-4)",
32
+ '숫자 컬럼 `{{label}}` 의 format 이 toLocaleString 을 쓰지 않습니다. 세 자리 구분 없이 표시됩니다. (AGENTS.md §1-4)',
38
33
  },
39
34
  },
40
35
 
@@ -54,25 +49,25 @@ export default {
54
49
 
55
50
  // render / renderCell 로 셀을 직접 그리는 경우는 이 규칙이 판단하지 않는다
56
51
  // (renderCell 은 3.x STable 의 `<td>` 커스텀 렌더 — 2.2.0 에서 추가됐다)
57
- if (prop(node, "render") || prop(node, "renderCell")) return;
52
+ if (prop(node, 'render') || prop(node, 'renderCell')) return;
58
53
 
59
54
  const unit = /금액|가격|단가|판매가|원가|비용|수수료|배송비|할인|합계|총액|잔액/.test(label)
60
- ? "원"
61
- : "";
55
+ ? '원'
56
+ : '';
62
57
 
63
- const formatProp = prop(node, "format");
58
+ const formatProp = prop(node, 'format');
64
59
  if (!formatProp) {
65
60
  context.report({
66
61
  node: labelProp,
67
- messageId: "missingFormat",
62
+ messageId: 'missingFormat',
68
63
  data: { label, unit },
69
64
  });
70
65
  return;
71
66
  }
72
67
 
73
68
  const src = context.sourceCode.getText(formatProp);
74
- if (!src.includes("toLocaleString")) {
75
- context.report({ node: formatProp, messageId: "missingLocale", data: { label } });
69
+ if (!src.includes('toLocaleString')) {
70
+ context.report({ node: formatProp, messageId: 'missingLocale', data: { label } });
76
71
  }
77
72
  },
78
73
  };
@@ -19,13 +19,13 @@
19
19
  * AGENTS.md §3-4.
20
20
  */
21
21
 
22
- import { prop, asColumnShape } from "../lib/table-column.mjs";
22
+ import { prop, asColumnShape } from '../lib/table-column.mjs';
23
23
 
24
24
  /** px 로 읽히는 폭 값인가 — 숫자 리터럴 또는 `'120px'` */
25
25
  function pxWidth(valueNode) {
26
26
  if (!valueNode) return undefined;
27
- if (valueNode.type === "Literal" && typeof valueNode.value === "number") return valueNode.value;
28
- if (valueNode.type === "Literal" && typeof valueNode.value === "string") {
27
+ if (valueNode.type === 'Literal' && typeof valueNode.value === 'number') return valueNode.value;
28
+ if (valueNode.type === 'Literal' && typeof valueNode.value === 'string') {
29
29
  const m = /^\s*(\d+)px\s*$/.exec(valueNode.value);
30
30
  return m ? Number(m[1]) : undefined;
31
31
  }
@@ -34,27 +34,27 @@ function pxWidth(valueNode) {
34
34
 
35
35
  export default {
36
36
  meta: {
37
- type: "problem",
37
+ type: 'problem',
38
38
  docs: {
39
- description: "테이블 컬럼 폭은 px 로만 주고, autoWidth 는 스페이서 열에만 쓴다",
39
+ description: '테이블 컬럼 폭은 px 로만 주고, autoWidth 는 스페이서 열에만 쓴다',
40
40
  },
41
41
  schema: [
42
42
  {
43
- type: "object",
43
+ type: 'object',
44
44
  properties: {
45
45
  /** 검사에서 뺄 컬럼 name */
46
- allow: { type: "array", items: { type: "string" }, uniqueItems: true },
46
+ allow: { type: 'array', items: { type: 'string' }, uniqueItems: true },
47
47
  },
48
48
  additionalProperties: false,
49
49
  },
50
50
  ],
51
51
  messages: {
52
52
  noWidth:
53
- "컬럼 `{{name}}` 에 `width` 도 `autoWidth` 도 없어 기본 120px 이 조용히 들어갑니다. 120px 이 맞더라도 `width: 120` 으로 적어 판단한 값임을 남기세요. (AGENTS.md §3-4)",
53
+ '컬럼 `{{name}}` 에 `width` 도 `autoWidth` 도 없어 기본 120px 이 조용히 들어갑니다. 120px 이 맞더라도 `width: 120` 으로 적어 판단한 값임을 남기세요. (AGENTS.md §3-4)',
54
54
  badWidth:
55
55
  "컬럼 `{{name}}` 의 `width: {{width}}` 는 px 이 아닙니다. 컬럼 폭은 `<col width>` 로 들어가 % · clamp() · min() 은 계산되지 않고 조용히 깨집니다 — `width: 120` 또는 `'120px'` 로 주세요. (AGENTS.md §3-4)",
56
56
  autoWidthWithContent:
57
- "컬럼 `{{name}}` 은 `{{contentProp}}` 로 값을 그리는데 `autoWidth` 입니다. `autoWidth` 는 남은 폭을 흡수하는 스페이서 열 하나에만 씁니다 — 값이 들어가는 열은 폭이 다른 열에 좌우돼 화면마다 달라집니다. px 로 폭을 명시하세요. (AGENTS.md §3-4)",
57
+ '컬럼 `{{name}}` 은 `{{contentProp}}` 로 값을 그리는데 `autoWidth` 입니다. `autoWidth` 는 남은 폭을 흡수하는 스페이서 열 하나에만 씁니다 — 값이 들어가는 열은 폭이 다른 열에 좌우돼 화면마다 달라집니다. px 로 폭을 명시하세요. (AGENTS.md §3-4)',
58
58
  },
59
59
  },
60
60
 
@@ -68,19 +68,19 @@ export default {
68
68
  const { columnName } = column;
69
69
  if (allow.has(columnName)) return;
70
70
 
71
- const widthProp = prop(node, "width");
72
- const autoWidthProp = prop(node, "autoWidth");
71
+ const widthProp = prop(node, 'width');
72
+ const autoWidthProp = prop(node, 'autoWidth');
73
73
  // autoWidth 를 변수로 넘기면 켜졌는지 알 수 없다 — 판단하지 않는다
74
74
  const autoWidthValue = autoWidthProp?.value;
75
- if (autoWidthProp && autoWidthValue?.type !== "Literal") return;
75
+ if (autoWidthProp && autoWidthValue?.type !== 'Literal') return;
76
76
  const autoWidth = autoWidthValue?.value === true;
77
77
 
78
78
  if (autoWidth) {
79
- const contentProp = prop(node, "render") ?? prop(node, "format");
79
+ const contentProp = prop(node, 'render') ?? prop(node, 'format');
80
80
  if (contentProp) {
81
81
  context.report({
82
82
  node: autoWidthProp,
83
- messageId: "autoWidthWithContent",
83
+ messageId: 'autoWidthWithContent',
84
84
  data: {
85
85
  name: columnName,
86
86
  contentProp: contentProp.key.name ?? contentProp.key.value,
@@ -91,16 +91,20 @@ export default {
91
91
  }
92
92
 
93
93
  if (!widthProp) {
94
- context.report({ node: column.nameProp, messageId: "noWidth", data: { name: columnName } });
94
+ context.report({
95
+ node: column.nameProp,
96
+ messageId: 'noWidth',
97
+ data: { name: columnName },
98
+ });
95
99
  return;
96
100
  }
97
101
 
98
102
  // 변수·계산식으로 넘긴 폭은 정적으로 알 수 없다
99
- if (widthProp.value.type !== "Literal") return;
103
+ if (widthProp.value.type !== 'Literal') return;
100
104
  if (pxWidth(widthProp.value) === undefined) {
101
105
  context.report({
102
106
  node: widthProp,
103
- messageId: "badWidth",
107
+ messageId: 'badWidth',
104
108
  data: { name: columnName, width: context.sourceCode.getText(widthProp.value) },
105
109
  });
106
110
  }
@@ -19,20 +19,20 @@ import {
19
19
  prop,
20
20
  literalString,
21
21
  asColumn,
22
- } from "../lib/table-column.mjs";
22
+ } from '../lib/table-column.mjs';
23
23
 
24
24
  export default {
25
25
  meta: {
26
- type: "problem",
26
+ type: 'problem',
27
27
  docs: {
28
- description: "테이블의 숫자 컬럼은 오른쪽 정렬",
28
+ description: '테이블의 숫자 컬럼은 오른쪽 정렬',
29
29
  },
30
30
  schema: [
31
31
  {
32
- type: "object",
32
+ type: 'object',
33
33
  properties: {
34
34
  /** 검사에서 뺄 컬럼 name */
35
- allow: { type: "array", items: { type: "string" }, uniqueItems: true },
35
+ allow: { type: 'array', items: { type: 'string' }, uniqueItems: true },
36
36
  },
37
37
  additionalProperties: false,
38
38
  },
@@ -41,7 +41,7 @@ export default {
41
41
  needsRight:
42
42
  "숫자 컬럼 `{{label}}` 은 `align: 'right'` 로 오른쪽 정렬해야 합니다{{reason}}. (AGENTS.md §3-4)",
43
43
  },
44
- fixable: "code",
44
+ fixable: 'code',
45
45
  },
46
46
 
47
47
  create(context) {
@@ -61,34 +61,34 @@ export default {
61
61
 
62
62
  // render/renderCell 로 셀을 직접 그리면 라벨만으로 숫자 여부를 알 수 없다
63
63
  // (`정산서 번호` → 링크, `순번` → 핀 아이콘). 실제 숫자 서식이 있을 때만 숫자로 본다.
64
- const renderProp = prop(node, "renderCell") ?? prop(node, "render");
64
+ const renderProp = prop(node, 'renderCell') ?? prop(node, 'render');
65
65
  if (renderProp && !NUMERIC_FORMAT.test(context.sourceCode.getText(renderProp))) return;
66
66
 
67
67
  // 숫자 판정 — label 키워드 또는 format 의 숫자 서식
68
- let reason = "";
68
+ let reason = '';
69
69
  let numeric = NUMERIC_LABEL.test(label);
70
70
 
71
- const formatProp = prop(node, "format");
71
+ const formatProp = prop(node, 'format');
72
72
  if (formatProp) {
73
73
  const src = context.sourceCode.getText(formatProp);
74
74
  if (NUMERIC_FORMAT.test(src)) {
75
75
  numeric = true;
76
- reason = " (format 이 숫자를 서식화하고 있습니다)";
76
+ reason = ' (format 이 숫자를 서식화하고 있습니다)';
77
77
  }
78
78
  }
79
79
 
80
80
  if (!numeric) return;
81
81
 
82
- const alignProp = prop(node, "align");
83
- if (literalString(alignProp) === "right") return;
82
+ const alignProp = prop(node, 'align');
83
+ if (literalString(alignProp) === 'right') return;
84
84
 
85
85
  // align 은 헤더·셀 공통이라 "헤더 가운데 + 셀 우측" 은 tdClass 로만 표현된다
86
- const tdClassProp = prop(node, "tdClass");
86
+ const tdClassProp = prop(node, 'tdClass');
87
87
  if (tdClassProp && CELL_RIGHT_CLASS.test(context.sourceCode.getText(tdClassProp))) return;
88
88
 
89
89
  context.report({
90
90
  node: alignProp ?? labelProp,
91
- messageId: "needsRight",
91
+ messageId: 'needsRight',
92
92
  data: { label, reason },
93
93
  fix(fixer) {
94
94
  if (alignProp) {
@@ -13,7 +13,7 @@ export const RADIUS = ["2","4","6","8","12","full","control-sm","control-md","fe
13
13
  export const TYPO_PRESETS = ["typo-body-lg-bold","typo-body-lg-default","typo-body-lg-medium","typo-body-lg-semibold","typo-body-md-bold","typo-body-md-default","typo-body-md-medium","typo-body-md-semibold","typo-body-sm-bold","typo-body-sm-default","typo-body-sm-medium","typo-body-sm-semibold","typo-body-xs-bold","typo-body-xs-default","typo-body-xs-medium","typo-body-xs-semibold","typo-control-lg","typo-control-md-default","typo-control-md-underline","typo-control-sm-default","typo-control-sm-underline","typo-control-xs-bold","typo-control-xs-default","typo-control-xs-medium","typo-control-xs-underline","typo-feedback-md-bold","typo-feedback-md-medium","typo-feedback-md-regular","typo-feedback-md-underline","typo-feedback-sm-bold","typo-feedback-sm-medium","typo-feedback-sm-regular","typo-feedback-sm-semibold","typo-feedback-sm-underline","typo-feedback-xs","typo-field-lg-bold","typo-field-lg-default","typo-field-lg-medium","typo-field-md-bold","typo-field-md-default","typo-field-md-medium","typo-field-sm-bold","typo-field-sm-default","typo-field-sm-medium","typo-field-xs-default","typo-heading-lg","typo-heading-md","typo-heading-sm","typo-heading-xl","typo-heading-xs","typo-heading-xxs","typo-item-md-default","typo-item-md-selected","typo-item-sm-default","typo-item-sm-selected","typo-navigation-lg-medium","typo-navigation-lg-semibold","typo-navigation-md-bold","typo-navigation-sm-bold","typo-navigation-sm-default","typo-navigation-sm-medium","typo-navigation-xs-bold","typo-navigation-xs-medium","typo-table-accent","typo-table-body","typo-table-header"];
14
14
 
15
15
  /** 색상 유틸리티 키 (text-*, bg-*, border-* 공통) */
16
- export const COLORS = ["bg-accent","bg-accentBright","bg-accentFaint","bg-accentLight","bg-brand","bg-caution","bg-danger","bg-dangerLight","bg-deep","bg-frame","bg-neutralBright","bg-neutralLight","bg-progress","bg-screen","bg-subtle","bg-success","bg-tableHeader","bg-warning","black","blue-moderate","blue-strong","blue-subtle","border-accent","border-accentLight","border-bold","border-danger","border-default","border-strong","border-success","border-warning","brilliantblue-05","brilliantblue-10","brilliantblue-20","brilliantblue-25","brilliantblue-40","brilliantblue-50","brilliantblue-60","brilliantblue-70","brilliantblue-75","brilliantblue-80","brilliantblue-85","brilliantblue-90","brilliantblue-95","brilliantblue-99","control-bg-inverseHover","danger-15","danger-20","danger-30","danger-45","danger-60","danger-70","danger-75","danger-80","danger-85","danger-90","danger-95","danger-99","danger-moderate","danger-strong","danger-subtle","darkblue-moderate","darkblue-strong","darkblue-subtle","disabled-bg","disabled-border","disabled-icon","disabled-text","divider-default","fg-accent","fg-accentLight","fg-brand","fg-danger","fg-dangerLight","fg-deep","fg-inverse","fg-primary","fg-quaternary","fg-secondary","fg-subtleCool","fg-subtleWarm","fg-success","fg-successLight","fg-tertiary","fg-warning","field-bg-default","field-bg-hover","field-bg-readOnly","field-border-danger","field-border-default","field-border-focus","field-border-hover","field-border-success","field-icon-default","field-text-default","field-text-placeholder","field-text-readOnly","green-15","green-25","green-45","green-55","green-65","green-70","green-75","green-80","green-85","green-90","green-95","green-99","green-moderate","green-strong","green-subtle","grey-05","grey-10","grey-20","grey-25","grey-30","grey-35","grey-45","grey-50","grey-55","grey-60","grey-65","grey-70","grey-80","grey-85","grey-90","grey-95","grey-moderate","grey-strong","grey-subtle","indigo-moderate","indigo-strong","indigo-subtle","lightblue-strong","lightblue-subtle","link-accent","navigation-gnb-bg-dark","navigation-gnb-bg-system","oceanblue-15","oceanblue-25","oceanblue-50","oceanblue-60","oceanblue-65","oceanblue-70","oceanblue-75","oceanblue-80","oceanblue-85","oceanblue-90","oceanblue-95","oceanblue-99","olive-05","olive-10","olive-15","olive-20","olive-30","olive-45","olive-55","olive-65","olive-70","olive-80","olive-90","olive-95","orange-10","orange-20","orange-35","orange-45","orange-55","orange-60","orange-65","orange-75","orange-85","orange-90","orange-95","orange-99","orange-moderate","orange-strong","orange-subtle","primary-05","primary-10","primary-20","primary-25","primary-40","primary-50","primary-60","primary-70","primary-75","primary-80","primary-85","primary-90","primary-95","primary-99","purple-15","purple-25","purple-45","purple-55","purple-65","purple-70","purple-75","purple-80","purple-85","purple-90","purple-95","purple-99","purple-moderate","purple-strong","purple-subtle","red-15","red-20","red-30","red-45","red-60","red-70","red-75","red-80","red-85","red-90","red-95","red-99","red-moderate","red-strong","red-subtle","steelblue-10","steelblue-25","steelblue-45","steelblue-60","steelblue-65","steelblue-70","steelblue-75","steelblue-80","steelblue-85","steelblue-90","steelblue-95","steelblue-99","success-15","success-25","success-45","success-55","success-65","success-70","success-75","success-80","success-85","success-90","success-95","success-99","success-moderate","success-strong","success-subtle","warning-10","warning-20","warning-35","warning-45","warning-55","warning-60","warning-65","warning-75","warning-85","warning-90","warning-95","warning-99","warning-moderate","warning-strong","warning-subtle","white","yellow-10","yellow-20","yellow-25","yellow-30","yellow-40","yellow-45","yellow-50","yellow-60","yellow-70","yellow-80","yellow-90","yellow-95","yellow-moderate","yellow-strong","yellow-subtle"];
16
+ export const COLORS = ["bg-accent","bg-accentBright","bg-accentFaint","bg-accentLight","bg-brand","bg-caution","bg-danger","bg-dangerLight","bg-deep","bg-frame","bg-neutralBright","bg-neutralLight","bg-progress","bg-screen","bg-subtle","bg-success","bg-tableHeader","bg-warning","black","blue-moderate","blue-strong","blue-subtle","border-accent","border-accentLight","border-bold","border-danger","border-default","border-strong","border-success","border-warning","brilliantblue-05","brilliantblue-10","brilliantblue-20","brilliantblue-25","brilliantblue-40","brilliantblue-50","brilliantblue-60","brilliantblue-70","brilliantblue-75","brilliantblue-80","brilliantblue-85","brilliantblue-90","brilliantblue-95","brilliantblue-99","control-bg-inverseHover","danger-15","danger-20","danger-30","danger-45","danger-60","danger-70","danger-75","danger-80","danger-85","danger-90","danger-95","danger-99","danger-moderate","danger-strong","danger-subtle","darkblue-moderate","darkblue-strong","darkblue-subtle","disabled-bg","disabled-border","disabled-icon","disabled-text","divider-default","fg-accent","fg-accentLight","fg-brand","fg-danger","fg-dangerLight","fg-deep","fg-inverse","fg-primary","fg-quaternary","fg-secondary","fg-subtleCool","fg-subtleWarm","fg-success","fg-successLight","fg-tertiary","fg-warning","fg-warningLight","field-bg-default","field-bg-hover","field-bg-readOnly","field-border-danger","field-border-default","field-border-focus","field-border-hover","field-border-success","field-icon-default","field-text-default","field-text-placeholder","field-text-readOnly","green-15","green-25","green-45","green-55","green-65","green-70","green-75","green-80","green-85","green-90","green-95","green-99","green-moderate","green-strong","green-subtle","grey-05","grey-10","grey-20","grey-25","grey-30","grey-35","grey-45","grey-50","grey-55","grey-60","grey-65","grey-70","grey-80","grey-85","grey-90","grey-95","grey-moderate","grey-strong","grey-subtle","indigo-moderate","indigo-strong","indigo-subtle","lightblue-strong","lightblue-subtle","link-accent","navigation-gnb-bg-dark","navigation-gnb-bg-system","oceanblue-15","oceanblue-25","oceanblue-50","oceanblue-60","oceanblue-65","oceanblue-70","oceanblue-75","oceanblue-80","oceanblue-85","oceanblue-90","oceanblue-95","oceanblue-99","olive-05","olive-10","olive-15","olive-20","olive-30","olive-45","olive-55","olive-65","olive-70","olive-80","olive-90","olive-95","orange-10","orange-20","orange-35","orange-45","orange-55","orange-60","orange-65","orange-75","orange-85","orange-90","orange-95","orange-99","orange-moderate","orange-strong","orange-subtle","primary-05","primary-10","primary-20","primary-25","primary-40","primary-50","primary-60","primary-70","primary-75","primary-80","primary-85","primary-90","primary-95","primary-99","purple-15","purple-25","purple-45","purple-55","purple-65","purple-70","purple-75","purple-80","purple-85","purple-90","purple-95","purple-99","purple-moderate","purple-strong","purple-subtle","red-15","red-20","red-30","red-45","red-60","red-70","red-75","red-80","red-85","red-90","red-95","red-99","red-moderate","red-strong","red-subtle","steelblue-10","steelblue-25","steelblue-45","steelblue-60","steelblue-65","steelblue-70","steelblue-75","steelblue-80","steelblue-85","steelblue-90","steelblue-95","steelblue-99","success-15","success-25","success-45","success-55","success-65","success-70","success-75","success-80","success-85","success-90","success-95","success-99","success-moderate","success-strong","success-subtle","warning-10","warning-20","warning-35","warning-45","warning-55","warning-60","warning-65","warning-75","warning-85","warning-90","warning-95","warning-99","warning-moderate","warning-strong","warning-subtle","white","yellow-10","yellow-20","yellow-25","yellow-30","yellow-40","yellow-45","yellow-50","yellow-60","yellow-70","yellow-80","yellow-90","yellow-95","yellow-moderate","yellow-strong","yellow-subtle"];
17
17
 
18
18
  /** 필드 폭 등급 (px). SFieldWidth 의 'xs'|'sm'|'md'|'lg'|'xl' 이 풀리는 값이다 */
19
19
  export const FIELD_WIDTHS = {"xs":80,"sm":120,"md":160,"lg":240,"xl":480};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sellmate-design-system-react",
3
- "version": "9.0.0-beta.74",
3
+ "version": "9.0.0-beta.76",
4
4
  "description": "Sellmate Design System — React (TypeScript + Tailwind v4) port",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": [
@@ -64,8 +64,8 @@
64
64
  "typecheck": "tsc --noEmit",
65
65
  "lint": "eslint \"src/**/*.{ts,tsx}\"",
66
66
  "lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
67
- "format": "prettier --check \"src/**/*.{ts,tsx,css,json,md}\"",
68
- "format:fix": "prettier --write \"src/**/*.{ts,tsx,css,json,md}\"",
67
+ "format": "prettier --check .",
68
+ "format:fix": "prettier --write .",
69
69
  "check:tokens": "node scripts/check-hardcoded.mjs",
70
70
  "check:refs": "node scripts/check-refs.mjs",
71
71
  "check:docs": "node scripts/check-docs-coverage.mjs",