sellmate-design-system-react 4.1.0 → 4.3.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/AGENTS.md +41 -6
- package/README.md +19 -3
- package/dist/components/SButton/README.md +2 -0
- package/dist/components/SDrawer/README.md +42 -0
- package/dist/components/SDrawer/SDrawer.d.ts +39 -0
- package/dist/components/SDrawer/index.d.ts +1 -0
- package/dist/components/SGhostButton/README.md +2 -0
- package/dist/components/SGnb/README.md +2 -1
- package/dist/components/SGnb/SGnb.d.ts +7 -1
- package/dist/components/SGnb/gnb.config.d.ts +23 -0
- package/dist/components/SGnb/index.d.ts +1 -1
- package/dist/index.cjs +254 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +253 -98
- package/dist/index.js.map +1 -1
- package/dist/llms-full.txt +83 -7
- package/dist/llms.txt +43 -8
- package/dist/styles.css +97 -9
- package/dist/theme.css +7 -0
- package/eslint/rules/no-off-scale-spacing.mjs +114 -35
- package/package.json +3 -1
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* 소비 앱이 원래 쓰던 Tailwind 코드이기 때문이다. 간격을 디자인 시스템 스케일로
|
|
10
10
|
* 통일하는 팀만 `requirePrefix: true` 로 켠다.
|
|
11
11
|
*
|
|
12
|
+
* 접두가 없는 클래스는 **깨진 코드가 아니라 다른 값으로 정상 동작하는 코드**다
|
|
13
|
+
* (`p-16` = 64px). 그래서 `sd-` 로 옮기는 것은 표기 정규화가 아니라 값 변경이고,
|
|
14
|
+
* 자동 수정(fix)이 아니라 제안(suggestion)으로만 낸다. 일괄 마이그레이션이
|
|
15
|
+
* 필요한 팀은 `unsafeFix: true` 로 명시적으로 켠다.
|
|
16
|
+
*
|
|
12
17
|
* AGENTS.md §2-2.
|
|
13
18
|
*/
|
|
14
19
|
import { collectClassTokens, stripVariants, stripModifiers } from "../lib/class-names.mjs";
|
|
@@ -23,28 +28,47 @@ const SPACING_PREFIXES = [
|
|
|
23
28
|
];
|
|
24
29
|
|
|
25
30
|
const ALLOWED = new Set([0, ...SPACING]);
|
|
31
|
+
const MAX = Math.max(...SPACING);
|
|
32
|
+
|
|
33
|
+
/** Tailwind 기본 스케일에서 `p-<n>` 이 실제로 만드는 px (`--spacing` = 0.25rem) */
|
|
34
|
+
const RENDERED_PX = (value) => value * 4;
|
|
26
35
|
|
|
27
36
|
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
28
37
|
|
|
38
|
+
/**
|
|
39
|
+
* 토큰 시작 경계 — 앞은 공백이거나 문자열 따옴표(= 문자열의 시작)여야 한다.
|
|
40
|
+
* `(?<![\w-])` 만 쓰면 `md:p-4 p-4` 에서 `p-4` 를 찾을 때 variant 안쪽(`md:` 뒤)이
|
|
41
|
+
* 먼저 걸려 엉뚱한 클래스를 고친다.
|
|
42
|
+
*/
|
|
43
|
+
const TOKEN_START = "(?<![^\\s'\"`])";
|
|
44
|
+
|
|
29
45
|
export default {
|
|
30
46
|
meta: {
|
|
31
47
|
type: "problem",
|
|
32
48
|
docs: {
|
|
33
49
|
description: "간격 유틸리티는 sd- 접두가 붙은 토큰 스케일 값만 사용",
|
|
34
50
|
},
|
|
51
|
+
// 스케일 밖 값(`gap-sd-13`)에는 fix 를 내지 않는다. `unsafeFix` 를 켠 팀의
|
|
52
|
+
// 접두 누락 수정에만 쓰이지만, 선언은 정적이어야 하므로 여기 남는다.
|
|
35
53
|
fixable: "code",
|
|
54
|
+
hasSuggestions: true,
|
|
36
55
|
schema: [
|
|
37
56
|
{
|
|
38
57
|
type: "object",
|
|
39
58
|
properties: {
|
|
40
59
|
/**
|
|
41
60
|
* 접두 없는 간격 유틸리티(`p-16`)도 잡는다. 기본 false.
|
|
42
|
-
*
|
|
43
|
-
* ⚠️ 자동 수정은 "클래스의 숫자 = px" 를 전제로 `p-16` → `p-sd-16` 로 바꾼다.
|
|
44
|
-
* 기본 Tailwind 스케일(`p-4` = 16px)로 쓰던 앱에서 켜면 여백이 1/4 로 줄어드니,
|
|
45
|
-
* 이전 버전의 디자인 시스템 스케일을 쓰던 앱에서만 켠다.
|
|
61
|
+
* 기본값에서는 두 해석(`p-sd-16` = 16px / 현재 값 유지)을 제안으로만 낸다.
|
|
46
62
|
*/
|
|
47
63
|
requirePrefix: { type: "boolean" },
|
|
64
|
+
/**
|
|
65
|
+
* 접두 누락을 "클래스의 숫자 = px" 로 단정하고 `p-16` → `p-sd-16` 자동 수정한다.
|
|
66
|
+
*
|
|
67
|
+
* ⚠️ **여백 값이 바뀐다.** 기본 Tailwind 스케일(`p-4` = 16px)로 쓰던 앱에서
|
|
68
|
+
* 켜면 여백이 1/4 로 줄어든다. 이전 버전의 디자인 시스템 스케일을 쓰던 앱을
|
|
69
|
+
* 한 번에 옮길 때만 켜고, 마이그레이션이 끝나면 다시 끈다.
|
|
70
|
+
*/
|
|
71
|
+
unsafeFix: { type: "boolean" },
|
|
48
72
|
},
|
|
49
73
|
additionalProperties: false,
|
|
50
74
|
},
|
|
@@ -52,32 +76,57 @@ export default {
|
|
|
52
76
|
messages: {
|
|
53
77
|
offScale:
|
|
54
78
|
"`{{token}}` 의 {{value}} 는 간격 스케일에 없어 스타일이 적용되지 않습니다. 가까운 값: {{nearest}}. (AGENTS.md §2-2)",
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"`{{token}}` 은
|
|
79
|
+
// 접두 누락 — 어느 해석이 스케일에 있느냐로 갈린다.
|
|
80
|
+
// `p-16` 은 지금 64px 이므로 "16px 의도" 와 "64px 유지" 가 서로 다른 결과다.
|
|
81
|
+
missingPrefixBoth:
|
|
82
|
+
"`{{token}}` 은 현재 {{actual}}px 입니다 (Tailwind 기본 스케일). {{value}}px 을 의도했다면 → `{{asLiteral}}` · {{actual}}px 을 유지하려면 → `{{asRendered}}` (AGENTS.md §2-2)",
|
|
83
|
+
missingPrefixLiteral:
|
|
84
|
+
"`{{token}}` 은 현재 {{actual}}px 입니다 (Tailwind 기본 스케일). {{value}}px 을 의도했다면 → `{{asLiteral}}`. {{actual}}px 유지는 간격 스케일에 값이 없습니다. {{fallback}} (AGENTS.md §2-2)",
|
|
85
|
+
missingPrefixRendered:
|
|
86
|
+
"`{{token}}` 은 현재 {{actual}}px 입니다 (Tailwind 기본 스케일). {{actual}}px 을 유지하려면 → `{{asRendered}}`. {{value}}px 은 간격 스케일에 없습니다. (AGENTS.md §2-2)",
|
|
87
|
+
missingPrefixNone:
|
|
88
|
+
"`{{token}}` 은 현재 {{actual}}px 입니다 (Tailwind 기본 스케일). {{value}}px·{{actual}}px 둘 다 간격 스케일에 없습니다. {{fallback}} (AGENTS.md §2-2)",
|
|
89
|
+
// 제안(suggestion) 설명 — 편집기 목록에 그대로 뜬다
|
|
90
|
+
useLiteral: "`{{to}}` 적용 — 숫자를 px 로 해석 ({{actual}}px → {{value}}px, 여백이 바뀝니다)",
|
|
91
|
+
useRendered: "`{{to}}` 적용 — 현재 {{actual}}px 유지",
|
|
59
92
|
},
|
|
60
93
|
},
|
|
61
94
|
|
|
62
95
|
create(context) {
|
|
63
96
|
const sourceCode = context.sourceCode ?? context.getSourceCode();
|
|
64
97
|
const requirePrefix = context.options[0]?.requirePrefix ?? false;
|
|
98
|
+
const unsafeFix = context.options[0]?.unsafeFix ?? false;
|
|
65
99
|
|
|
66
100
|
/** 토큰 문자열을 소스에서 찾아 치환하는 fixer (없으면 null) */
|
|
67
101
|
const replaceToken = (valueNode, token, fixed) => (fixer) => {
|
|
68
102
|
const text = sourceCode.getText(valueNode);
|
|
69
|
-
const idx = text.search(new RegExp(
|
|
103
|
+
const idx = text.search(new RegExp(`${TOKEN_START}${escapeRegExp(token)}(?![\\w-])`));
|
|
70
104
|
if (idx === -1) return null;
|
|
71
105
|
const start = valueNode.range[0] + idx;
|
|
72
106
|
return fixer.replaceTextRange([start, start + token.length], fixed);
|
|
73
107
|
};
|
|
74
108
|
|
|
75
|
-
/**
|
|
76
|
-
const
|
|
77
|
-
[...ALLOWED]
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
109
|
+
/** 스케일을 value 와의 거리순으로 (동률이면 작은 값 먼저) */
|
|
110
|
+
const byDistance = (value) =>
|
|
111
|
+
[...ALLOWED].sort((a, b) => Math.abs(a - value) - Math.abs(b - value) || a - b);
|
|
112
|
+
|
|
113
|
+
/** 가장 가까운 값 하나 — 거리순 배열을 다시 정렬하면 안 된다 (64 → 62 지 60 이 아니다) */
|
|
114
|
+
const nearestOne = (value) => byDistance(value)[0];
|
|
115
|
+
|
|
116
|
+
/** 스케일에서 value 에 가장 가까운 값 두 개 (표시용이라 오름차순) */
|
|
117
|
+
const nearestOf = (value) => byDistance(value).slice(0, 2).sort((a, b) => a - b);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 대응 토큰이 없을 때의 안내. 기준은 클래스에 적힌 숫자가 아니라 **실제 렌더 px** 다.
|
|
121
|
+
* 스케일 최대를 넘으면 가까운 값을 권하는 것 자체가 무의미하므로 토큰 추가로 안내한다.
|
|
122
|
+
*/
|
|
123
|
+
const fallbackFor = (prefix, renderedPx) => {
|
|
124
|
+
if (renderedPx > MAX) {
|
|
125
|
+
return `간격 스케일 최대가 ${MAX}px 이라 대응 토큰이 없습니다 — 토큰 추가를 요청하세요.`;
|
|
126
|
+
}
|
|
127
|
+
const near = nearestOne(renderedPx);
|
|
128
|
+
return `가까운 \`${prefix}-sd-${near}\`(${near}px) 를 검토하거나 토큰 추가를 요청하세요.`;
|
|
129
|
+
};
|
|
81
130
|
|
|
82
131
|
return {
|
|
83
132
|
JSXAttribute(node) {
|
|
@@ -99,31 +148,61 @@ export default {
|
|
|
99
148
|
const value = Number(raw);
|
|
100
149
|
if (value === 0) continue; // p-0 은 스케일과 무관하게 0
|
|
101
150
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
151
|
+
const rendered = RENDERED_PX(value);
|
|
152
|
+
/** `md:p-4` → `md:p-sd-16` — variant·부호 접두는 그대로 두고 뒤만 갈아낀다 */
|
|
153
|
+
const rewrite = (px) =>
|
|
154
|
+
token.replace(
|
|
155
|
+
new RegExp(`${escapeRegExp(`${prefix}-${raw}`)}$`),
|
|
156
|
+
`${prefix}-sd-${px}`,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// 두 해석 — 숫자를 px 로 읽는 쪽(값이 바뀐다)과 현재 렌더 값을 지키는 쪽.
|
|
160
|
+
// 스케일에 없는 해석은 후보에서 빠진다 (sd- 를 붙여도 스타일이 안 생긴다).
|
|
161
|
+
const asLiteral = ALLOWED.has(value) ? rewrite(value) : null;
|
|
162
|
+
const asRendered = ALLOWED.has(rendered) ? rewrite(rendered) : null;
|
|
163
|
+
|
|
164
|
+
const data = {
|
|
165
|
+
token,
|
|
166
|
+
value: raw,
|
|
167
|
+
actual: String(rendered),
|
|
168
|
+
asLiteral: asLiteral ?? "",
|
|
169
|
+
asRendered: asRendered ?? "",
|
|
170
|
+
fallback: fallbackFor(prefix, rendered),
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const suggest = [];
|
|
174
|
+
if (asLiteral) {
|
|
175
|
+
suggest.push({
|
|
176
|
+
messageId: "useLiteral",
|
|
177
|
+
data: { to: asLiteral, actual: String(rendered), value: raw },
|
|
178
|
+
fix: replaceToken(valueNode, token, asLiteral),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (asRendered) {
|
|
182
|
+
suggest.push({
|
|
183
|
+
messageId: "useRendered",
|
|
184
|
+
data: { to: asRendered, actual: String(rendered) },
|
|
185
|
+
fix: replaceToken(valueNode, token, asRendered),
|
|
114
186
|
});
|
|
115
|
-
continue;
|
|
116
187
|
}
|
|
117
188
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
189
|
+
const messageId = asLiteral
|
|
190
|
+
? asRendered
|
|
191
|
+
? "missingPrefixBoth"
|
|
192
|
+
: "missingPrefixLiteral"
|
|
193
|
+
: asRendered
|
|
194
|
+
? "missingPrefixRendered"
|
|
195
|
+
: "missingPrefixNone";
|
|
196
|
+
|
|
122
197
|
context.report({
|
|
123
198
|
node: valueNode,
|
|
124
|
-
messageId
|
|
125
|
-
data
|
|
126
|
-
|
|
199
|
+
messageId,
|
|
200
|
+
data,
|
|
201
|
+
suggest,
|
|
202
|
+
// 마이그레이션용 옵트인 — "숫자 = px" 를 단정하므로 여백 값이 바뀐다
|
|
203
|
+
...(unsafeFix && asLiteral
|
|
204
|
+
? { fix: replaceToken(valueNode, token, asLiteral) }
|
|
205
|
+
: {}),
|
|
127
206
|
});
|
|
128
207
|
continue;
|
|
129
208
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sellmate-design-system-react",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Sellmate Design System — React (TypeScript + Tailwind v4) port",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"keywords": [
|
|
@@ -67,6 +67,8 @@
|
|
|
67
67
|
"format:fix": "prettier --write \"src/**/*.{ts,tsx,css,json,md}\"",
|
|
68
68
|
"check:tokens": "node scripts/check-hardcoded.mjs",
|
|
69
69
|
"check:refs": "node scripts/check-refs.mjs",
|
|
70
|
+
"check:docs": "node scripts/check-docs-coverage.mjs",
|
|
71
|
+
"check:docs-refs": "node scripts/check-docs-refs.mjs",
|
|
70
72
|
"version": "node scripts/version.mjs",
|
|
71
73
|
"version:dry": "node scripts/version.mjs --dry-run",
|
|
72
74
|
"clean": "rm -rf dist storybook-static"
|