vibe-design-system 2.5.41 → 2.5.43
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/package.json
CHANGED
|
@@ -542,6 +542,7 @@ function extractComponentSuggestions() {
|
|
|
542
542
|
const pageDirs = [
|
|
543
543
|
{ dir: PAGES_DIR, prefix: "src/pages/" },
|
|
544
544
|
{ dir: APP_DIR, prefix: "src/app/" },
|
|
545
|
+
{ dir: COMPONENTS_DIR, prefix: "src/components/" },
|
|
545
546
|
];
|
|
546
547
|
const allPageFiles = [];
|
|
547
548
|
for (const { dir, prefix } of pageDirs) {
|
|
@@ -1195,6 +1196,64 @@ function extractFoundations() {
|
|
|
1195
1196
|
};
|
|
1196
1197
|
}
|
|
1197
1198
|
|
|
1199
|
+
function extractButtonUsage() {
|
|
1200
|
+
if (!fs.existsSync(SRC_DIR)) return null;
|
|
1201
|
+
const files = getAllTsxJsxInDir(SRC_DIR);
|
|
1202
|
+
if (!Array.isArray(files) || files.length === 0) return null;
|
|
1203
|
+
|
|
1204
|
+
const combos = new Map();
|
|
1205
|
+
let total = 0;
|
|
1206
|
+
|
|
1207
|
+
const extractPropLiteral = (attrs, name) => {
|
|
1208
|
+
if (!attrs) return null;
|
|
1209
|
+
// variant="primary"
|
|
1210
|
+
let re = new RegExp(name + '\\\\s*=\\\\s*["\']([^"\']+)["\']');
|
|
1211
|
+
let m = attrs.match(re);
|
|
1212
|
+
if (m) return m[1];
|
|
1213
|
+
// variant={"primary"}
|
|
1214
|
+
re = new RegExp(name + '\\\\s*=\\\\s*\\\\{\\\\s*["\']([^"\']+)["\']\\\\s*\\\\}');
|
|
1215
|
+
m = attrs.match(re);
|
|
1216
|
+
if (m) return m[1];
|
|
1217
|
+
return null;
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
for (const rel of files) {
|
|
1221
|
+
const fullPath = path.join(SRC_DIR, rel);
|
|
1222
|
+
let content;
|
|
1223
|
+
try {
|
|
1224
|
+
content = fs.readFileSync(fullPath, "utf-8");
|
|
1225
|
+
} catch (_) {
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
const re = /<Button\\b([^>]*)>/g;
|
|
1229
|
+
let m;
|
|
1230
|
+
while ((m = re.exec(content)) !== null) {
|
|
1231
|
+
total += 1;
|
|
1232
|
+
const attrs = m[1] || "";
|
|
1233
|
+
const variant = extractPropLiteral(attrs, "variant") || "default";
|
|
1234
|
+
const size = extractPropLiteral(attrs, "size") || "default";
|
|
1235
|
+
const asChild = /\\basChild\\b/.test(attrs);
|
|
1236
|
+
const key = `${variant}__${size}__${asChild ? "1" : "0"}`;
|
|
1237
|
+
if (!combos.has(key)) {
|
|
1238
|
+
combos.set(key, {
|
|
1239
|
+
key,
|
|
1240
|
+
variant,
|
|
1241
|
+
size,
|
|
1242
|
+
asChild,
|
|
1243
|
+
count: 0,
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
combos.get(key).count += 1;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
if (total === 0 || combos.size === 0) return null;
|
|
1251
|
+
return {
|
|
1252
|
+
total,
|
|
1253
|
+
combos: Array.from(combos.values()),
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1198
1257
|
function scan() {
|
|
1199
1258
|
if (!fs.existsSync(COMPONENTS_DIR)) {
|
|
1200
1259
|
console.error(CLI_LOCALES[CLI_LOCALE].componentsNotFound);
|
|
@@ -1242,6 +1301,10 @@ function scan() {
|
|
|
1242
1301
|
const foundations = extractFoundations();
|
|
1243
1302
|
foundations.icons = extractLucideIconsUsed(SRC_DIR);
|
|
1244
1303
|
foundations.brand = { assets: extractBrandAssets() };
|
|
1304
|
+
const buttonUsage = extractButtonUsage();
|
|
1305
|
+
if (buttonUsage) {
|
|
1306
|
+
foundations.buttonUsage = buttonUsage;
|
|
1307
|
+
}
|
|
1245
1308
|
const componentSuggestions = extractComponentSuggestions();
|
|
1246
1309
|
const unreleasedSectionCandidates = extractUnreleasedSectionCandidates();
|
|
1247
1310
|
const output = {
|
|
@@ -20,6 +20,9 @@ const VDS_OUTPUT = path.join(PROJECT_ROOT, "vds-output.json");
|
|
|
20
20
|
const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
21
21
|
const STORIES_DIR = path.join(SRC_DIR, "stories");
|
|
22
22
|
|
|
23
|
+
// Filled in main() after reading vds-output.json so helpers (buildSpecialStories) can see foundations data (e.g. buttonUsage).
|
|
24
|
+
let FOUNDATIONS_DATA = null;
|
|
25
|
+
|
|
23
26
|
// CSS is loaded from .storybook/preview.tsx — never add CSS import to story files.
|
|
24
27
|
|
|
25
28
|
// Components we don't want to auto-generate stories for (project-specific dashboards, heavy UIs, etc.)
|
|
@@ -478,7 +481,7 @@ const REACTNODE_PLACEHOLDER_TEXT = {
|
|
|
478
481
|
children: "Example content",
|
|
479
482
|
};
|
|
480
483
|
|
|
481
|
-
/** Component adına göre ekstra args (toString/
|
|
484
|
+
/** Component adına göre ekstra args (toString/dateRange/toFixed hatalarını önlemek için; her prop güvenli değer alır). */
|
|
482
485
|
const COMPONENT_EXTRA_ARGS = {
|
|
483
486
|
TaskEstimateInput: [
|
|
484
487
|
" task: { id: \"1\", title: \"Example\", estimate: 0, description: \"\" },",
|
|
@@ -491,6 +494,22 @@ const COMPONENT_EXTRA_ARGS = {
|
|
|
491
494
|
" step: 1,",
|
|
492
495
|
" taskId: \"1\",",
|
|
493
496
|
],
|
|
497
|
+
TimeFilterPanel: [
|
|
498
|
+
" filter: { dateRange: {}, userIds: [], tags: [], billable: \"all\" },",
|
|
499
|
+
" onFilterChange: () => {},",
|
|
500
|
+
" onSaveFilter: () => {},",
|
|
501
|
+
" onDeleteFilter: () => {},",
|
|
502
|
+
" filteredCount: 0,",
|
|
503
|
+
" savedFilters: [],",
|
|
504
|
+
],
|
|
505
|
+
TimeStats: [
|
|
506
|
+
" totalLogged: 0,",
|
|
507
|
+
" totalBillable: 0,",
|
|
508
|
+
" totalNonBillable: 0,",
|
|
509
|
+
" totalBilled: 0,",
|
|
510
|
+
" totalEstimated: 0,",
|
|
511
|
+
" projectTotalEstimated: 0,",
|
|
512
|
+
],
|
|
494
513
|
};
|
|
495
514
|
|
|
496
515
|
/** Render'da args'a uygulanacak fallback (useState(estimate.toString()) gibi kullanımlar için; args/Controls undefined yapsa bile). */
|
|
@@ -501,6 +520,8 @@ const RENDER_ARGS_FALLBACKS = {
|
|
|
501
520
|
/** Kalıcı çözüm: Storybook Docs/Controls args'ı geçmezse bile component'a güvenli props veren wrapper. Önce safeDefaults uygulanır, sonra gelen props. */
|
|
502
521
|
const SAFE_WRAPPER_DEFAULTS = {
|
|
503
522
|
TaskEstimateInput: `{ estimate: 0, onUpdate: () => {}, value: 0, task: { id: "1", title: "Example", estimate: 0 }, compact: false }`,
|
|
523
|
+
TimeFilterPanel: `{ filter: { dateRange: {}, userIds: [], tags: [], billable: "all" }, onFilterChange: () => {}, onSaveFilter: () => {}, onDeleteFilter: () => {}, filteredCount: 0, savedFilters: [] }`,
|
|
524
|
+
TimeStats: `{ totalLogged: 0, totalBillable: 0, totalNonBillable: 0, totalBilled: 0, totalEstimated: 0, projectTotalEstimated: 0 }`,
|
|
504
525
|
};
|
|
505
526
|
|
|
506
527
|
/** Recursive list of .tsx/.jsx file paths under dir (relative to dir). Index.tsx / index.tsx first for deterministic "first usage". */
|
|
@@ -825,6 +846,30 @@ function detectExportStyle(source, componentName) {
|
|
|
825
846
|
function buildSpecialStories(componentName, variants) {
|
|
826
847
|
const lines = [];
|
|
827
848
|
|
|
849
|
+
// Button: show used variant/size/asChild combinations from buttonUsage (if available)
|
|
850
|
+
if (componentName === "Button") {
|
|
851
|
+
const usage = FOUNDATIONS_DATA?.buttonUsage;
|
|
852
|
+
const combos = Array.isArray(usage?.combos) && usage.combos.length
|
|
853
|
+
? usage.combos
|
|
854
|
+
: (variants && variants.length ? variants.map((v) => ({ key: v, variant: v, size: "default", asChild: false, count: 1 })) : []);
|
|
855
|
+
lines.push(`export const UsedVariants: Story = {`);
|
|
856
|
+
lines.push(` render: () => (`);
|
|
857
|
+
lines.push(` <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>`);
|
|
858
|
+
lines.push(` {${JSON.stringify(combos)}.map((c) => (`);
|
|
859
|
+
lines.push(` <ComponentRef`);
|
|
860
|
+
lines.push(` key={c.key}`);
|
|
861
|
+
lines.push(` variant={c.variant}`);
|
|
862
|
+
lines.push(` size={c.size}`);
|
|
863
|
+
lines.push(` >`);
|
|
864
|
+
lines.push(` {c.variant}{c.size !== "default" ? " · " + c.size : ""}`);
|
|
865
|
+
lines.push(` </ComponentRef>`);
|
|
866
|
+
lines.push(` ))}`);
|
|
867
|
+
lines.push(` </div>`);
|
|
868
|
+
lines.push(` ),`);
|
|
869
|
+
lines.push(`};`);
|
|
870
|
+
return lines.join("\\n");
|
|
871
|
+
}
|
|
872
|
+
|
|
828
873
|
// Simple input-like primitives — always render real component with args
|
|
829
874
|
if (componentName === "Input") {
|
|
830
875
|
lines.push(`export const Default: Story = {`);
|
|
@@ -1295,6 +1340,58 @@ function writeFoundationsStories(foundations) {
|
|
|
1295
1340
|
fs.writeFileSync(path.join(foundationsDir, "Icons.stories.tsx"), iconsContent, "utf-8");
|
|
1296
1341
|
console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Icons.stories.tsx")));
|
|
1297
1342
|
}
|
|
1343
|
+
|
|
1344
|
+
const buttonUsage = foundations?.buttonUsage;
|
|
1345
|
+
if (buttonUsage && Array.isArray(buttonUsage.combos) && buttonUsage.combos.length > 0) {
|
|
1346
|
+
const combos = buttonUsage.combos.map((c) => ({
|
|
1347
|
+
key: c.key,
|
|
1348
|
+
variant: c.variant,
|
|
1349
|
+
size: c.size,
|
|
1350
|
+
asChild: !!c.asChild,
|
|
1351
|
+
count: c.count ?? 0,
|
|
1352
|
+
}));
|
|
1353
|
+
const buttonsContent =
|
|
1354
|
+
[
|
|
1355
|
+
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
1356
|
+
"",
|
|
1357
|
+
"const meta = { title: \"Foundations/Button Usage\" } satisfies Meta;",
|
|
1358
|
+
"export default meta;",
|
|
1359
|
+
"type Story = StoryObj;",
|
|
1360
|
+
"",
|
|
1361
|
+
`const combos = ${JSON.stringify(combos)};`,
|
|
1362
|
+
"",
|
|
1363
|
+
"export const Default: Story = {",
|
|
1364
|
+
" render: () => (",
|
|
1365
|
+
" <div style={{ padding: 24, fontFamily: \"system-ui, sans-serif\" }}>",
|
|
1366
|
+
" <h2 style={{ marginBottom: 8 }}>Used Button variants in app code</h2>",
|
|
1367
|
+
" <p style={{ marginBottom: 16, color: \"#888\", fontSize: 13 }}>Based on <Button ... /> usages in src/ (ignores lowercase <button>).</p>",
|
|
1368
|
+
" <table style={{ borderCollapse: \"collapse\", width: \"100%\", fontSize: 13 }}>",
|
|
1369
|
+
" <thead>",
|
|
1370
|
+
" <tr>",
|
|
1371
|
+
" <th style={{ textAlign: \"left\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>Variant</th>",
|
|
1372
|
+
" <th style={{ textAlign: \"left\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>Size</th>",
|
|
1373
|
+
" <th style={{ textAlign: \"left\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>asChild</th>",
|
|
1374
|
+
" <th style={{ textAlign: \"right\", padding: \"4px 8px\", borderBottom: \"1px solid #333\" }}>Count</th>",
|
|
1375
|
+
" </tr>",
|
|
1376
|
+
" </thead>",
|
|
1377
|
+
" <tbody>",
|
|
1378
|
+
" {combos.map((c) => (",
|
|
1379
|
+
" <tr key={c.key}>",
|
|
1380
|
+
" <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\" }}>{c.variant}</td>",
|
|
1381
|
+
" <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\" }}>{c.size}</td>",
|
|
1382
|
+
" <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\" }}>{c.asChild ? \"yes\" : \"no\"}</td>",
|
|
1383
|
+
" <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid #222\", textAlign: \"right\" }}>{c.count}</td>",
|
|
1384
|
+
" </tr>",
|
|
1385
|
+
" ))}",
|
|
1386
|
+
" </tbody>",
|
|
1387
|
+
" </table>",
|
|
1388
|
+
" </div>",
|
|
1389
|
+
" ),",
|
|
1390
|
+
"};",
|
|
1391
|
+
].join("\n");
|
|
1392
|
+
fs.writeFileSync(path.join(foundationsDir, "Buttons.stories.tsx"), buttonsContent, "utf-8");
|
|
1393
|
+
console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Buttons.stories.tsx")));
|
|
1394
|
+
}
|
|
1298
1395
|
}
|
|
1299
1396
|
|
|
1300
1397
|
function writeComponentSuggestionsStory(componentSuggestions) {
|
|
@@ -1323,7 +1420,7 @@ function writeComponentSuggestionsStory(componentSuggestions) {
|
|
|
1323
1420
|
" render: () => (",
|
|
1324
1421
|
" <div style={{ padding: 24, fontFamily: \"system-ui, sans-serif\" }}>",
|
|
1325
1422
|
" <h2 style={{ marginBottom: 16 }}>Visual section candidates (component suggestions)</h2>",
|
|
1326
|
-
" <p style={{ color: \"#888\", marginBottom: 24 }}>From src/pages
|
|
1423
|
+
" <p style={{ color: \"#888\", marginBottom: 24 }}>From src/pages, src/app ve src/components: kendi className kümeleri, 2+ child element, 2+ tekrar. Aynı yapı birden fazla dosyada geçiyorsa ayrı component olarak düşünün.</p>",
|
|
1327
1424
|
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 24 }}>",
|
|
1328
1425
|
" {suggestions.map((s, i) => (",
|
|
1329
1426
|
" <div key={i} style={{ border: \"1px solid #333\", borderRadius: 8, padding: 16, background: \"#111\" }}>",
|
|
@@ -1382,6 +1479,7 @@ function main() {
|
|
|
1382
1479
|
const data = JSON.parse(raw);
|
|
1383
1480
|
const components = Array.isArray(data.components) ? data.components : [];
|
|
1384
1481
|
const foundations = data.foundations || null;
|
|
1482
|
+
FOUNDATIONS_DATA = foundations;
|
|
1385
1483
|
|
|
1386
1484
|
const onlyName = process.argv[2] || null;
|
|
1387
1485
|
|