@keenmate/pure-admin-core 2.3.6 → 2.5.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.
Files changed (47) hide show
  1. package/README.md +23 -29
  2. package/dist/css/main.css +68 -148
  3. package/package.json +1 -5
  4. package/snippets/AUDIT.md +94 -0
  5. package/snippets/alerts.html +264 -89
  6. package/snippets/badges.html +193 -61
  7. package/snippets/buttons.html +178 -0
  8. package/snippets/callouts.html +210 -129
  9. package/snippets/cards.html +383 -200
  10. package/snippets/checkbox-lists.html +199 -65
  11. package/snippets/code.html +55 -11
  12. package/snippets/command-palette.html +401 -111
  13. package/snippets/comparison.html +144 -93
  14. package/snippets/customization.html +311 -104
  15. package/snippets/data-display.html +584 -0
  16. package/snippets/detail-panel.html +470 -138
  17. package/snippets/filter-card.html +246 -0
  18. package/snippets/forms.html +408 -308
  19. package/snippets/grid.html +253 -141
  20. package/snippets/layout.html +379 -480
  21. package/snippets/lists.html +144 -47
  22. package/snippets/loaders.html +64 -39
  23. package/snippets/manifest.json +330 -280
  24. package/snippets/modal-dialogs.html +137 -64
  25. package/snippets/modals.html +221 -151
  26. package/snippets/notifications.html +285 -0
  27. package/snippets/popconfirm.html +213 -19
  28. package/snippets/profile.html +290 -330
  29. package/snippets/statistics.html +247 -0
  30. package/snippets/tables.html +359 -150
  31. package/snippets/tabs.html +129 -45
  32. package/snippets/timeline.html +123 -56
  33. package/snippets/toasts.html +179 -31
  34. package/snippets/tooltips.html +199 -81
  35. package/snippets/typography.html +183 -58
  36. package/snippets/utilities.html +511 -415
  37. package/snippets/virtual-scroll.html +201 -75
  38. package/snippets/web-daterangepicker.html +369 -189
  39. package/snippets/web-multiselect.html +360 -124
  40. package/src/scss/core-components/_alerts.scss +51 -12
  41. package/src/scss/core-components/_pagers.scss +1 -1
  42. package/src/scss/core-components/_popconfirm.scss +35 -13
  43. package/src/scss/core-components/_profile.scss +18 -8
  44. package/src/scss/core-components/_statistics.scss +12 -12
  45. package/src/scss/core-components/_tables.scss +2 -134
  46. package/src/scss/variables/_components.scss +17 -2
  47. package/scripts/download-themes.js +0 -351
