@keenmate/pure-admin-core 2.0.2 → 2.1.1
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/README.md +65 -9
- package/dist/css/main.css +311 -85
- package/package.json +6 -4
- package/scripts/download-themes.js +351 -0
- package/scripts/pack-theme.js +2 -2
- package/snippets/badges.html +4 -4
- package/snippets/buttons.html +64 -0
- package/snippets/tooltips.html +36 -36
- package/src/scss/_core.scss +3 -0
- package/src/scss/_fonts.scss +36 -36
- package/src/scss/core-components/_buttons.scss +97 -0
- package/src/scss/core-components/_cards.scss +13 -3
- package/src/scss/core-components/_data-display.scss +35 -2
- package/src/scss/core-components/_filter-card.scss +58 -0
- package/src/scss/core-components/_tables.scss +4 -0
- package/src/scss/core-components/_timeline.scss +2 -2
- package/src/scss/core-components/_tooltips.scss +110 -54
- package/src/scss/core-components/_utilities.scss +362 -362
- package/src/scss/core-components/badges/_badge-base.scss +14 -3
- package/src/scss/core-components/layout/_sidebar.scss +2 -2
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8UH-qVHQ.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8UH-qVHQ.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8UH-qVHQ.woff2.2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8VH-qVHQ.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8VH-qVHQ.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8VH-qVHQ.woff2.2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8bH-o.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8bH-o.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8fH-qVHQ.woff2 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8fH-qVHQ.woff2.1 +0 -0
- package/dist/fonts/google/3y976aknfjLm_3lMKjiMgmUUYBs04Y8fH-qVHQ.woff2.2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtE2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtU2Hw.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtY2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvtg2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvto2H68T.woff2 +0 -0
- package/dist/fonts/google/6aez4K2oVqwIvts2H68T.woff2 +0 -0
- package/dist/fonts/google/7Auup_AqnyWWAxW2Wk3swUz56MS91Eww8SX21nejog.woff2 +0 -0
- package/dist/fonts/google/7Auup_AqnyWWAxW2Wk3swUz56MS91Eww8SX21nijogp5.woff2 +0 -0
- package/dist/fonts/google/7Auup_AqnyWWAxW2Wk3swUz56MS91Eww8SX21nmjogp5.woff2 +0 -0
- package/dist/fonts/google/PN_xRfK9oXHga0XdZ8g_vT0.woff2 +0 -0
- package/dist/fonts/google/PN_xRfK9oXHga0XdZsg_.woff2 +0 -0
- package/dist/fonts/google/PN_xRfK9oXHga0XdaMg_vT0.woff2 +0 -0
- package/dist/fonts/google/TK3tWkYFABsmjsphPho.woff2 +0 -0
- package/dist/fonts/google/TK3tWkYFABsmjspuPho7vA.woff2 +0 -0
- package/dist/fonts/google/TK3tWkYFABsmjspvPho7vA.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjTYJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjXYJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6Vj_YJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjbYJwQj.woff2 +0 -0
- package/dist/fonts/google/dg45_pLmvrkcOkBnKsOzXyGWTBcmg-X6VjvYJw.woff2 +0 -0
- package/dist/fonts/google/fonts-tracklist.txt +0 -48
- package/dist/fonts/google/vEFO2_JTCgwQ5ejvMV0O96D01E8J0tJXHKbBjM4.woff2 +0 -0
- package/dist/fonts/google/vEFO2_JTCgwQ5ejvMV0O96D01E8J0tJXHKbOjM7sfA.woff2 +0 -0
- package/dist/fonts/google/vEFO2_JTCgwQ5ejvMV0O96D01E8J0tJXHKbPjM7sfA.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfLtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfXtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfbtrQ.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfjtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfntrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfrtrftV.woff2 +0 -0
- package/dist/fonts/google/wEOhEADFm8hSaQTFG18FErVhsC9x-tarUfvtrftV.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_B-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_C-bk.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_G-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_M-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_N-bnBeA.woff2 +0 -0
- package/dist/fonts/google/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_P-bnBeA.woff2 +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/pure-admin-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Lightweight, data-focused HTML/CSS admin framework built with PureCSS foundation and comprehensive component system",
|
|
5
5
|
"style": "dist/css/main.css",
|
|
6
6
|
"exports": {
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"./css/*": "./dist/css/*",
|
|
10
10
|
"./scss": "./src/scss/main.scss",
|
|
11
11
|
"./scss/*": "./src/scss/*",
|
|
12
|
-
"./fonts/*": "./dist/fonts/*",
|
|
13
12
|
"./snippets/*": "./snippets/*"
|
|
14
13
|
},
|
|
15
14
|
"files": [
|
|
@@ -17,6 +16,7 @@
|
|
|
17
16
|
"src/scss/",
|
|
18
17
|
"schemas/",
|
|
19
18
|
"scripts/pack-theme.js",
|
|
19
|
+
"scripts/download-themes.js",
|
|
20
20
|
"snippets/",
|
|
21
21
|
"README.md",
|
|
22
22
|
"LICENSE"
|
|
@@ -24,8 +24,7 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build:scss": "sass src/scss/main.scss dist/css/main.css --no-source-map --silence-deprecation=import",
|
|
26
26
|
"build:scss:watch": "sass src/scss/main.scss dist/css/main.css --watch --silence-deprecation=import",
|
|
27
|
-
"build
|
|
28
|
-
"build": "npm run build:scss && npm run build:fonts",
|
|
27
|
+
"build": "npm run build:scss",
|
|
29
28
|
"watch": "npm run build:scss:watch",
|
|
30
29
|
"generate-hashes": "node scripts/generate-hashes.js",
|
|
31
30
|
"pack-theme": "node scripts/pack-theme.js",
|
|
@@ -41,6 +40,9 @@
|
|
|
41
40
|
"components",
|
|
42
41
|
"ui-kit"
|
|
43
42
|
],
|
|
43
|
+
"bin": {
|
|
44
|
+
"download-themes": "./scripts/download-themes.js"
|
|
45
|
+
},
|
|
44
46
|
"author": "KeenMate",
|
|
45
47
|
"license": "MIT",
|
|
46
48
|
"peerDependencies": {
|
|
@@ -0,0 +1,351 @@
|
|
|
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
|
+
});
|
package/scripts/pack-theme.js
CHANGED
|
@@ -311,8 +311,8 @@ ${theme.modes && theme.modes.supported && theme.modes.supported.length > 1
|
|
|
311
311
|
|
|
312
312
|
## More Information
|
|
313
313
|
|
|
314
|
-
- Pure Admin documentation: https://
|
|
315
|
-
- Theme gallery: https://
|
|
314
|
+
- Pure Admin documentation: https://demo.pureadmin.io
|
|
315
|
+
- Theme gallery: https://pureadmin.io
|
|
316
316
|
${theme.homepage ? `- Theme homepage: ${theme.homepage}` : ''}
|
|
317
317
|
|
|
318
318
|
---
|
package/snippets/badges.html
CHANGED
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
<span class="pa-badge pa-badge--info minwr-15 maxwr-15 text-truncate">Very Long Badge Text That Needs Truncation</span>
|
|
84
84
|
</span>
|
|
85
85
|
|
|
86
|
-
<!-- Fixed Width with
|
|
86
|
+
<!-- Fixed Width with Start-Side Ellipsis (for paths, breadcrumbs) -->
|
|
87
87
|
<span class="pa-tooltip pa-tooltip--bottom pa-tooltip--multiline" data-tooltip="Settings > User Preferences > Notifications > Email">
|
|
88
|
-
<span class="pa-badge pa-badge--secondary minwr-10 maxwr-10 text-truncate pa-badge--ellipsis-
|
|
88
|
+
<span class="pa-badge pa-badge--secondary minwr-10 maxwr-10 text-truncate pa-badge--ellipsis-start">Settings > User Preferences > Notifications > Email</span>
|
|
89
89
|
</span>
|
|
90
90
|
|
|
91
91
|
|
|
@@ -186,8 +186,8 @@ SIZES:
|
|
|
186
186
|
|
|
187
187
|
MODIFIERS:
|
|
188
188
|
- .pa-badge--pill: Rounded pill shape
|
|
189
|
-
- .pa-badge--ellipsis-
|
|
190
|
-
- Use utilities for fixed width:
|
|
189
|
+
- .pa-badge--ellipsis-start: Truncate from start instead of end
|
|
190
|
+
- Use utilities for fixed width: maxwr-* text-truncate
|
|
191
191
|
|
|
192
192
|
ELEMENTS:
|
|
193
193
|
- .pa-badge__icon: Icon container inside badge
|
package/snippets/buttons.html
CHANGED
|
@@ -190,6 +190,70 @@
|
|
|
190
190
|
</div>
|
|
191
191
|
|
|
192
192
|
|
|
193
|
+
<!-- SPLIT BUTTONS -->
|
|
194
|
+
|
|
195
|
+
<!-- Basic Split Button (chevron rotates on open) -->
|
|
196
|
+
<div class="pa-btn-split">
|
|
197
|
+
<button class="pa-btn pa-btn--primary">Save</button>
|
|
198
|
+
<button class="pa-btn pa-btn--primary pa-btn-split__toggle" onclick="toggleSplitMenu(event)">
|
|
199
|
+
<i class="fas fa-chevron-down text-2xs pa-btn-split__chevron"></i>
|
|
200
|
+
</button>
|
|
201
|
+
<div class="pa-btn-split__menu">
|
|
202
|
+
<button class="pa-btn-split__item">Save as Draft</button>
|
|
203
|
+
<button class="pa-btn-split__item">Save & Close</button>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<!-- Split Button with Danger Item -->
|
|
208
|
+
<div class="pa-btn-split">
|
|
209
|
+
<button class="pa-btn pa-btn--primary">Export</button>
|
|
210
|
+
<button class="pa-btn pa-btn--primary pa-btn-split__toggle" onclick="toggleSplitMenu(event)">
|
|
211
|
+
<i class="fas fa-chevron-down text-2xs pa-btn-split__chevron"></i>
|
|
212
|
+
</button>
|
|
213
|
+
<div class="pa-btn-split__menu">
|
|
214
|
+
<button class="pa-btn-split__item">Export as CSV</button>
|
|
215
|
+
<button class="pa-btn-split__item">Export as PDF</button>
|
|
216
|
+
<button class="pa-btn-split__item pa-btn-split__item--danger">Delete All</button>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<!-- Split Button - Upward Placement -->
|
|
221
|
+
<div class="pa-btn-split" data-placement="top-end">
|
|
222
|
+
<button class="pa-btn pa-btn--primary">Upload</button>
|
|
223
|
+
<button class="pa-btn pa-btn--primary pa-btn-split__toggle" onclick="toggleSplitMenu(event)">
|
|
224
|
+
<i class="fas fa-chevron-up text-2xs pa-btn-split__chevron"></i>
|
|
225
|
+
</button>
|
|
226
|
+
<div class="pa-btn-split__menu">
|
|
227
|
+
<button class="pa-btn-split__item">Upload File</button>
|
|
228
|
+
<button class="pa-btn-split__item">Upload Folder</button>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<!-- Split Button - Custom Icon (no rotation) -->
|
|
233
|
+
<div class="pa-btn-split">
|
|
234
|
+
<button class="pa-btn pa-btn--secondary">Share</button>
|
|
235
|
+
<button class="pa-btn pa-btn--secondary pa-btn-split__toggle" onclick="toggleSplitMenu(event)">
|
|
236
|
+
<i class="fas fa-ellipsis-vertical"></i>
|
|
237
|
+
</button>
|
|
238
|
+
<div class="pa-btn-split__menu">
|
|
239
|
+
<button class="pa-btn-split__item">Share via Email</button>
|
|
240
|
+
<button class="pa-btn-split__item">Share via Link</button>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<!-- Split Button - Caret Icon (rotates) -->
|
|
245
|
+
<div class="pa-btn-split">
|
|
246
|
+
<button class="pa-btn pa-btn--danger">Delete</button>
|
|
247
|
+
<button class="pa-btn pa-btn--danger pa-btn-split__toggle" onclick="toggleSplitMenu(event)">
|
|
248
|
+
<i class="fas fa-caret-down pa-btn-split__chevron"></i>
|
|
249
|
+
</button>
|
|
250
|
+
<div class="pa-btn-split__menu">
|
|
251
|
+
<button class="pa-btn-split__item pa-btn-split__item--danger">Delete Permanently</button>
|
|
252
|
+
<button class="pa-btn-split__item">Move to Trash</button>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
|
|
193
257
|
<!-- BUTTONS WITH ICONS -->
|
|
194
258
|
|
|
195
259
|
<!-- Button with Icon (Text Icon) -->
|
package/snippets/tooltips.html
CHANGED
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
<!-- Tooltip Top (default) -->
|
|
20
20
|
<span class="pa-tooltip" data-tooltip="This is a tooltip">Hover me</span>
|
|
21
21
|
|
|
22
|
-
<!-- Tooltip
|
|
23
|
-
<span class="pa-tooltip pa-tooltip--
|
|
22
|
+
<!-- Tooltip End -->
|
|
23
|
+
<span class="pa-tooltip pa-tooltip--end" data-tooltip="Tooltip on the end">Hover me</span>
|
|
24
24
|
|
|
25
25
|
<!-- Tooltip Bottom -->
|
|
26
26
|
<span class="pa-tooltip pa-tooltip--bottom" data-tooltip="Tooltip on the bottom">Hover me</span>
|
|
27
27
|
|
|
28
|
-
<!-- Tooltip
|
|
29
|
-
<span class="pa-tooltip pa-tooltip--
|
|
28
|
+
<!-- Tooltip Start -->
|
|
29
|
+
<span class="pa-tooltip pa-tooltip--start" data-tooltip="Tooltip on the start">Hover me</span>
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
<!-- TOOLTIP POSITIONS (All 4 directions) -->
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
Top
|
|
37
37
|
</button>
|
|
38
38
|
|
|
39
|
-
<!--
|
|
40
|
-
<button class="pa-btn pa-btn--primary pa-tooltip pa-tooltip--
|
|
41
|
-
|
|
39
|
+
<!-- End Tooltip -->
|
|
40
|
+
<button class="pa-btn pa-btn--primary pa-tooltip pa-tooltip--end" data-tooltip="End tooltip">
|
|
41
|
+
End
|
|
42
42
|
</button>
|
|
43
43
|
|
|
44
44
|
<!-- Bottom Tooltip -->
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
Bottom
|
|
47
47
|
</button>
|
|
48
48
|
|
|
49
|
-
<!--
|
|
50
|
-
<button class="pa-btn pa-btn--primary pa-tooltip pa-tooltip--
|
|
51
|
-
|
|
49
|
+
<!-- Start Tooltip -->
|
|
50
|
+
<button class="pa-btn pa-btn--primary pa-tooltip pa-tooltip--start" data-tooltip="Start tooltip">
|
|
51
|
+
Start
|
|
52
52
|
</button>
|
|
53
53
|
|
|
54
54
|
|
|
@@ -109,14 +109,14 @@
|
|
|
109
109
|
Primary Bottom
|
|
110
110
|
</button>
|
|
111
111
|
|
|
112
|
-
<!-- Success
|
|
113
|
-
<button class="pa-btn pa-btn--success pa-tooltip pa-tooltip--success pa-tooltip--
|
|
114
|
-
Success
|
|
112
|
+
<!-- Success End Tooltip -->
|
|
113
|
+
<button class="pa-btn pa-btn--success pa-tooltip pa-tooltip--success pa-tooltip--end" data-tooltip="Operation successful!">
|
|
114
|
+
Success End
|
|
115
115
|
</button>
|
|
116
116
|
|
|
117
|
-
<!-- Danger
|
|
118
|
-
<button class="pa-btn pa-btn--danger pa-tooltip pa-tooltip--danger pa-tooltip--
|
|
119
|
-
Danger
|
|
117
|
+
<!-- Danger Start Tooltip -->
|
|
118
|
+
<button class="pa-btn pa-btn--danger pa-tooltip pa-tooltip--danger pa-tooltip--start" data-tooltip="Critical warning!">
|
|
119
|
+
Danger Start
|
|
120
120
|
</button>
|
|
121
121
|
|
|
122
122
|
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
|
|
177
177
|
<!-- Popovers are click-triggered rich content overlays
|
|
178
178
|
- Use Floating UI for positioning (like tooltips)
|
|
179
|
-
- Support all 4 positions (top,
|
|
179
|
+
- Support all 4 positions (top, end, bottom, start) via data-placement attribute
|
|
180
180
|
- Auto collision detection and flipping
|
|
181
181
|
- Close with X button or clicking outside
|
|
182
182
|
-->
|
|
@@ -195,8 +195,8 @@
|
|
|
195
195
|
</div>
|
|
196
196
|
</div>
|
|
197
197
|
|
|
198
|
-
<!-- Popover
|
|
199
|
-
<div class="pa-popover" data-placement="
|
|
198
|
+
<!-- Popover End -->
|
|
199
|
+
<div class="pa-popover" data-placement="end">
|
|
200
200
|
<button class="pa-popover__trigger">i</button>
|
|
201
201
|
<div class="pa-popover__content">
|
|
202
202
|
<div class="pa-popover__header">
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
<button class="pa-popover__close">×</button>
|
|
205
205
|
</div>
|
|
206
206
|
<div class="pa-popover__body">
|
|
207
|
-
<p>Popover positioned
|
|
207
|
+
<p>Popover positioned at the inline-end.</p>
|
|
208
208
|
</div>
|
|
209
209
|
</div>
|
|
210
210
|
</div>
|
|
@@ -223,8 +223,8 @@
|
|
|
223
223
|
</div>
|
|
224
224
|
</div>
|
|
225
225
|
|
|
226
|
-
<!-- Popover
|
|
227
|
-
<div class="pa-popover" data-placement="
|
|
226
|
+
<!-- Popover Start -->
|
|
227
|
+
<div class="pa-popover" data-placement="start">
|
|
228
228
|
<button class="pa-popover__trigger">!</button>
|
|
229
229
|
<div class="pa-popover__content">
|
|
230
230
|
<div class="pa-popover__header">
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
<button class="pa-popover__close">×</button>
|
|
233
233
|
</div>
|
|
234
234
|
<div class="pa-popover__body">
|
|
235
|
-
<p>Popover positioned
|
|
235
|
+
<p>Popover positioned at the inline-start.</p>
|
|
236
236
|
</div>
|
|
237
237
|
</div>
|
|
238
238
|
</div>
|
|
@@ -286,16 +286,16 @@
|
|
|
286
286
|
|
|
287
287
|
<!-- POPOVER ALIGNMENT -->
|
|
288
288
|
|
|
289
|
-
<!--
|
|
289
|
+
<!-- Start Aligned (default) -->
|
|
290
290
|
<div class="pa-popover" data-placement="bottom">
|
|
291
291
|
<button class="pa-popover__trigger">?</button>
|
|
292
292
|
<div class="pa-popover__content">
|
|
293
293
|
<div class="pa-popover__header">
|
|
294
|
-
<h4>
|
|
294
|
+
<h4>Start Aligned</h4>
|
|
295
295
|
<button class="pa-popover__close">×</button>
|
|
296
296
|
</div>
|
|
297
297
|
<div class="pa-popover__body">
|
|
298
|
-
<p>Default alignment is
|
|
298
|
+
<p>Default alignment is start.</p>
|
|
299
299
|
<ul>
|
|
300
300
|
<li>Lists look natural</li>
|
|
301
301
|
<li>Easy to read</li>
|
|
@@ -318,16 +318,16 @@
|
|
|
318
318
|
</div>
|
|
319
319
|
</div>
|
|
320
320
|
|
|
321
|
-
<!--
|
|
321
|
+
<!-- End Aligned -->
|
|
322
322
|
<div class="pa-popover pa-popover--end" data-placement="bottom">
|
|
323
323
|
<button class="pa-popover__trigger">?</button>
|
|
324
324
|
<div class="pa-popover__content">
|
|
325
325
|
<div class="pa-popover__header">
|
|
326
|
-
<h4>
|
|
326
|
+
<h4>End Aligned</h4>
|
|
327
327
|
<button class="pa-popover__close">×</button>
|
|
328
328
|
</div>
|
|
329
329
|
<div class="pa-popover__body">
|
|
330
|
-
<p>Use --
|
|
330
|
+
<p>Use --end modifier for end-aligned text.</p>
|
|
331
331
|
</div>
|
|
332
332
|
</div>
|
|
333
333
|
</div>
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
</div>
|
|
358
358
|
|
|
359
359
|
<!-- Popover with Code -->
|
|
360
|
-
<div class="pa-popover pa-popover--lg" data-placement="
|
|
360
|
+
<div class="pa-popover pa-popover--lg" data-placement="end">
|
|
361
361
|
<button class="pa-popover__trigger">?</button>
|
|
362
362
|
<div class="pa-popover__content">
|
|
363
363
|
<div class="pa-popover__header">
|
|
@@ -399,10 +399,10 @@ document.addEventListener('content-loaded', function() {
|
|
|
399
399
|
TOOLTIPS:
|
|
400
400
|
- pa-tooltip (base class, top position by default)
|
|
401
401
|
|
|
402
|
-
POSITIONS:
|
|
403
|
-
- pa-tooltip--right
|
|
402
|
+
POSITIONS (RTL-aware):
|
|
403
|
+
- pa-tooltip--end (inline-end: right in LTR, left in RTL)
|
|
404
404
|
- pa-tooltip--bottom
|
|
405
|
-
- pa-tooltip--left
|
|
405
|
+
- pa-tooltip--start (inline-start: left in LTR, right in RTL)
|
|
406
406
|
|
|
407
407
|
VARIANTS:
|
|
408
408
|
- pa-tooltip--primary (primary colored background)
|
|
@@ -417,11 +417,11 @@ MULTILINE:
|
|
|
417
417
|
POPOVERS:
|
|
418
418
|
- pa-popover (base container)
|
|
419
419
|
|
|
420
|
-
POSITIONING (use data-placement attribute):
|
|
420
|
+
POSITIONING (use data-placement attribute, RTL-aware):
|
|
421
421
|
- data-placement="top" (default)
|
|
422
|
-
- data-placement="right
|
|
422
|
+
- data-placement="end" (inline-end: right in LTR, left in RTL)
|
|
423
423
|
- data-placement="bottom"
|
|
424
|
-
- data-placement="left
|
|
424
|
+
- data-placement="start" (inline-start: left in LTR, right in RTL)
|
|
425
425
|
|
|
426
426
|
SIZES:
|
|
427
427
|
- pa-popover--sm (small)
|