@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.
Files changed (35) hide show
  1. package/README.md +55 -13
  2. package/dist/css/main.css +554 -7
  3. package/package.json +3 -1
  4. package/snippets/buttons.html +51 -0
  5. package/snippets/cards.html +132 -47
  6. package/snippets/comparison.html +26 -22
  7. package/snippets/manifest.json +180 -115
  8. package/snippets/range-group.html +125 -0
  9. package/snippets/splitter.html +44 -38
  10. package/snippets/statistics.html +31 -0
  11. package/src/js/btn-split-auto-absorb.js +327 -0
  12. package/src/js/command-palette.js +472 -0
  13. package/src/js/file-selector.js +1275 -0
  14. package/src/js/internal/logging.js +121 -0
  15. package/src/js/logic-tree-renderer.js +303 -0
  16. package/src/js/modal-dialogs.js +460 -0
  17. package/src/js/overflow.js +371 -0
  18. package/src/js/pa-stat-fit.js +184 -0
  19. package/src/js/range-group.js +663 -0
  20. package/src/js/search-autocomplete-v2.js +907 -0
  21. package/src/js/search-autocomplete.js +434 -0
  22. package/src/js/settings-panel.js +245 -0
  23. package/src/js/split-button.js +141 -0
  24. package/src/js/splitter.js +1323 -0
  25. package/src/js/toast-service.js +302 -0
  26. package/src/js/tooltips-popovers.js +275 -0
  27. package/src/js/virtual-scroll.js +143 -0
  28. package/src/js/virtual-textbox.js +803 -0
  29. package/src/scss/_core.scss +7 -0
  30. package/src/scss/core-components/_buttons.scss +44 -0
  31. package/src/scss/core-components/_cards.scss +95 -6
  32. package/src/scss/core-components/_overflow.scss +50 -0
  33. package/src/scss/core-components/_range-group.scss +474 -0
  34. package/src/scss/core-components/_statistics.scss +163 -0
  35. 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
+ }