@keenmate/pure-admin-core 2.9.0-rc03 → 2.9.0-rc05
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 +55 -13
- package/dist/css/main.css +554 -7
- package/package.json +3 -1
- package/snippets/buttons.html +51 -0
- package/snippets/cards.html +132 -47
- package/snippets/comparison.html +26 -22
- package/snippets/manifest.json +180 -115
- package/snippets/range-group.html +125 -0
- package/snippets/splitter.html +44 -38
- package/snippets/statistics.html +31 -0
- package/src/js/btn-split-auto-absorb.js +327 -0
- package/src/js/command-palette.js +472 -0
- package/src/js/file-selector.js +1275 -0
- package/src/js/internal/logging.js +121 -0
- package/src/js/logic-tree-renderer.js +303 -0
- package/src/js/modal-dialogs.js +460 -0
- package/src/js/overflow.js +371 -0
- package/src/js/pa-stat-fit.js +184 -0
- package/src/js/range-group.js +663 -0
- package/src/js/search-autocomplete-v2.js +907 -0
- package/src/js/search-autocomplete.js +434 -0
- package/src/js/settings-panel.js +245 -0
- package/src/js/split-button.js +141 -0
- package/src/js/splitter.js +1323 -0
- package/src/js/toast-service.js +302 -0
- package/src/js/tooltips-popovers.js +275 -0
- package/src/js/virtual-scroll.js +143 -0
- package/src/js/virtual-textbox.js +803 -0
- package/src/scss/_core.scss +7 -0
- package/src/scss/core-components/_buttons.scss +44 -0
- package/src/scss/core-components/_cards.scss +95 -6
- package/src/scss/core-components/_overflow.scss +50 -0
- package/src/scss/core-components/_range-group.scss +474 -0
- package/src/scss/core-components/_statistics.scss +163 -0
- package/src/scss/variables/_components.scss +41 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic category-based logging utility
|
|
3
|
+
*
|
|
4
|
+
* Provides a flexible logging system with:
|
|
5
|
+
* - Multiple log levels (debug, info, warn, error)
|
|
6
|
+
* - Category-based filtering
|
|
7
|
+
* - Custom prefixes and colors
|
|
8
|
+
*
|
|
9
|
+
* This is an internal utility not exposed to end users.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Track which categories are enabled
|
|
13
|
+
const enabledCategories = new Set()
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Enable logging for a specific category
|
|
17
|
+
* @param {string} category - Category name to enable
|
|
18
|
+
*/
|
|
19
|
+
export function enableLoggingCategory(category) {
|
|
20
|
+
enabledCategories.add(category)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Disable logging for a specific category
|
|
25
|
+
* @param {string} category - Category name to disable
|
|
26
|
+
*/
|
|
27
|
+
export function disableLoggingCategory(category) {
|
|
28
|
+
enabledCategories.delete(category)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a category is enabled
|
|
33
|
+
* @param {string} category - Category name to check
|
|
34
|
+
* @returns {boolean} true if category is enabled
|
|
35
|
+
*/
|
|
36
|
+
export function isLoggingCategoryEnabled(category) {
|
|
37
|
+
return enabledCategories.has(category)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Internal log function that handles all logging
|
|
42
|
+
* @param {string} level - Log level (debug, info, warn, error)
|
|
43
|
+
* @param {string} category - Category name
|
|
44
|
+
* @param {string} prefix - Display prefix (e.g., '[Router]')
|
|
45
|
+
* @param {string} color - CSS color for prefix
|
|
46
|
+
* @param {string} message - Log message
|
|
47
|
+
* @param {...any} args - Additional arguments to log
|
|
48
|
+
*/
|
|
49
|
+
function log(level, category, prefix, color, message, ...args) {
|
|
50
|
+
// Check if category is enabled
|
|
51
|
+
if (!enabledCategories.has(category)) {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Select appropriate console method
|
|
56
|
+
const method = level === 'warn' ? console.warn :
|
|
57
|
+
level === 'error' ? console.error :
|
|
58
|
+
level === 'info' ? console.info :
|
|
59
|
+
console.log
|
|
60
|
+
|
|
61
|
+
// Log with CSS styling
|
|
62
|
+
method(
|
|
63
|
+
`%c${prefix}%c ${message}`,
|
|
64
|
+
`color: ${color}; font-weight: bold;`,
|
|
65
|
+
'color: inherit;',
|
|
66
|
+
...args
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a logger instance for a specific category
|
|
72
|
+
*
|
|
73
|
+
* @param {string} category - Category identifier (e.g., 'router', 'router-utils')
|
|
74
|
+
* @param {string} prefix - Display prefix (e.g., '[Router]')
|
|
75
|
+
* @param {string} color - CSS color for the prefix (e.g., '#ff3e00')
|
|
76
|
+
* @returns {Object} Logger instance with debug, info, warn, error methods
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* const logger = createLogger('router', '[Router]', '#ff3e00')
|
|
80
|
+
* logger.debug('Route matched:', route)
|
|
81
|
+
* logger.warn('No match found for:', path)
|
|
82
|
+
*/
|
|
83
|
+
export function createLogger(category, prefix, color) {
|
|
84
|
+
return {
|
|
85
|
+
/**
|
|
86
|
+
* Log debug-level message
|
|
87
|
+
* @param {string} message - Message to log
|
|
88
|
+
* @param {...any} args - Additional arguments
|
|
89
|
+
*/
|
|
90
|
+
debug: (message, ...args) => {
|
|
91
|
+
log('debug', category, prefix, color, message, ...args)
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Log info-level message
|
|
96
|
+
* @param {string} message - Message to log
|
|
97
|
+
* @param {...any} args - Additional arguments
|
|
98
|
+
*/
|
|
99
|
+
info: (message, ...args) => {
|
|
100
|
+
log('info', category, prefix, color, message, ...args)
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Log warning-level message
|
|
105
|
+
* @param {string} message - Message to log
|
|
106
|
+
* @param {...any} args - Additional arguments
|
|
107
|
+
*/
|
|
108
|
+
warn: (message, ...args) => {
|
|
109
|
+
log('warn', category, prefix, color, message, ...args)
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Log error-level message
|
|
114
|
+
* @param {string} message - Message to log
|
|
115
|
+
* @param {...any} args - Additional arguments
|
|
116
|
+
*/
|
|
117
|
+
error: (message, ...args) => {
|
|
118
|
+
log('error', category, prefix, color, message, ...args)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logic Tree Renderer - Scratch-style visual query builder
|
|
3
|
+
* Renders a parsed query as a nested block structure showing logical flow
|
|
4
|
+
* Similar to Scratch programming interface but readonly
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class LogicTreeRenderer {
|
|
8
|
+
constructor(container, options = {}) {
|
|
9
|
+
this.container = container;
|
|
10
|
+
this.options = {
|
|
11
|
+
showIndices: false, // Show token indices for debugging
|
|
12
|
+
animated: true, // Animate tree building
|
|
13
|
+
...options
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse tokens into a tree structure with logical precedence
|
|
19
|
+
* Handles AND (higher precedence) and OR (lower precedence)
|
|
20
|
+
* Handles parentheses for grouping
|
|
21
|
+
*/
|
|
22
|
+
parseTokens(tokens) {
|
|
23
|
+
if (!tokens || tokens.length === 0) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Convert tokens to easier format
|
|
28
|
+
const tokenList = Array.from(tokens).map((token, index) => ({
|
|
29
|
+
type: token.getAttribute('data-token-type'),
|
|
30
|
+
value: token.textContent || token.getAttribute('data-token-value'),
|
|
31
|
+
index
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
return this.parseExpression(tokenList, 0).node;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parse expression with operator precedence
|
|
39
|
+
* Returns { node, endIndex }
|
|
40
|
+
*/
|
|
41
|
+
parseExpression(tokens, startIndex) {
|
|
42
|
+
// Parse OR expressions (lowest precedence)
|
|
43
|
+
return this.parseOrExpression(tokens, startIndex);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parse OR expressions (a OR b OR c)
|
|
48
|
+
*/
|
|
49
|
+
parseOrExpression(tokens, startIndex) {
|
|
50
|
+
let result = this.parseAndExpression(tokens, startIndex);
|
|
51
|
+
let currentIndex = result.endIndex;
|
|
52
|
+
|
|
53
|
+
while (currentIndex < tokens.length) {
|
|
54
|
+
const token = tokens[currentIndex];
|
|
55
|
+
|
|
56
|
+
if (token.type === 'logical' && token.value === 'OR') {
|
|
57
|
+
// Found OR operator
|
|
58
|
+
currentIndex++; // Skip OR token
|
|
59
|
+
const rightResult = this.parseAndExpression(tokens, currentIndex);
|
|
60
|
+
|
|
61
|
+
result = {
|
|
62
|
+
node: {
|
|
63
|
+
type: 'logical',
|
|
64
|
+
operator: 'OR',
|
|
65
|
+
left: result.node,
|
|
66
|
+
right: rightResult.node
|
|
67
|
+
},
|
|
68
|
+
endIndex: rightResult.endIndex
|
|
69
|
+
};
|
|
70
|
+
currentIndex = rightResult.endIndex;
|
|
71
|
+
} else {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Parse AND expressions (a AND b AND c)
|
|
81
|
+
*/
|
|
82
|
+
parseAndExpression(tokens, startIndex) {
|
|
83
|
+
let result = this.parsePrimaryExpression(tokens, startIndex);
|
|
84
|
+
let currentIndex = result.endIndex;
|
|
85
|
+
|
|
86
|
+
while (currentIndex < tokens.length) {
|
|
87
|
+
const token = tokens[currentIndex];
|
|
88
|
+
|
|
89
|
+
if (token.type === 'logical' && token.value === 'AND') {
|
|
90
|
+
// Found AND operator
|
|
91
|
+
currentIndex++; // Skip AND token
|
|
92
|
+
const rightResult = this.parsePrimaryExpression(tokens, currentIndex);
|
|
93
|
+
|
|
94
|
+
result = {
|
|
95
|
+
node: {
|
|
96
|
+
type: 'logical',
|
|
97
|
+
operator: 'AND',
|
|
98
|
+
left: result.node,
|
|
99
|
+
right: rightResult.node
|
|
100
|
+
},
|
|
101
|
+
endIndex: rightResult.endIndex
|
|
102
|
+
};
|
|
103
|
+
currentIndex = rightResult.endIndex;
|
|
104
|
+
} else {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parse primary expression (condition or parenthesized group)
|
|
114
|
+
*/
|
|
115
|
+
parsePrimaryExpression(tokens, startIndex) {
|
|
116
|
+
if (startIndex >= tokens.length) {
|
|
117
|
+
return { node: null, endIndex: startIndex };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const token = tokens[startIndex];
|
|
121
|
+
|
|
122
|
+
// Handle opening parenthesis
|
|
123
|
+
if (token.type === 'paren' && token.value === '(') {
|
|
124
|
+
// Find matching closing paren
|
|
125
|
+
let depth = 1;
|
|
126
|
+
let endIndex = startIndex + 1;
|
|
127
|
+
|
|
128
|
+
while (endIndex < tokens.length && depth > 0) {
|
|
129
|
+
const t = tokens[endIndex];
|
|
130
|
+
if (t.type === 'paren') {
|
|
131
|
+
if (t.value === '(') depth++;
|
|
132
|
+
if (t.value === ')') depth--;
|
|
133
|
+
}
|
|
134
|
+
endIndex++;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Parse expression inside parentheses
|
|
138
|
+
const innerResult = this.parseExpression(tokens, startIndex + 1);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
node: {
|
|
142
|
+
type: 'group',
|
|
143
|
+
expression: innerResult.node
|
|
144
|
+
},
|
|
145
|
+
endIndex: endIndex
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Handle condition (field operator value)
|
|
150
|
+
if (token.type === 'field') {
|
|
151
|
+
let currentIndex = startIndex + 1;
|
|
152
|
+
const field = token.value;
|
|
153
|
+
|
|
154
|
+
// Expect operator
|
|
155
|
+
if (currentIndex >= tokens.length || tokens[currentIndex].type !== 'operator') {
|
|
156
|
+
return { node: null, endIndex: currentIndex };
|
|
157
|
+
}
|
|
158
|
+
const operator = tokens[currentIndex].value;
|
|
159
|
+
currentIndex++;
|
|
160
|
+
|
|
161
|
+
// Expect value
|
|
162
|
+
if (currentIndex >= tokens.length || tokens[currentIndex].type !== 'value') {
|
|
163
|
+
return { node: null, endIndex: currentIndex };
|
|
164
|
+
}
|
|
165
|
+
const value = tokens[currentIndex].value;
|
|
166
|
+
currentIndex++;
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
node: {
|
|
170
|
+
type: 'condition',
|
|
171
|
+
field,
|
|
172
|
+
operator,
|
|
173
|
+
value
|
|
174
|
+
},
|
|
175
|
+
endIndex: currentIndex
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { node: null, endIndex: startIndex + 1 };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Render the parsed tree as HTML
|
|
184
|
+
*/
|
|
185
|
+
render(tokens) {
|
|
186
|
+
this.container.innerHTML = '';
|
|
187
|
+
|
|
188
|
+
if (!tokens || tokens.length === 0) {
|
|
189
|
+
this.container.innerHTML = '<div class="pa-logic-tree__empty">No conditions defined</div>';
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const tree = this.parseTokens(tokens);
|
|
194
|
+
|
|
195
|
+
if (!tree) {
|
|
196
|
+
this.container.innerHTML = '<div class="pa-logic-tree__empty">Invalid query structure</div>';
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const treeElement = this.renderNode(tree, 0);
|
|
201
|
+
this.container.appendChild(treeElement);
|
|
202
|
+
|
|
203
|
+
if (this.options.animated) {
|
|
204
|
+
this.container.classList.add('pa-logic-tree--animated');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Render a single node in the tree
|
|
210
|
+
*/
|
|
211
|
+
renderNode(node, depth) {
|
|
212
|
+
if (!node) {
|
|
213
|
+
// Return empty div instead of text node for proper styling
|
|
214
|
+
const emptyDiv = document.createElement('div');
|
|
215
|
+
emptyDiv.className = 'pa-logic-tree__empty-branch';
|
|
216
|
+
emptyDiv.textContent = '...';
|
|
217
|
+
return emptyDiv;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const wrapper = document.createElement('div');
|
|
221
|
+
wrapper.className = 'pa-logic-tree__node';
|
|
222
|
+
wrapper.setAttribute('data-depth', depth);
|
|
223
|
+
|
|
224
|
+
if (node.type === 'condition') {
|
|
225
|
+
// Render condition block (field operator value)
|
|
226
|
+
const block = document.createElement('div');
|
|
227
|
+
block.className = 'pa-logic-tree__block pa-logic-tree__block--condition';
|
|
228
|
+
|
|
229
|
+
block.innerHTML = `
|
|
230
|
+
<div class="pa-logic-tree__block-content">
|
|
231
|
+
<span class="pa-logic-tree__token pa-logic-tree__token--field">${this.escapeHtml(node.field)}</span>
|
|
232
|
+
<span class="pa-logic-tree__token pa-logic-tree__token--operator">${this.escapeHtml(node.operator)}</span>
|
|
233
|
+
<span class="pa-logic-tree__token pa-logic-tree__token--value">${this.escapeHtml(node.value)}</span>
|
|
234
|
+
</div>
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
wrapper.appendChild(block);
|
|
238
|
+
} else if (node.type === 'logical') {
|
|
239
|
+
// Render logical operator with tree structure
|
|
240
|
+
const block = document.createElement('div');
|
|
241
|
+
block.className = `pa-logic-tree__block pa-logic-tree__block--logical pa-logic-tree__block--${node.operator.toLowerCase()}`;
|
|
242
|
+
|
|
243
|
+
// Create tree structure container
|
|
244
|
+
const treeContainer = document.createElement('div');
|
|
245
|
+
treeContainer.className = 'pa-logic-tree__tree-structure';
|
|
246
|
+
|
|
247
|
+
// Left branch with connector
|
|
248
|
+
const leftBranch = document.createElement('div');
|
|
249
|
+
leftBranch.className = 'pa-logic-tree__tree-branch pa-logic-tree__tree-branch--left';
|
|
250
|
+
leftBranch.appendChild(this.renderNode(node.left, depth + 1));
|
|
251
|
+
treeContainer.appendChild(leftBranch);
|
|
252
|
+
|
|
253
|
+
// Operator node in the middle
|
|
254
|
+
const operatorNode = document.createElement('div');
|
|
255
|
+
operatorNode.className = 'pa-logic-tree__tree-operator';
|
|
256
|
+
operatorNode.innerHTML = `<span class="pa-logic-tree__token pa-logic-tree__token--logical">${node.operator}</span>`;
|
|
257
|
+
treeContainer.appendChild(operatorNode);
|
|
258
|
+
|
|
259
|
+
// Right branch with connector
|
|
260
|
+
const rightBranch = document.createElement('div');
|
|
261
|
+
rightBranch.className = 'pa-logic-tree__tree-branch pa-logic-tree__tree-branch--right';
|
|
262
|
+
rightBranch.appendChild(this.renderNode(node.right, depth + 1));
|
|
263
|
+
treeContainer.appendChild(rightBranch);
|
|
264
|
+
|
|
265
|
+
block.appendChild(treeContainer);
|
|
266
|
+
wrapper.appendChild(block);
|
|
267
|
+
} else if (node.type === 'group') {
|
|
268
|
+
// Render parenthesized group
|
|
269
|
+
const block = document.createElement('div');
|
|
270
|
+
block.className = 'pa-logic-tree__block pa-logic-tree__block--group';
|
|
271
|
+
|
|
272
|
+
const groupLabel = document.createElement('div');
|
|
273
|
+
groupLabel.className = 'pa-logic-tree__group-label';
|
|
274
|
+
groupLabel.innerHTML = '<span class="pa-logic-tree__token pa-logic-tree__token--paren">Grouped Condition</span>';
|
|
275
|
+
block.appendChild(groupLabel);
|
|
276
|
+
|
|
277
|
+
const groupContent = document.createElement('div');
|
|
278
|
+
groupContent.className = 'pa-logic-tree__group-content';
|
|
279
|
+
groupContent.appendChild(this.renderNode(node.expression, depth + 1));
|
|
280
|
+
block.appendChild(groupContent);
|
|
281
|
+
|
|
282
|
+
wrapper.appendChild(block);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return wrapper;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Escape HTML for safe rendering
|
|
290
|
+
*/
|
|
291
|
+
escapeHtml(text) {
|
|
292
|
+
const div = document.createElement('div');
|
|
293
|
+
div.textContent = text;
|
|
294
|
+
return div.innerHTML;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Clear the tree visualization
|
|
299
|
+
*/
|
|
300
|
+
clear() {
|
|
301
|
+
this.container.innerHTML = '';
|
|
302
|
+
}
|
|
303
|
+
}
|