@@ -1,351 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Download themes from pureadmin.io
5
- *
6
- * Reads themes.json + .themes.json (local override) and downloads
7
- * themes that don't have a local "path" set.
8
- *
9
- * Format:
10
- * {
11
- * "themes": {
12
- * "audi": {}, // download from pureadmin.io
13
- * "corporate": { "path": "../my-themes/corp" }, // use local path
14
- * "custom": { "url": "https://..." } // download from custom URL
15
- * }
16
- * }
17
- *
18
- * Downloaded themes are saved to ./themes/{name}/dist/{name}.css
19
- */
20
-
21
- const fs = require('fs');
22
- const path = require('path');
23
- const https = require('https');
24
- const http = require('http');
25
-
26
- const BUNDLE_URL = 'https://pureadmin.io/api/bundle';
27
- const PROJECT_ROOT = process.cwd();
28
- const THEMES_DIR = path.join(PROJECT_ROOT, 'themes');
29
- const THEMES_CONFIG = path.join(PROJECT_ROOT, 'themes.json');
30
- const THEMES_LOCAL = path.join(PROJECT_ROOT, '.themes.json');
31
-
32
- function download(url) {
33
- return new Promise((resolve, reject) => {
34
- const client = url.startsWith('https') ? https : http;
35
-
36
- client.get(url, (res) => {
37
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
38
- return download(res.headers.location).then(resolve).catch(reject);
39
- }
40
-
41
- if (res.statusCode !== 200) {
42
- reject(new Error(`HTTP ${res.statusCode} for ${url}`));
43
- return;
44
- }
45
-
46
- const chunks = [];
47
- res.on('data', chunk => chunks.push(chunk));
48
- res.on('end', () => resolve(Buffer.concat(chunks)));
49
- res.on('error', reject);
50
- }).on('error', reject);
51
- });
52
- }
53
-
54
- function extractZip(buffer, destDir) {
55
- const tmpFile = path.join(destDir, '..', '_tmp_theme.zip');
56
- if (!fs.existsSync(path.dirname(tmpFile))) {
57
- fs.mkdirSync(path.dirname(tmpFile), { recursive: true });
58
- }
59
- fs.writeFileSync(tmpFile, buffer);
60
-
61
- const { execSync } = require('child_process');
62
- try {
63
- execSync(`unzip -o "${tmpFile}" -d "${destDir}"`, { stdio: 'ignore' });
64
- } catch (e) {
65
- try {
66
- execSync(`powershell -Command "Expand-Archive -Force -Path '${tmpFile}' -DestinationPath '${destDir}'"`, { stdio: 'ignore' });
67
- } catch (e2) {
68
- fs.unlinkSync(tmpFile);
69
- throw new Error('Could not extract zip. Install unzip or use PowerShell.');
70
- }
71
- }
72
- fs.unlinkSync(tmpFile);
73
- }
74
-
75
- function findThemeCss(dir, themeName) {
76
- const entries = fs.readdirSync(dir, { withFileTypes: true });
77
-
78
- for (const entry of entries) {
79
- const fullPath = path.join(dir, entry.name);
80
-
81
- if (entry.isFile() && entry.name === `${themeName}.css`) {
82
- return fullPath;
83
- }
84
-
85
- if (entry.isDirectory()) {
86
- const found = findThemeCss(fullPath, themeName);
87
- if (found) return found;
88
- }
89
- }
90
-
91
- return null;
92
- }
93
-
94
- function getThemeVersion(themeDir) {
95
- const manifestPath = path.join(themeDir, 'theme.json');
96
- if (fs.existsSync(manifestPath)) {
97
- try {
98
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
99
- return manifest.version || null;
100
- } catch (e) {
101
- return null;
102
- }
103
- }
104
- return null;
105
- }
106
-
107
- function copyDir(src, dest) {
108
- if (!fs.existsSync(dest)) {
109
- fs.mkdirSync(dest, { recursive: true });
110
- }
111
-
112
- const entries = fs.readdirSync(src, { withFileTypes: true });
113
- for (const entry of entries) {
114
- const srcPath = path.join(src, entry.name);
115
- const destPath = path.join(dest, entry.name);
116
-
117
- if (entry.isDirectory()) {
118
- copyDir(srcPath, destPath);
119
- } else {
120
- fs.copyFileSync(srcPath, destPath);
121
- }
122
- }
123
- }
124
-
125
- async function main() {
126
- // Load themes.json (base) + .themes.json (local overrides)
127
- let themes = {};
128
-
129
- if (fs.existsSync(THEMES_CONFIG)) {
130
- const base = JSON.parse(fs.readFileSync(THEMES_CONFIG, 'utf-8'));
131
- themes = { ...themes, ...(base.themes || {}) };
132
- console.log(`Loaded themes.json (${Object.keys(base.themes || {}).length} theme(s))`);
133
- }
134
-
135
- if (fs.existsSync(THEMES_LOCAL)) {
136
- const local = JSON.parse(fs.readFileSync(THEMES_LOCAL, 'utf-8'));
137
- themes = { ...themes, ...(local.themes || {}) };
138
- console.log(`Loaded .themes.json (${Object.keys(local.themes || {}).length} override(s))`);
139
- }
140
-
141
- if (Object.keys(themes).length === 0) {
142
- console.error('No themes found. Create themes.json or .themes.json, e.g.:');
143
- console.error(' { "themes": { "audi": {}, "corporate": {} } }');
144
- process.exit(1);
145
- }
146
-
147
- console.log('');
148
-
149
- // Ensure themes directory exists
150
- if (!fs.existsSync(THEMES_DIR)) {
151
- fs.mkdirSync(THEMES_DIR, { recursive: true });
152
- }
153
-
154
- // Separate themes by type
155
- const bundleThemes = []; // No path, no url — download from bundle
156
- const urlThemes = []; // Custom url
157
- const localThemes = []; // Has path — skip
158
-
159
- for (const [name, config] of Object.entries(themes)) {
160
- const cfg = config || {};
161
- if (cfg.path) {
162
- localThemes.push(name);
163
- } else if (cfg.url) {
164
- urlThemes.push({ name, url: cfg.url });
165
- } else {
166
- bundleThemes.push(name);
167
- }
168
- }
169
-
170
- let downloaded = 0;
171
-
172
- // Download named themes via bundle (single request)
173
- if (bundleThemes.length > 0) {
174
- process.stdout.write(`Fetching theme bundle from pureadmin.io (${bundleThemes.length} theme(s))...`);
175
-
176
- try {
177
- const data = await download(BUNDLE_URL);
178
- console.log(` ${(data.length / 1024).toFixed(0)}KB`);
179
-
180
- // Extract bundle to temp dir
181
- const tmpDir = path.join(THEMES_DIR, '_bundle_tmp');
182
- if (fs.existsSync(tmpDir)) {
183
- fs.rmSync(tmpDir, { recursive: true });
184
- }
185
- fs.mkdirSync(tmpDir, { recursive: true });
186
- extractZip(data, tmpDir);
187
-
188
- // Find and copy requested themes
189
- for (const name of bundleThemes) {
190
- const cssFile = findThemeCss(tmpDir, name);
191
-
192
- if (cssFile) {
193
- const themeDestDir = path.join(THEMES_DIR, name);
194
- const distDir = path.join(themeDestDir, 'dist');
195
- if (!fs.existsSync(distDir)) {
196
- fs.mkdirSync(distDir, { recursive: true });
197
- }
198
- fs.copyFileSync(cssFile, path.join(distDir, `${name}.css`));
199
-
200
- // Copy assets dir if it exists (fonts etc.)
201
- const assetsDir = path.join(path.dirname(cssFile), 'assets');
202
- if (fs.existsSync(assetsDir)) {
203
- copyDir(assetsDir, path.join(distDir, 'assets'));
204
- }
205
-
206
- // Copy theme.json manifest if it exists
207
- const themeJsonSrc = path.join(path.dirname(cssFile), '..', 'theme.json');
208
- if (fs.existsSync(themeJsonSrc)) {
209
- fs.copyFileSync(themeJsonSrc, path.join(themeDestDir, 'theme.json'));
210
- }
211
-
212
- // Read version from theme.json
213
- const version = getThemeVersion(themeDestDir);
214
- console.log(` ${name}: extracted${version ? ` (v${version})` : ''}`);
215
- downloaded++;
216
- } else {
217
- console.log(` ${name}: NOT FOUND in bundle`);
218
- }
219
- }
220
-
221
- // Clean up temp dir
222
- fs.rmSync(tmpDir, { recursive: true });
223
- } catch (err) {
224
- console.log(` FAILED: ${err.message}`);
225
- }
226
- }
227
-
228
- // Download URL themes individually
229
- for (const { name, url } of urlThemes) {
230
- process.stdout.write(` ${name}: downloading from ${url}...`);
231
-
232
- try {
233
- const data = await download(url);
234
- const themeDestDir = path.join(THEMES_DIR, name);
235
-
236
- if (data[0] === 0x50 && data[1] === 0x4B) {
237
- if (!fs.existsSync(themeDestDir)) {
238
- fs.mkdirSync(themeDestDir, { recursive: true });
239
- }
240
- extractZip(data, themeDestDir);
241
- } else {
242
- const distDir = path.join(themeDestDir, 'dist');
243
- if (!fs.existsSync(distDir)) {
244
- fs.mkdirSync(distDir, { recursive: true });
245
- }
246
- fs.writeFileSync(path.join(distDir, `${name}.css`), data);
247
- }
248
-
249
- console.log(' done');
250
- downloaded++;
251
- } catch (err) {
252
- console.log(` FAILED: ${err.message}`);
253
- }
254
- }
255
-
256
- // Report skipped
257
- for (const name of localThemes) {
258
- console.log(` ${name}: local path (${themes[name].path}), skipping`);
259
- }
260
-
261
- console.log(`\nDownloaded ${downloaded} theme(s), skipped ${localThemes.length} local path(s)`);
262
-
263
- // Check compatibility with installed core version
264
- checkCoreCompatibility();
265
- }
266
-
267
- /**
268
- * Check if downloaded themes are compatible with the installed core version
269
- */
270
- function checkCoreCompatibility() {
271
- // Find installed core version
272
- const corePkgPaths = [
273
- path.join(PROJECT_ROOT, 'node_modules', '@keenmate', 'pure-admin-core', 'package.json'),
274
- path.join(PROJECT_ROOT, '..', 'packages', 'core', 'package.json') // workspace
275
- ];
276
-
277
- let coreVersion = null;
278
- for (const p of corePkgPaths) {
279
- if (fs.existsSync(p)) {
280
- try {
281
- const pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
282
- coreVersion = pkg.version;
283
- break;
284
- } catch (e) {}
285
- }
286
- }
287
-
288
- if (!coreVersion) return;
289
-
290
- // Check each downloaded theme
291
- const warnings = [];
292
- const themeDirs = fs.readdirSync(THEMES_DIR, { withFileTypes: true })
293
- .filter(d => d.isDirectory())
294
- .map(d => d.name);
295
-
296
- for (const name of themeDirs) {
297
- const manifestPath = path.join(THEMES_DIR, name, 'theme.json');
298
- if (!fs.existsSync(manifestPath)) continue;
299
-
300
- try {
301
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
302
- const requiredCore = manifest.dependencies && manifest.dependencies.core;
303
- if (!requiredCore) continue;
304
-
305
- if (!satisfiesSemver(coreVersion, requiredCore)) {
306
- warnings.push(` ${name} (v${manifest.version}) requires core ${requiredCore}, installed: ${coreVersion}`);
307
- }
308
- } catch (e) {}
309
- }
310
-
311
- if (warnings.length > 0) {
312
- console.log('\n⚠ Compatibility warnings:');
313
- warnings.forEach(w => console.log(w));
314
- console.log('\nConsider updating @keenmate/pure-admin-core');
315
- } else if (themeDirs.length > 0) {
316
- console.log(`\nAll themes compatible with core v${coreVersion}`);
317
- }
318
- }
319
-
320
- /**
321
- * Simple semver satisfaction check for ^major.minor.patch ranges
322
- */
323
- function satisfiesSemver(version, range) {
324
- // Parse version
325
- const v = version.replace(/^v/, '').split('.').map(Number);
326
-
327
- // Parse range (supports ^x.y.z and x.y.z)
328
- const caret = range.startsWith('^');
329
- const r = range.replace(/^[\^~]/, '').split('.').map(Number);
330
-
331
- if (caret) {
332
- // ^2.0.0 means >=2.0.0 <3.0.0
333
- // ^0.2.0 means >=0.2.0 <0.3.0
334
- if (r[0] > 0) {
335
- return v[0] === r[0] && (v[1] > r[1] || (v[1] === r[1] && v[2] >= r[2]));
336
- } else if (r[1] > 0) {
337
- return v[0] === 0 && v[1] === r[1] && v[2] >= r[2];
338
- } else {
339
- return v[0] === 0 && v[1] === 0 && v[2] === r[2];
340
- }
341
- }
342
-
343
- // Exact match
344
- return v[0] === r[0] && v[1] === r[1] && v[2] === r[2];
345
- }
346
-
347
- console.log('Downloading themes...\n');
348
- main().catch(err => {
349
- console.error('Fatal error:', err.message);
350
- process.exit(1);
351
- });