@shokirovr16/frontend-library 0.1.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.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/bin/cmfrt.js +69 -0
- package/package.json +47 -0
- package/src/auth/README.md +193 -0
- package/src/auth/core/AuthEngine.js +623 -0
- package/src/auth/core/OidcClient.js +79 -0
- package/src/auth/core/OidcDiscovery.js +17 -0
- package/src/auth/core/Pkce.js +18 -0
- package/src/auth/events/AuthEventBus.js +22 -0
- package/src/auth/http/authFetch.js +32 -0
- package/src/auth/http/createAuthHttpClient.js +42 -0
- package/src/auth/index.js +90 -0
- package/src/auth/permissions/ClaimsNormalizer.js +69 -0
- package/src/auth/permissions/permissions.js +26 -0
- package/src/auth/react/AuthProvider.js +34 -0
- package/src/auth/react/guards/RequireAuth.js +35 -0
- package/src/auth/react/guards/RequirePermission.js +16 -0
- package/src/auth/react/guards/withAuthGuard.js +12 -0
- package/src/auth/react/hooks/useRequireAuth.js +24 -0
- package/src/auth/react/index.js +6 -0
- package/src/auth/react/useAuth.js +29 -0
- package/src/auth/silent/silentCallback.js +42 -0
- package/src/auth/singleton.js +22 -0
- package/src/auth/storage/InMemoryTokenStore.js +56 -0
- package/src/auth/storage/TransactionStore.js +51 -0
- package/src/auth/sync/BroadcastChannelSync.js +29 -0
- package/src/auth/tenancy/TenantResolver.js +39 -0
- package/src/auth/types.js +113 -0
- package/src/auth/utils/base64url.js +15 -0
- package/src/auth/utils/jwt.js +26 -0
- package/src/auth/utils/random.js +13 -0
- package/src/auth/utils/url.js +27 -0
- package/src/commands/add.js +80 -0
- package/src/commands/init.js +113 -0
- package/src/commands/list.js +92 -0
- package/src/commands/remove.js +150 -0
- package/src/commands/status.js +96 -0
- package/src/commands/theme.js +47 -0
- package/src/commands/uninstall.js +198 -0
- package/src/commands/update.js +151 -0
- package/src/lib/config.js +55 -0
- package/src/lib/fs.js +13 -0
- package/src/lib/packageManager.js +30 -0
- package/src/lib/paths.js +14 -0
- package/src/lib/registry.js +11 -0
- package/src/lib/styles.js +223 -0
- package/src/lib/targets.js +15 -0
- package/src/lib/theme.js +102 -0
- package/templates/docs/cmfrt-doc.md +82 -0
- package/templates/lib/utils.js +6 -0
- package/templates/registry.json +42 -0
- package/templates/styles/theme.cjs +832 -0
- package/templates/styles/type-utilities.css +136 -0
- package/templates/styles/type-utility-classes.css +138 -0
- package/templates/styles/variables.css +1560 -0
- package/templates/styles/variables.json +6870 -0
- package/templates/ui/button.jsx +117 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { copyFile, ensureDirectory, pathExists, readFile, writeFile } from './fs.js';
|
|
4
|
+
import { projectRoot, templatePath } from './paths.js';
|
|
5
|
+
import { resolveTargetRoot } from './targets.js';
|
|
6
|
+
|
|
7
|
+
const { cyan, green, yellow } = pc;
|
|
8
|
+
|
|
9
|
+
const TAILWIND_CONFIG_CANDIDATES = [
|
|
10
|
+
'tailwind.config.js',
|
|
11
|
+
'tailwind.config.cjs',
|
|
12
|
+
'tailwind.config.mjs',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
async function detectTailwindConfig(root) {
|
|
16
|
+
for (const filename of TAILWIND_CONFIG_CANDIDATES) {
|
|
17
|
+
const configPath = path.resolve(root, filename);
|
|
18
|
+
if (await pathExists(configPath)) {
|
|
19
|
+
return configPath;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ensureRelativePath(p) {
|
|
26
|
+
if (p.startsWith('.')) return p;
|
|
27
|
+
return `./${p}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function ensureThemeImport(content, configPath, stylesPath) {
|
|
31
|
+
const rel = ensureRelativePath(path.relative(path.dirname(configPath), path.resolve(projectRoot(), stylesPath, 'theme.cjs')));
|
|
32
|
+
if (content.includes('theme.cjs') && content.match(/\btheme\b/)) {
|
|
33
|
+
return content;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (content.includes('module.exports')) {
|
|
37
|
+
return `const theme = require("${rel}");\n${content}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (content.includes('export default')) {
|
|
41
|
+
const importLine = `import theme from "${rel}";\n`;
|
|
42
|
+
if (content.startsWith('import ')) {
|
|
43
|
+
const firstExport = content.indexOf('export default');
|
|
44
|
+
const beforeExport = content.slice(0, firstExport);
|
|
45
|
+
if (beforeExport.includes(importLine.trim())) return content;
|
|
46
|
+
return beforeExport + importLine + content.slice(firstExport);
|
|
47
|
+
}
|
|
48
|
+
return importLine + content;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return content;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ensureThemeColors(content) {
|
|
55
|
+
if (content.match(/colors\s*:\s*theme\.colors/)) return content;
|
|
56
|
+
|
|
57
|
+
if (content.match(/extend\s*:\s*{/)) {
|
|
58
|
+
return content.replace(/extend\s*:\s*{/, (match) => `${match}\n colors: theme.colors,`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (content.match(/theme\s*:\s*{/)) {
|
|
62
|
+
return content.replace(/theme\s*:\s*{/, (match) => `${match}\n extend: { colors: theme.colors },`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (content.match(/export\s+default\s*{/) || content.match(/module\.exports\s*=\s*{/)) {
|
|
66
|
+
return content.replace(/({\s*)/, (match) => `${match}\n theme: { extend: { colors: theme.colors } },`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return content;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function ensureTailwindSetup(config) {
|
|
73
|
+
const root = projectRoot();
|
|
74
|
+
const configPath = await detectTailwindConfig(root);
|
|
75
|
+
if (!configPath) {
|
|
76
|
+
const stylesRoot = path.resolve(root, config.stylesPath || 'src/styles');
|
|
77
|
+
const relTheme = ensureRelativePath(path.relative(root, path.join(stylesRoot, 'theme.cjs')));
|
|
78
|
+
const fallbackPath = path.resolve(root, 'tailwind.config.cjs');
|
|
79
|
+
const content = `const theme = require("${relTheme}");\n\nmodule.exports = {\n content: [\n "./index.html",\n "./src/**/*.{js,jsx,ts,tsx}",\n ],\n theme: {\n extend: {\n colors: theme.colors,\n },\n },\n plugins: [],\n};\n`;
|
|
80
|
+
|
|
81
|
+
await writeFile(fallbackPath, content);
|
|
82
|
+
console.log(green('Tailwind config created (tailwind.config.cjs) with styles/theme.cjs colors.'));
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const raw = await readFile(configPath, 'utf8');
|
|
87
|
+
let next = raw;
|
|
88
|
+
next = ensureThemeImport(next, configPath, config.stylesPath || 'src/styles');
|
|
89
|
+
next = ensureThemeColors(next);
|
|
90
|
+
|
|
91
|
+
if (next !== raw) {
|
|
92
|
+
await writeFile(configPath, next);
|
|
93
|
+
console.log(green('Tailwind config updated with styles/theme.cjs colors.'));
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function ensureCssImports(config) {
|
|
101
|
+
const root = projectRoot();
|
|
102
|
+
const candidates = ['src/index.css', 'src/main.css', 'src/app.css'];
|
|
103
|
+
let cssPath = null;
|
|
104
|
+
for (const candidate of candidates) {
|
|
105
|
+
const full = path.resolve(root, candidate);
|
|
106
|
+
if (await pathExists(full)) {
|
|
107
|
+
cssPath = full;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!cssPath) {
|
|
112
|
+
console.log(yellow('Global CSS file not found (looked for src/index.css, src/main.css, src/app.css).'));
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const relStylesPath = ensureRelativePath(path.relative(path.dirname(cssPath), path.resolve(root, config.stylesPath || 'src/styles')));
|
|
117
|
+
const variablesImport = `@import "${relStylesPath}/variables.css";`;
|
|
118
|
+
const utilitiesImport = `@import "${relStylesPath}/type-utility-classes.css";`;
|
|
119
|
+
|
|
120
|
+
const raw = await readFile(cssPath, 'utf8');
|
|
121
|
+
let next = raw;
|
|
122
|
+
if (!raw.includes(variablesImport)) {
|
|
123
|
+
next = `${variablesImport}\n${next}`;
|
|
124
|
+
}
|
|
125
|
+
if (!next.includes(utilitiesImport)) {
|
|
126
|
+
next = `${utilitiesImport}\n${next}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (next !== raw) {
|
|
130
|
+
await writeFile(cssPath, next);
|
|
131
|
+
console.log(green(`Global CSS updated with token imports (${path.relative(root, cssPath)}).`));
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseVersion(version) {
|
|
138
|
+
if (!version) return [0, 0, 0];
|
|
139
|
+
return String(version)
|
|
140
|
+
.split('.')
|
|
141
|
+
.map((part) => Number.parseInt(part, 10))
|
|
142
|
+
.map((num) => (Number.isNaN(num) ? 0 : num));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function compareSemver(a, b) {
|
|
146
|
+
const left = parseVersion(a);
|
|
147
|
+
const right = parseVersion(b);
|
|
148
|
+
const length = Math.max(left.length, right.length, 3);
|
|
149
|
+
for (let i = 0; i < length; i += 1) {
|
|
150
|
+
const l = left[i] ?? 0;
|
|
151
|
+
const r = right[i] ?? 0;
|
|
152
|
+
if (l > r) return 1;
|
|
153
|
+
if (l < r) return -1;
|
|
154
|
+
}
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function installEntryFiles(entry, targetRoot, { overwrite }) {
|
|
159
|
+
const installedFiles = [];
|
|
160
|
+
for (const file of entry.files ?? []) {
|
|
161
|
+
const source = templatePath(file.source);
|
|
162
|
+
const destination = path.resolve(targetRoot, file.target);
|
|
163
|
+
await ensureDirectory(path.dirname(destination));
|
|
164
|
+
await copyFile(source, destination, { overwrite });
|
|
165
|
+
installedFiles.push(path.relative(targetRoot, destination));
|
|
166
|
+
}
|
|
167
|
+
return installedFiles;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function ensureStylesInstalled(registry, config) {
|
|
171
|
+
const entry = registry.styles;
|
|
172
|
+
if (!entry) return { updated: false, config };
|
|
173
|
+
|
|
174
|
+
const targetRoot = resolveTargetRoot(entry, config);
|
|
175
|
+
const installed = config.installed?.styles;
|
|
176
|
+
const registryVersion = entry.version ?? '0.0.0';
|
|
177
|
+
|
|
178
|
+
if (!installed) {
|
|
179
|
+
console.log(cyan('Installing design tokens (styles)...'));
|
|
180
|
+
const files = await installEntryFiles(entry, targetRoot, { overwrite: true });
|
|
181
|
+
const next = {
|
|
182
|
+
...config,
|
|
183
|
+
installed: {
|
|
184
|
+
...config.installed,
|
|
185
|
+
styles: {
|
|
186
|
+
version: registryVersion,
|
|
187
|
+
files,
|
|
188
|
+
installedAt: new Date().toISOString(),
|
|
189
|
+
updatedAt: new Date().toISOString(),
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
console.log(green('Styles installed.'));
|
|
194
|
+
await ensureTailwindSetup(config);
|
|
195
|
+
await ensureCssImports(config);
|
|
196
|
+
return { updated: true, config: next };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const installedVersion = installed.version ?? '0.0.0';
|
|
200
|
+
if (compareSemver(installedVersion, registryVersion) >= 0) {
|
|
201
|
+
return { updated: false, config };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log(cyan('Updating design tokens (styles)...'));
|
|
205
|
+
const files = await installEntryFiles(entry, targetRoot, { overwrite: true });
|
|
206
|
+
const next = {
|
|
207
|
+
...config,
|
|
208
|
+
installed: {
|
|
209
|
+
...config.installed,
|
|
210
|
+
styles: {
|
|
211
|
+
...installed,
|
|
212
|
+
version: registryVersion,
|
|
213
|
+
files,
|
|
214
|
+
installedAt: installed.installedAt ?? new Date().toISOString(),
|
|
215
|
+
updatedAt: new Date().toISOString(),
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
console.log(yellow('Styles updated.'));
|
|
220
|
+
await ensureTailwindSetup(config);
|
|
221
|
+
await ensureCssImports(config);
|
|
222
|
+
return { updated: true, config: next };
|
|
223
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { componentsDir, projectRoot } from './paths.js';
|
|
3
|
+
|
|
4
|
+
export function resolveTargetRoot(entry, config) {
|
|
5
|
+
if (entry?.targetRoot === 'styles') {
|
|
6
|
+
const stylesPath = config.stylesPath || 'src/styles';
|
|
7
|
+
return path.resolve(projectRoot(), stylesPath);
|
|
8
|
+
}
|
|
9
|
+
if (entry?.targetRoot) {
|
|
10
|
+
return path.resolve(projectRoot(), entry.targetRoot);
|
|
11
|
+
}
|
|
12
|
+
return config.componentsPath
|
|
13
|
+
? path.resolve(projectRoot(), config.componentsPath)
|
|
14
|
+
: componentsDir();
|
|
15
|
+
}
|
package/src/lib/theme.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { pathExists, readFile, readJson, writeFile } from './fs.js';
|
|
3
|
+
import { projectRoot, templatePath } from './paths.js';
|
|
4
|
+
|
|
5
|
+
function safeName(s) {
|
|
6
|
+
return String(s || '')
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/\s+/g, '-')
|
|
9
|
+
.replace(/[^a-zA-Z0-9-_]/g, '')
|
|
10
|
+
.toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function modesFromVariablesJson(filePath) {
|
|
14
|
+
if (!(await pathExists(filePath))) return [];
|
|
15
|
+
const json = await readJson(filePath);
|
|
16
|
+
const modes = new Set();
|
|
17
|
+
for (const collection of json.collections ?? []) {
|
|
18
|
+
for (const mode of collection.modes ?? []) {
|
|
19
|
+
if (mode?.name) modes.add(mode.name);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return Array.from(modes).map((name) => ({ name, safe: safeName(name) }));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function modesFromVariablesCss(filePath) {
|
|
26
|
+
if (!(await pathExists(filePath))) return [];
|
|
27
|
+
const raw = await readFile(filePath, 'utf8');
|
|
28
|
+
const matches = raw.matchAll(/\.theme-([a-z0-9-]+)\s*\{/g);
|
|
29
|
+
const modes = new Set();
|
|
30
|
+
for (const match of matches) {
|
|
31
|
+
if (match[1]) modes.add(match[1]);
|
|
32
|
+
}
|
|
33
|
+
return Array.from(modes).map((safe) => ({ name: safe, safe }));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function getAvailableModes(config = {}) {
|
|
37
|
+
const stylesPath = config.stylesPath || 'src/styles';
|
|
38
|
+
const root = projectRoot();
|
|
39
|
+
|
|
40
|
+
const projectJson = path.resolve(root, stylesPath, 'variables.json');
|
|
41
|
+
const projectCss = path.resolve(root, stylesPath, 'variables.css');
|
|
42
|
+
const templateJson = templatePath('styles', 'variables.json');
|
|
43
|
+
|
|
44
|
+
let modes = await modesFromVariablesJson(projectJson);
|
|
45
|
+
if (modes.length === 0) {
|
|
46
|
+
modes = await modesFromVariablesCss(projectCss);
|
|
47
|
+
}
|
|
48
|
+
if (modes.length === 0) {
|
|
49
|
+
modes = await modesFromVariablesJson(templateJson);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return modes;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resolveMode(modes, input) {
|
|
56
|
+
if (!input) return null;
|
|
57
|
+
const normalized = String(input).trim().toLowerCase();
|
|
58
|
+
return (
|
|
59
|
+
modes.find((m) => m.name.toLowerCase() === normalized) ||
|
|
60
|
+
modes.find((m) => m.safe.toLowerCase() === normalized) ||
|
|
61
|
+
modes.find((m) => safeName(m.name) === normalized) ||
|
|
62
|
+
null
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function applyThemeMode(modeSafe) {
|
|
67
|
+
const root = projectRoot();
|
|
68
|
+
const htmlPath = path.resolve(root, 'index.html');
|
|
69
|
+
if (!(await pathExists(htmlPath))) {
|
|
70
|
+
return { updated: false, message: 'index.html not found.' };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const raw = await readFile(htmlPath, 'utf8');
|
|
74
|
+
const match = raw.match(/<html[^>]*>/i);
|
|
75
|
+
if (!match) {
|
|
76
|
+
return { updated: false, message: 'No <html> tag found in index.html.' };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const htmlTag = match[0];
|
|
80
|
+
const classMatch = htmlTag.match(/class\s*=\s*["']([^"']*)["']/i);
|
|
81
|
+
const current = classMatch ? classMatch[1] : '';
|
|
82
|
+
const nextClasses = current
|
|
83
|
+
.split(/\s+/)
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.filter((c) => !c.startsWith('theme-'))
|
|
86
|
+
.concat(modeSafe ? [`theme-${modeSafe}`] : []);
|
|
87
|
+
|
|
88
|
+
let nextTag = htmlTag;
|
|
89
|
+
if (classMatch) {
|
|
90
|
+
nextTag = htmlTag.replace(classMatch[0], `class=\"${nextClasses.join(' ')}\"`);
|
|
91
|
+
} else if (modeSafe) {
|
|
92
|
+
nextTag = htmlTag.replace(/<html/i, `<html class=\"${nextClasses.join(' ')}\"`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const next = raw.replace(htmlTag, nextTag);
|
|
96
|
+
if (next !== raw) {
|
|
97
|
+
await writeFile(htmlPath, next);
|
|
98
|
+
return { updated: true };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { updated: false };
|
|
102
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# cmfrt Docs (Quick Start + Troubleshooting)
|
|
2
|
+
|
|
3
|
+
## Quick Start
|
|
4
|
+
1. `cmfrt init`
|
|
5
|
+
2. `cmfrt add button`
|
|
6
|
+
3. Use the component:
|
|
7
|
+
|
|
8
|
+
```jsx
|
|
9
|
+
import { Button } from "./src/components/ui/button";
|
|
10
|
+
|
|
11
|
+
export default function App() {
|
|
12
|
+
return (
|
|
13
|
+
<Button tone="neutral" type="filled">
|
|
14
|
+
Button
|
|
15
|
+
</Button>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Required Setup (Tokens + Tailwind)
|
|
21
|
+
Make sure your Tailwind config extends colors from `styles/theme.cjs`:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
const theme = require("./src/styles/theme.cjs");
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}"],
|
|
28
|
+
theme: {
|
|
29
|
+
extend: {
|
|
30
|
+
colors: theme.colors,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
plugins: [],
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
And your global CSS imports token files:
|
|
38
|
+
|
|
39
|
+
```css
|
|
40
|
+
@import "tailwindcss";
|
|
41
|
+
@import "./styles/variables.css";
|
|
42
|
+
@import "./styles/type-utility-classes.css";
|
|
43
|
+
@config "../tailwind.config.cjs";
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Common Issues
|
|
47
|
+
### 1) Token classes not working
|
|
48
|
+
Symptoms: `bg-colorbase-...` classes exist but no color shows.
|
|
49
|
+
Fix:
|
|
50
|
+
- Ensure `styles/variables.css` is imported in global CSS.
|
|
51
|
+
- Ensure Tailwind config extends colors from `styles/theme.cjs`.
|
|
52
|
+
|
|
53
|
+
### 2) Padding/class not applied
|
|
54
|
+
If you add `p-4` manually but it does not show:
|
|
55
|
+
- `size` classes might override it via `twMerge`.
|
|
56
|
+
- Put your custom class after `sizeClass`, or remove size.
|
|
57
|
+
|
|
58
|
+
### 3) Filled vs flled typo
|
|
59
|
+
Make sure `type="filled"`. The component normalizes `flled`, but `filled` is correct.
|
|
60
|
+
|
|
61
|
+
### 4) Disabled styles not showing
|
|
62
|
+
Disabled styles only apply when `disabled={true}` is set on `<Button />`.
|
|
63
|
+
|
|
64
|
+
## Theme Mode
|
|
65
|
+
List modes:
|
|
66
|
+
```
|
|
67
|
+
cmfrt theme --list
|
|
68
|
+
```
|
|
69
|
+
Set mode:
|
|
70
|
+
```
|
|
71
|
+
cmfrt theme --set "Client light"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Uninstall
|
|
75
|
+
To remove a component:
|
|
76
|
+
```
|
|
77
|
+
cmfrt uninstall button
|
|
78
|
+
```
|
|
79
|
+
Remove everything:
|
|
80
|
+
```
|
|
81
|
+
cmfrt uninstall --all
|
|
82
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"button": {
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"category": "forms",
|
|
5
|
+
"description": "Clickable button with variants",
|
|
6
|
+
"files": [
|
|
7
|
+
{
|
|
8
|
+
"source": "ui/button.jsx",
|
|
9
|
+
"target": "button.jsx"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"dependencies": [
|
|
13
|
+
"clsx",
|
|
14
|
+
"tailwind-merge"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"styles": {
|
|
18
|
+
"version": "1.0.3",
|
|
19
|
+
"category": "styles",
|
|
20
|
+
"description": "Design tokens and global style utilities",
|
|
21
|
+
"targetRoot": "styles",
|
|
22
|
+
"files": [
|
|
23
|
+
{
|
|
24
|
+
"source": "styles/variables.css",
|
|
25
|
+
"target": "variables.css"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"source": "styles/theme.cjs",
|
|
29
|
+
"target": "theme.cjs"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"source": "styles/type-utilities.css",
|
|
33
|
+
"target": "type-utilities.css"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"source": "styles/type-utility-classes.css",
|
|
37
|
+
"target": "type-utility-classes.css"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"dependencies": []
|
|
41
|
+
}
|
|
42
|
+
}
|