@flowerforce/flowerbase 1.7.5 → 1.7.6-beta.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 +125 -1
- package/dist/auth/controller.d.ts.map +1 -1
- package/dist/auth/controller.js +11 -10
- package/dist/auth/plugins/jwt.js +1 -1
- package/dist/auth/providers/anon-user/controller.js +1 -1
- package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
- package/dist/auth/providers/custom-function/controller.js +28 -7
- package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.js +15 -14
- package/dist/auth/utils.d.ts +1 -0
- package/dist/auth/utils.d.ts.map +1 -1
- package/dist/constants.d.ts +11 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +14 -3
- package/dist/features/encryption/interface.d.ts +36 -0
- package/dist/features/encryption/interface.d.ts.map +1 -0
- package/dist/features/encryption/interface.js +2 -0
- package/dist/features/encryption/utils.d.ts +9 -0
- package/dist/features/encryption/utils.d.ts.map +1 -0
- package/dist/features/encryption/utils.js +34 -0
- package/dist/features/rules/utils.d.ts.map +1 -1
- package/dist/features/rules/utils.js +1 -11
- package/dist/features/triggers/index.d.ts.map +1 -1
- package/dist/features/triggers/index.js +5 -1
- package/dist/features/triggers/utils.js +3 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/monitoring/plugin.d.ts.map +1 -1
- package/dist/monitoring/plugin.js +31 -0
- package/dist/monitoring/routes/users.d.ts.map +1 -1
- package/dist/monitoring/routes/users.js +7 -6
- package/dist/monitoring/utils.d.ts.map +1 -1
- package/dist/monitoring/utils.js +5 -4
- package/dist/services/api/index.d.ts +4 -0
- package/dist/services/api/index.d.ts.map +1 -1
- package/dist/services/api/utils.d.ts +1 -0
- package/dist/services/api/utils.d.ts.map +1 -1
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +9 -7
- package/dist/services/mongodb-atlas/model.d.ts +2 -1
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/dist/shared/handleUserDeletion.js +1 -1
- package/dist/shared/handleUserRegistration.js +2 -2
- package/dist/utils/context/helpers.d.ts +12 -0
- package/dist/utils/context/helpers.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +14 -3
- package/dist/utils/initializer/exposeRoutes.js +1 -1
- package/dist/utils/initializer/mongodbCSFLE.d.ts +69 -0
- package/dist/utils/initializer/mongodbCSFLE.d.ts.map +1 -0
- package/dist/utils/initializer/mongodbCSFLE.js +131 -0
- package/dist/utils/initializer/registerPlugins.d.ts +5 -1
- package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
- package/dist/utils/initializer/registerPlugins.js +27 -5
- package/dist/utils/rules-matcher/interface.d.ts +5 -1
- package/dist/utils/rules-matcher/interface.d.ts.map +1 -1
- package/dist/utils/rules-matcher/interface.js +2 -0
- package/dist/utils/rules-matcher/utils.d.ts.map +1 -1
- package/dist/utils/rules-matcher/utils.js +51 -16
- package/package.json +4 -2
- package/src/auth/__tests__/controller.test.ts +1 -0
- package/src/auth/controller.ts +12 -11
- package/src/auth/plugins/jwt.ts +2 -2
- package/src/auth/providers/anon-user/__tests__/controller.test.ts +1 -0
- package/src/auth/providers/anon-user/controller.ts +2 -2
- package/src/auth/providers/custom-function/controller.ts +29 -8
- package/src/auth/providers/local-userpass/controller.ts +16 -15
- package/src/auth/utils.ts +1 -0
- package/src/constants.ts +14 -4
- package/src/features/encryption/interface.ts +46 -0
- package/src/features/encryption/utils.ts +22 -0
- package/src/features/rules/utils.ts +1 -11
- package/src/features/triggers/__tests__/index.test.ts +1 -0
- package/src/features/triggers/index.ts +6 -2
- package/src/features/triggers/utils.ts +4 -4
- package/src/index.ts +10 -2
- package/src/monitoring/plugin.ts +33 -0
- package/src/monitoring/routes/users.ts +8 -7
- package/src/monitoring/ui.collections.js +7 -10
- package/src/monitoring/ui.css +383 -1
- package/src/monitoring/ui.endpoints.js +5 -10
- package/src/monitoring/ui.events.js +4 -6
- package/src/monitoring/ui.functions.js +64 -71
- package/src/monitoring/ui.html +8 -0
- package/src/monitoring/ui.js +189 -0
- package/src/monitoring/ui.shared.js +239 -3
- package/src/monitoring/ui.triggers.js +2 -3
- package/src/monitoring/ui.users.js +5 -9
- package/src/monitoring/utils.ts +6 -5
- package/src/services/mongodb-atlas/index.ts +10 -13
- package/src/services/mongodb-atlas/model.ts +3 -1
- package/src/shared/handleUserDeletion.ts +2 -2
- package/src/shared/handleUserRegistration.ts +3 -3
- package/src/types/fastify-raw-body.d.ts +0 -9
- package/src/utils/__tests__/mongodbCSFLE.test.ts +105 -0
- package/src/utils/__tests__/operators.test.ts +24 -0
- package/src/utils/__tests__/rule.test.ts +39 -0
- package/src/utils/__tests__/rulesMatcherInterfaces.test.ts +2 -0
- package/src/utils/index.ts +12 -1
- package/src/utils/initializer/exposeRoutes.ts +2 -2
- package/src/utils/initializer/mongodbCSFLE.ts +224 -0
- package/src/utils/initializer/registerPlugins.ts +45 -10
- package/src/utils/rules-matcher/interface.ts +5 -1
- package/src/utils/rules-matcher/utils.ts +78 -32
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
if (state.functionUserMap === undefined) state.functionUserMap = {};
|
|
16
16
|
if (state.functionUserQuery === undefined) state.functionUserQuery = '';
|
|
17
17
|
if (state.__functionUserTimer === undefined) state.__functionUserTimer = null;
|
|
18
|
+
if (state.functionCodeEditor === undefined) state.functionCodeEditor = null;
|
|
18
19
|
|
|
19
20
|
dom.functionList = document.getElementById('functionList');
|
|
20
21
|
dom.functionSelected = document.getElementById('functionSelected');
|
|
@@ -22,7 +23,6 @@
|
|
|
22
23
|
dom.functionUserInput = document.getElementById('functionUserInput');
|
|
23
24
|
dom.functionUserList = document.getElementById('functionUserList');
|
|
24
25
|
dom.functionRunMode = document.getElementById('functionRunMode');
|
|
25
|
-
dom.functionEditor = document.getElementById('functionEditor');
|
|
26
26
|
dom.functionCode = document.getElementById('functionCode');
|
|
27
27
|
dom.functionHighlight = document.getElementById('functionHighlight');
|
|
28
28
|
dom.functionGutter = document.getElementById('functionGutter');
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
functionUserInput,
|
|
43
43
|
functionUserList,
|
|
44
44
|
functionRunMode,
|
|
45
|
-
functionEditor,
|
|
46
45
|
functionCode,
|
|
47
46
|
functionHighlight,
|
|
48
47
|
functionGutter,
|
|
@@ -59,9 +58,31 @@
|
|
|
59
58
|
const { setActiveTab } = helpers;
|
|
60
59
|
|
|
61
60
|
const HISTORY_LIMIT = 30;
|
|
61
|
+
const getCodeEditor = () => state.functionCodeEditor;
|
|
62
|
+
|
|
63
|
+
const getFunctionCodeValue = () => {
|
|
64
|
+
const editor = getCodeEditor();
|
|
65
|
+
if (editor && typeof editor.getValue === 'function') {
|
|
66
|
+
return editor.getValue() || '';
|
|
67
|
+
}
|
|
68
|
+
return functionCode ? (functionCode.value || '') : '';
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const setFunctionCodeValue = (value) => {
|
|
72
|
+
const next = value || '';
|
|
73
|
+
const editor = getCodeEditor();
|
|
74
|
+
if (editor && typeof editor.setValue === 'function') {
|
|
75
|
+
editor.setValue(next);
|
|
76
|
+
editor.execCommand('goDocStart');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (functionCode) {
|
|
80
|
+
functionCode.value = next;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
62
83
|
|
|
63
84
|
const syncFunctionEditorScroll = () => {
|
|
64
|
-
if (!functionCode) return;
|
|
85
|
+
if (!functionCode || getCodeEditor()) return;
|
|
65
86
|
if (functionHighlight) {
|
|
66
87
|
functionHighlight.scrollTop = functionCode.scrollTop;
|
|
67
88
|
functionHighlight.scrollLeft = functionCode.scrollLeft;
|
|
@@ -72,6 +93,11 @@
|
|
|
72
93
|
};
|
|
73
94
|
|
|
74
95
|
const updateFunctionEditor = () => {
|
|
96
|
+
const editor = getCodeEditor();
|
|
97
|
+
if (editor) {
|
|
98
|
+
if (typeof editor.refresh === 'function') editor.refresh();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
75
101
|
if (!functionCode) return;
|
|
76
102
|
const code = functionCode.value || '';
|
|
77
103
|
if (functionHighlight) {
|
|
@@ -191,10 +217,10 @@
|
|
|
191
217
|
};
|
|
192
218
|
|
|
193
219
|
const isFunctionCodeModified = (name) => {
|
|
194
|
-
if (!name
|
|
220
|
+
if (!name) return false;
|
|
195
221
|
const base = state.functionCodeCache[name];
|
|
196
222
|
if (typeof base !== 'string') return false;
|
|
197
|
-
return
|
|
223
|
+
return getFunctionCodeValue() !== base;
|
|
198
224
|
};
|
|
199
225
|
|
|
200
226
|
const updateFunctionModifiedState = () => {
|
|
@@ -207,10 +233,10 @@
|
|
|
207
233
|
};
|
|
208
234
|
|
|
209
235
|
const loadFunctionCode = async () => {
|
|
210
|
-
if (!functionCode) return;
|
|
236
|
+
if (!functionCode && !getCodeEditor()) return;
|
|
211
237
|
const name = state.selectedFunction;
|
|
212
238
|
if (!name) {
|
|
213
|
-
|
|
239
|
+
setFunctionCodeValue('');
|
|
214
240
|
updateFunctionEditor();
|
|
215
241
|
updateFunctionModifiedState();
|
|
216
242
|
return;
|
|
@@ -219,7 +245,7 @@
|
|
|
219
245
|
const data = await api('/functions/' + encodeURIComponent(name));
|
|
220
246
|
const baseCode = data && data.code ? data.code : '';
|
|
221
247
|
state.functionCodeCache[name] = baseCode;
|
|
222
|
-
|
|
248
|
+
setFunctionCodeValue(baseCode);
|
|
223
249
|
updateFunctionEditor();
|
|
224
250
|
} catch (err) {
|
|
225
251
|
console.error(err);
|
|
@@ -228,8 +254,8 @@
|
|
|
228
254
|
};
|
|
229
255
|
|
|
230
256
|
const applyFunctionOverride = (code) => {
|
|
231
|
-
if (!functionCode) return;
|
|
232
|
-
|
|
257
|
+
if (!functionCode && !getCodeEditor()) return;
|
|
258
|
+
setFunctionCodeValue(code || '');
|
|
233
259
|
updateFunctionEditor();
|
|
234
260
|
updateFunctionModifiedState();
|
|
235
261
|
};
|
|
@@ -237,10 +263,8 @@
|
|
|
237
263
|
const clearFunctionOverride = () => {
|
|
238
264
|
const name = state.selectedFunction;
|
|
239
265
|
if (!name) return;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
updateFunctionEditor();
|
|
243
|
-
}
|
|
266
|
+
setFunctionCodeValue(state.functionCodeCache[name] || '');
|
|
267
|
+
updateFunctionEditor();
|
|
244
268
|
updateFunctionModifiedState();
|
|
245
269
|
};
|
|
246
270
|
|
|
@@ -396,10 +420,34 @@
|
|
|
396
420
|
renderFunctionUserOptions(options);
|
|
397
421
|
};
|
|
398
422
|
|
|
423
|
+
const initCodeEditor = () => {
|
|
424
|
+
if (!functionCode) return;
|
|
425
|
+
const codeEditor = typeof window !== 'undefined' ? window.CodeMirror : null;
|
|
426
|
+
if (!codeEditor || typeof codeEditor.fromTextArea !== 'function') return;
|
|
427
|
+
const editor = codeEditor.fromTextArea(functionCode, {
|
|
428
|
+
mode: 'javascript',
|
|
429
|
+
lineNumbers: true,
|
|
430
|
+
lineWrapping: false,
|
|
431
|
+
tabSize: 2,
|
|
432
|
+
indentUnit: 2,
|
|
433
|
+
foldGutter: true,
|
|
434
|
+
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
|
|
435
|
+
});
|
|
436
|
+
state.functionCodeEditor = editor;
|
|
437
|
+
const wrapper = editor.getWrapperElement();
|
|
438
|
+
if (wrapper) wrapper.classList.add('monit-code-editor');
|
|
439
|
+
const host = functionCode.closest('.code-editor');
|
|
440
|
+
if (host) host.classList.add('cm-enabled');
|
|
441
|
+
editor.on('change', () => {
|
|
442
|
+
updateFunctionModifiedState();
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
|
|
399
446
|
const init = () => {
|
|
400
447
|
if (refreshFunctions) refreshFunctions.addEventListener('click', loadFunctions);
|
|
448
|
+
initCodeEditor();
|
|
401
449
|
|
|
402
|
-
if (functionCode) {
|
|
450
|
+
if (functionCode && !getCodeEditor()) {
|
|
403
451
|
functionCode.addEventListener('input', () => {
|
|
404
452
|
updateFunctionEditor();
|
|
405
453
|
updateFunctionModifiedState();
|
|
@@ -407,61 +455,6 @@
|
|
|
407
455
|
functionCode.addEventListener('scroll', () => {
|
|
408
456
|
syncFunctionEditorScroll();
|
|
409
457
|
});
|
|
410
|
-
functionCode.addEventListener('keydown', (event) => {
|
|
411
|
-
if (event.key !== 'Tab') return;
|
|
412
|
-
event.preventDefault();
|
|
413
|
-
const indent = ' ';
|
|
414
|
-
const value = functionCode.value || '';
|
|
415
|
-
const start = functionCode.selectionStart || 0;
|
|
416
|
-
const end = functionCode.selectionEnd || 0;
|
|
417
|
-
const hasSelection = start !== end;
|
|
418
|
-
const lineStart = value.lastIndexOf('\n', start - 1) + 1;
|
|
419
|
-
if (!hasSelection && !event.shiftKey) {
|
|
420
|
-
const nextValue = value.slice(0, start) + indent + value.slice(end);
|
|
421
|
-
functionCode.value = nextValue;
|
|
422
|
-
const cursor = start + indent.length;
|
|
423
|
-
functionCode.selectionStart = cursor;
|
|
424
|
-
functionCode.selectionEnd = cursor;
|
|
425
|
-
updateFunctionEditor();
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const lineEndIndex = value.indexOf('\n', end);
|
|
430
|
-
const blockEnd = lineEndIndex === -1 ? value.length : lineEndIndex;
|
|
431
|
-
const block = value.slice(lineStart, blockEnd);
|
|
432
|
-
const lines = block.split('\n');
|
|
433
|
-
if (!event.shiftKey) {
|
|
434
|
-
const newLines = lines.map((line) => indent + line);
|
|
435
|
-
const newBlock = newLines.join('\n');
|
|
436
|
-
const nextValue = value.slice(0, lineStart) + newBlock + value.slice(blockEnd);
|
|
437
|
-
functionCode.value = nextValue;
|
|
438
|
-
functionCode.selectionStart = start + indent.length;
|
|
439
|
-
functionCode.selectionEnd = end + indent.length * lines.length;
|
|
440
|
-
updateFunctionEditor();
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const removedCounts = lines.map((line) => {
|
|
445
|
-
if (line.startsWith(indent)) return indent.length;
|
|
446
|
-
if (line.startsWith('\t')) return 1;
|
|
447
|
-
if (line.startsWith(' ')) return 1;
|
|
448
|
-
return 0;
|
|
449
|
-
});
|
|
450
|
-
const newLines = lines.map((line, index) => {
|
|
451
|
-
const remove = removedCounts[index];
|
|
452
|
-
return remove > 0 ? line.slice(remove) : line;
|
|
453
|
-
});
|
|
454
|
-
const newBlock = newLines.join('\n');
|
|
455
|
-
const nextValue = value.slice(0, lineStart) + newBlock + value.slice(blockEnd);
|
|
456
|
-
functionCode.value = nextValue;
|
|
457
|
-
const removedTotal = removedCounts.reduce((acc, count) => acc + count, 0);
|
|
458
|
-
const removedFirst = removedCounts[0] || 0;
|
|
459
|
-
const nextStart = Math.max(lineStart, start - removedFirst);
|
|
460
|
-
const nextEnd = Math.max(nextStart, end - removedTotal);
|
|
461
|
-
functionCode.selectionStart = nextStart;
|
|
462
|
-
functionCode.selectionEnd = nextEnd;
|
|
463
|
-
updateFunctionEditor();
|
|
464
|
-
});
|
|
465
458
|
}
|
|
466
459
|
|
|
467
460
|
if (functionList) {
|
|
@@ -606,7 +599,7 @@
|
|
|
606
599
|
const userId = selectedUser
|
|
607
600
|
? String(selectedUser.id || (selectedUser.auth && selectedUser.auth._id) || '')
|
|
608
601
|
: fallbackUserId;
|
|
609
|
-
const liveCode =
|
|
602
|
+
const liveCode = getFunctionCodeValue();
|
|
610
603
|
const overrideCode = liveCode.trim() ? liveCode : undefined;
|
|
611
604
|
const data = await api('/functions/invoke', {
|
|
612
605
|
method: 'POST',
|
package/src/monitoring/ui.html
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
<meta charset="utf-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
7
|
<title>Flowerbase monit</title>
|
|
8
|
+
<link rel="stylesheet" href="__MONIT_BASE__/vendor/codemirror/codemirror.css" />
|
|
9
|
+
<link rel="stylesheet" href="__MONIT_BASE__/vendor/codemirror/foldgutter.css" />
|
|
8
10
|
<link rel="stylesheet" href="__MONIT_BASE__/ui.css" />
|
|
9
11
|
</head>
|
|
10
12
|
|
|
@@ -441,6 +443,12 @@
|
|
|
441
443
|
</section>
|
|
442
444
|
</div>
|
|
443
445
|
</main>
|
|
446
|
+
<script src="__MONIT_BASE__/vendor/codemirror/codemirror.js"></script>
|
|
447
|
+
<script src="__MONIT_BASE__/vendor/codemirror/javascript.js"></script>
|
|
448
|
+
<script src="__MONIT_BASE__/vendor/codemirror/foldcode.js"></script>
|
|
449
|
+
<script src="__MONIT_BASE__/vendor/codemirror/foldgutter.js"></script>
|
|
450
|
+
<script src="__MONIT_BASE__/vendor/codemirror/brace-fold.js"></script>
|
|
451
|
+
<script src="__MONIT_BASE__/vendor/codemirror/comment-fold.js"></script>
|
|
444
452
|
<script src="__MONIT_BASE__/ui.shared.js"></script>
|
|
445
453
|
<script src="__MONIT_BASE__/ui.events.js"></script>
|
|
446
454
|
<script src="__MONIT_BASE__/ui.users.js"></script>
|
package/src/monitoring/ui.js
CHANGED
|
@@ -14,6 +14,194 @@
|
|
|
14
14
|
const { api } = utils;
|
|
15
15
|
const { setActiveTab } = helpers;
|
|
16
16
|
|
|
17
|
+
const RESIZE_MIN_PANE = 220;
|
|
18
|
+
const RESIZE_MIN_STACK = 140;
|
|
19
|
+
const RESIZE_MIN_RATIO = 0.2;
|
|
20
|
+
const RESIZE_MAX_RATIO = 0.8;
|
|
21
|
+
|
|
22
|
+
const getSplitDefault = (container) => {
|
|
23
|
+
if (container.classList.contains('functions-grid')) return '30%';
|
|
24
|
+
if (container.classList.contains('triggers-grid')) return '30%';
|
|
25
|
+
if (container.classList.contains('collections-grid')) return '30%';
|
|
26
|
+
return '66%';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
30
|
+
|
|
31
|
+
const getHorizontalResizeBounds = (containerWidth, handleWidth) => {
|
|
32
|
+
const minByPixels = RESIZE_MIN_PANE;
|
|
33
|
+
const maxByPixels = Math.max(minByPixels, containerWidth - RESIZE_MIN_PANE - handleWidth);
|
|
34
|
+
const minByRatio = containerWidth * RESIZE_MIN_RATIO;
|
|
35
|
+
const maxByRatio = containerWidth * RESIZE_MAX_RATIO;
|
|
36
|
+
const min = Math.max(minByPixels, minByRatio);
|
|
37
|
+
const max = Math.min(maxByPixels, maxByRatio);
|
|
38
|
+
if (max < min) return { min: max, max };
|
|
39
|
+
return {
|
|
40
|
+
min,
|
|
41
|
+
max
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const getVerticalResizeBounds = (totalHeight) => {
|
|
46
|
+
const minByPixels = RESIZE_MIN_STACK;
|
|
47
|
+
const maxByPixels = Math.max(minByPixels, totalHeight - RESIZE_MIN_STACK);
|
|
48
|
+
const minByRatio = totalHeight * RESIZE_MIN_RATIO;
|
|
49
|
+
const maxByRatio = totalHeight * RESIZE_MAX_RATIO;
|
|
50
|
+
const min = Math.max(minByPixels, minByRatio);
|
|
51
|
+
const max = Math.min(maxByPixels, maxByRatio);
|
|
52
|
+
if (max < min) return { min: max, max };
|
|
53
|
+
return {
|
|
54
|
+
min,
|
|
55
|
+
max
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const bindSplitResize = (container, handle, leftPane, rightPane) => {
|
|
60
|
+
const onPointerDown = (event) => {
|
|
61
|
+
if (event.button !== 0) return;
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
|
|
64
|
+
document.body.classList.add('is-resizing');
|
|
65
|
+
if (handle.setPointerCapture) handle.setPointerCapture(event.pointerId);
|
|
66
|
+
|
|
67
|
+
const onPointerMove = (moveEvent) => {
|
|
68
|
+
const bounds = container.getBoundingClientRect();
|
|
69
|
+
const handleWidth = handle.getBoundingClientRect().width || 10;
|
|
70
|
+
if (!bounds.width) return;
|
|
71
|
+
|
|
72
|
+
const limits = getHorizontalResizeBounds(bounds.width, handleWidth);
|
|
73
|
+
const offsetX = moveEvent.clientX - bounds.left - (handleWidth / 2);
|
|
74
|
+
const nextLeft = clamp(offsetX, limits.min, limits.max);
|
|
75
|
+
container.style.setProperty('--split-left-size', nextLeft + 'px');
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const onPointerUp = (upEvent) => {
|
|
79
|
+
document.body.classList.remove('is-resizing');
|
|
80
|
+
if (handle.releasePointerCapture) {
|
|
81
|
+
try {
|
|
82
|
+
handle.releasePointerCapture(upEvent.pointerId);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// no-op: pointer might already be released
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
window.removeEventListener('pointermove', onPointerMove);
|
|
88
|
+
window.removeEventListener('pointerup', onPointerUp);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
window.addEventListener('pointermove', onPointerMove);
|
|
92
|
+
window.addEventListener('pointerup', onPointerUp);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
handle.addEventListener('pointerdown', onPointerDown);
|
|
96
|
+
handle.addEventListener('keydown', (event) => {
|
|
97
|
+
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
const direction = event.key === 'ArrowLeft' ? -1 : 1;
|
|
100
|
+
const bounds = container.getBoundingClientRect();
|
|
101
|
+
const handleWidth = handle.getBoundingClientRect().width || 10;
|
|
102
|
+
const current = leftPane.getBoundingClientRect().width;
|
|
103
|
+
const limits = getHorizontalResizeBounds(bounds.width, handleWidth);
|
|
104
|
+
const nextLeft = clamp(current + (direction * 24), limits.min, limits.max);
|
|
105
|
+
container.style.setProperty('--split-left-size', nextLeft + 'px');
|
|
106
|
+
rightPane.offsetHeight;
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const bindStackResize = (handle, topPane, bottomPane) => {
|
|
111
|
+
const onPointerDown = (event) => {
|
|
112
|
+
if (event.button !== 0) return;
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
|
|
115
|
+
document.body.classList.add('is-resizing');
|
|
116
|
+
if (handle.setPointerCapture) handle.setPointerCapture(event.pointerId);
|
|
117
|
+
|
|
118
|
+
const topStart = topPane.getBoundingClientRect().height;
|
|
119
|
+
const bottomStart = bottomPane.getBoundingClientRect().height;
|
|
120
|
+
const total = topStart + bottomStart;
|
|
121
|
+
|
|
122
|
+
const onPointerMove = (moveEvent) => {
|
|
123
|
+
const topBounds = topPane.getBoundingClientRect();
|
|
124
|
+
const positionY = moveEvent.clientY - topBounds.top;
|
|
125
|
+
const handleHalf = (handle.getBoundingClientRect().height || 8) / 2;
|
|
126
|
+
const limits = getVerticalResizeBounds(total);
|
|
127
|
+
const boundedTop = clamp(positionY - handleHalf, limits.min, limits.max);
|
|
128
|
+
const boundedBottom = Math.max(limits.min, total - boundedTop);
|
|
129
|
+
|
|
130
|
+
topPane.style.flex = '0 0 ' + boundedTop + 'px';
|
|
131
|
+
bottomPane.style.flex = '0 0 ' + boundedBottom + 'px';
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const onPointerUp = (upEvent) => {
|
|
135
|
+
document.body.classList.remove('is-resizing');
|
|
136
|
+
if (handle.releasePointerCapture) {
|
|
137
|
+
try {
|
|
138
|
+
handle.releasePointerCapture(upEvent.pointerId);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
// no-op: pointer might already be released
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
window.removeEventListener('pointermove', onPointerMove);
|
|
144
|
+
window.removeEventListener('pointerup', onPointerUp);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
window.addEventListener('pointermove', onPointerMove);
|
|
148
|
+
window.addEventListener('pointerup', onPointerUp);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
handle.addEventListener('pointerdown', onPointerDown);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const initResizableSections = () => {
|
|
155
|
+
document.querySelectorAll('.split-grid').forEach((container) => {
|
|
156
|
+
if (container.dataset.resizableInit === '1') return;
|
|
157
|
+
const panes = Array.from(container.children).filter((element) =>
|
|
158
|
+
!element.classList.contains('split-resizer')
|
|
159
|
+
);
|
|
160
|
+
if (panes.length !== 2) return;
|
|
161
|
+
|
|
162
|
+
const [leftPane, rightPane] = panes;
|
|
163
|
+
const handle = document.createElement('div');
|
|
164
|
+
handle.className = 'split-resizer';
|
|
165
|
+
handle.tabIndex = 0;
|
|
166
|
+
handle.setAttribute('role', 'separator');
|
|
167
|
+
handle.setAttribute('aria-orientation', 'vertical');
|
|
168
|
+
handle.setAttribute('title', 'Resize columns');
|
|
169
|
+
|
|
170
|
+
container.classList.add('resizable-split-grid');
|
|
171
|
+
leftPane.classList.add('split-pane-left');
|
|
172
|
+
rightPane.classList.add('split-pane-right');
|
|
173
|
+
container.style.setProperty('--split-left-size', getSplitDefault(container));
|
|
174
|
+
container.insertBefore(handle, rightPane);
|
|
175
|
+
bindSplitResize(container, handle, leftPane, rightPane);
|
|
176
|
+
container.dataset.resizableInit = '1';
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
document.querySelectorAll('.column-stack').forEach((container) => {
|
|
180
|
+
if (container.dataset.stackResizableInit === '1') return;
|
|
181
|
+
const panels = Array.from(container.children).filter((element) =>
|
|
182
|
+
element.classList && element.classList.contains('subpanel')
|
|
183
|
+
);
|
|
184
|
+
if (panels.length < 2) return;
|
|
185
|
+
|
|
186
|
+
container.classList.add('resizable-stack');
|
|
187
|
+
panels.forEach((panel) => panel.classList.add('stack-pane'));
|
|
188
|
+
|
|
189
|
+
for (let index = 0; index < panels.length - 1; index += 1) {
|
|
190
|
+
const topPane = panels[index];
|
|
191
|
+
const bottomPane = panels[index + 1];
|
|
192
|
+
const handle = document.createElement('div');
|
|
193
|
+
handle.className = 'stack-resizer';
|
|
194
|
+
handle.setAttribute('role', 'separator');
|
|
195
|
+
handle.setAttribute('aria-orientation', 'horizontal');
|
|
196
|
+
handle.setAttribute('title', 'Resize sections');
|
|
197
|
+
container.insertBefore(handle, bottomPane);
|
|
198
|
+
bindStackResize(handle, topPane, bottomPane);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
container.dataset.stackResizableInit = '1';
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
|
|
17
205
|
const initModules = () => {
|
|
18
206
|
if (root.events && root.events.init) root.events.init();
|
|
19
207
|
if (root.users && root.users.init) root.users.init();
|
|
@@ -50,6 +238,7 @@
|
|
|
50
238
|
});
|
|
51
239
|
});
|
|
52
240
|
|
|
241
|
+
initResizableSections();
|
|
53
242
|
initModules();
|
|
54
243
|
|
|
55
244
|
updateClock();
|