brep-io-kernel 1.0.17 → 1.0.19
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/dist-kernel/brep-kernel.js +13547 -12034
- package/package.json +1 -1
- package/src/FeatureRegistry.js +3 -0
- package/src/PartHistory.js +141 -0
- package/src/UI/CADmaterials.js +2 -2
- package/src/UI/MainToolbar.js +39 -2
- package/src/UI/SelectionFilter.js +438 -0
- package/src/UI/featureDialogWidgets/booleanOperationField.js +33 -52
- package/src/UI/featureDialogWidgets/referenceSelectionField.js +10 -0
- package/src/UI/featureDialogs.js +841 -8
- package/src/UI/history/HistoryCollectionWidget.js +20 -1
- package/src/UI/pmi/AnnotationRegistry.js +3 -0
- package/src/UI/toolbarButtons/orientToFaceButton.js +3 -0
- package/src/UI/toolbarButtons/registerDefaultButtons.js +0 -6
- package/src/UI/toolbarButtons/registerSelectionButtons.js +68 -0
- package/src/UI/viewer.js +22 -4
- package/src/assemblyConstraints/AssemblyConstraintRegistry.js +3 -0
- package/src/features/boolean/BooleanFeature.js +15 -0
- package/src/features/chamfer/ChamferFeature.js +12 -0
- package/src/features/extrude/ExtrudeFeature.js +11 -0
- package/src/features/fillet/FilletFeature.js +12 -0
- package/src/features/hole/HoleFeature.js +15 -0
- package/src/features/loft/LoftFeature.js +17 -0
- package/src/features/mirror/MirrorFeature.js +14 -0
- package/src/features/patternLinear/PatternLinearFeature.js +9 -0
- package/src/features/patternRadial/PatternRadialFeature.js +13 -0
- package/src/features/plane/PlaneFeature.js +10 -0
- package/src/features/revolve/RevolveFeature.js +15 -0
- package/src/features/sketch/SketchFeature.js +11 -0
- package/src/features/sweep/SweepFeature.js +17 -0
- package/src/features/transform/TransformFeature.js +12 -0
- package/src/features/tube/TubeFeature.js +12 -0
|
@@ -17,6 +17,15 @@ export class SelectionFilter {
|
|
|
17
17
|
static previouseAllowedSelectionTypes = null;
|
|
18
18
|
static _hovered = new Set(); // objects currently hover-highlighted
|
|
19
19
|
static hoverColor = '#fbff00'; // default hover tint
|
|
20
|
+
static _selectionActions = new Map();
|
|
21
|
+
static _selectionActionOrder = [];
|
|
22
|
+
static _selectionActionSeq = 1;
|
|
23
|
+
static _selectionActionListenerBound = false;
|
|
24
|
+
static _selectionActionsPending = false;
|
|
25
|
+
static _selectionActionBar = null;
|
|
26
|
+
static _historyContextActions = new Map();
|
|
27
|
+
static _selectionActionSeparator = null;
|
|
28
|
+
static _contextSuppressReasons = new Set();
|
|
20
29
|
|
|
21
30
|
constructor() {
|
|
22
31
|
throw new Error("SelectionFilter is static and cannot be instantiated.");
|
|
@@ -188,6 +197,25 @@ export class SelectionFilter {
|
|
|
188
197
|
let clone;
|
|
189
198
|
try { clone = typeof origMat.clone === 'function' ? origMat.clone() : origMat; } catch { clone = origMat; }
|
|
190
199
|
try { if (clone && clone.color && typeof clone.color.set === 'function') clone.color.set(SelectionFilter.hoverColor); } catch { }
|
|
200
|
+
try {
|
|
201
|
+
if (origMat && clone && origMat.resolution && clone.resolution && typeof clone.resolution.copy === 'function') {
|
|
202
|
+
clone.resolution.copy(origMat.resolution);
|
|
203
|
+
}
|
|
204
|
+
} catch { }
|
|
205
|
+
try {
|
|
206
|
+
if (origMat && clone && typeof origMat.dashed !== 'undefined' && typeof clone.dashed !== 'undefined') {
|
|
207
|
+
clone.dashed = origMat.dashed;
|
|
208
|
+
}
|
|
209
|
+
if (origMat && clone && typeof origMat.dashSize !== 'undefined' && typeof clone.dashSize !== 'undefined') {
|
|
210
|
+
clone.dashSize = origMat.dashSize;
|
|
211
|
+
}
|
|
212
|
+
if (origMat && clone && typeof origMat.gapSize !== 'undefined' && typeof clone.gapSize !== 'undefined') {
|
|
213
|
+
clone.gapSize = origMat.gapSize;
|
|
214
|
+
}
|
|
215
|
+
if (origMat && clone && typeof origMat.dashScale !== 'undefined' && typeof clone.dashScale !== 'undefined') {
|
|
216
|
+
clone.dashScale = origMat.dashScale;
|
|
217
|
+
}
|
|
218
|
+
} catch { }
|
|
191
219
|
try {
|
|
192
220
|
t.userData.__hoverOrigMat = origMat;
|
|
193
221
|
t.userData.__hoverMatApplied = true;
|
|
@@ -429,6 +457,11 @@ export class SelectionFilter {
|
|
|
429
457
|
if (!targetObj) return false;
|
|
430
458
|
|
|
431
459
|
// Update the reference input with the chosen object
|
|
460
|
+
try {
|
|
461
|
+
if (activeRefInput && typeof activeRefInput.__captureReferencePreview === 'function') {
|
|
462
|
+
activeRefInput.__captureReferencePreview(targetObj);
|
|
463
|
+
}
|
|
464
|
+
} catch (_) { /* ignore preview capture errors */ }
|
|
432
465
|
const objType = targetObj.type;
|
|
433
466
|
const objectName = targetObj.name || `${objType}(${targetObj.position?.x || 0},${targetObj.position?.y || 0},${targetObj.position?.z || 0})`;
|
|
434
467
|
|
|
@@ -617,6 +650,411 @@ export class SelectionFilter {
|
|
|
617
650
|
} catch (_) { /* noop */ }
|
|
618
651
|
}
|
|
619
652
|
|
|
653
|
+
static getSelectedObjects(options = {}) {
|
|
654
|
+
const scene = options.scene
|
|
655
|
+
|| SelectionFilter.viewer?.partHistory?.scene
|
|
656
|
+
|| SelectionFilter.viewer?.scene
|
|
657
|
+
|| null;
|
|
658
|
+
const selected = [];
|
|
659
|
+
if (!scene || typeof scene.traverse !== 'function') return selected;
|
|
660
|
+
scene.traverse((obj) => {
|
|
661
|
+
if (obj && obj.selected) selected.push(obj);
|
|
662
|
+
});
|
|
663
|
+
return selected;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
static registerSelectionAction(spec = {}) {
|
|
667
|
+
if (!spec) return null;
|
|
668
|
+
const id = String(spec.id || `selection-action-${SelectionFilter._selectionActionSeq++}`);
|
|
669
|
+
const entry = SelectionFilter._selectionActions.get(id) || { id };
|
|
670
|
+
entry.label = spec.label ?? entry.label ?? '';
|
|
671
|
+
entry.title = spec.title ?? entry.title ?? entry.label ?? '';
|
|
672
|
+
entry.onClick = spec.onClick ?? entry.onClick ?? null;
|
|
673
|
+
entry.shouldShow = typeof spec.shouldShow === 'function' ? spec.shouldShow : (entry.shouldShow || null);
|
|
674
|
+
SelectionFilter._selectionActions.set(id, entry);
|
|
675
|
+
if (!SelectionFilter._selectionActionOrder.includes(id)) {
|
|
676
|
+
SelectionFilter._selectionActionOrder.push(id);
|
|
677
|
+
}
|
|
678
|
+
SelectionFilter._ensureSelectionActionListener();
|
|
679
|
+
SelectionFilter._syncSelectionActions();
|
|
680
|
+
return id;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
static unregisterSelectionAction(id) {
|
|
684
|
+
if (!id) return;
|
|
685
|
+
const entry = SelectionFilter._selectionActions.get(id);
|
|
686
|
+
if (entry?.btn && entry.btn.parentNode) {
|
|
687
|
+
try { entry.btn.parentNode.removeChild(entry.btn); } catch { }
|
|
688
|
+
}
|
|
689
|
+
SelectionFilter._selectionActions.delete(id);
|
|
690
|
+
SelectionFilter._selectionActionOrder = SelectionFilter._selectionActionOrder.filter((k) => k !== id);
|
|
691
|
+
SelectionFilter._syncSelectionActions();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
static refreshSelectionActions() {
|
|
695
|
+
SelectionFilter._syncSelectionActions();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
static _ensureSelectionActionListener() {
|
|
699
|
+
if (SelectionFilter._selectionActionListenerBound) return;
|
|
700
|
+
if (typeof window === 'undefined') return;
|
|
701
|
+
SelectionFilter._selectionActionListenerBound = true;
|
|
702
|
+
window.addEventListener('selection-changed', () => SelectionFilter._syncSelectionActions());
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
static _syncSelectionActions() {
|
|
706
|
+
const viewer = SelectionFilter.viewer;
|
|
707
|
+
const bar = SelectionFilter._ensureSelectionActionBar(viewer);
|
|
708
|
+
if (!bar) {
|
|
709
|
+
SelectionFilter._selectionActionsPending = true;
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
SelectionFilter._selectionActionsPending = false;
|
|
713
|
+
const suppressed = SelectionFilter._contextSuppressReasons?.size > 0;
|
|
714
|
+
if (suppressed) {
|
|
715
|
+
try { bar.style.display = 'none'; } catch { }
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const selection = SelectionFilter.getSelectedObjects();
|
|
719
|
+
const hideAll = !!viewer?._sketchMode;
|
|
720
|
+
const utilityButtons = [];
|
|
721
|
+
const actions = SelectionFilter._selectionActions;
|
|
722
|
+
|
|
723
|
+
for (const id of SelectionFilter._selectionActionOrder) {
|
|
724
|
+
const entry = actions.get(id);
|
|
725
|
+
if (!entry) continue;
|
|
726
|
+
if (!entry.btn) {
|
|
727
|
+
entry.btn = SelectionFilter._createSelectionActionButton(entry);
|
|
728
|
+
}
|
|
729
|
+
if (!entry.btn) continue;
|
|
730
|
+
try {
|
|
731
|
+
entry.btn.textContent = String(entry.label ?? '');
|
|
732
|
+
entry.btn.title = String(entry.title ?? entry.label ?? '');
|
|
733
|
+
entry.btn.__sabOnClick = entry.onClick;
|
|
734
|
+
const isIcon = String(entry.label || '').length <= 2;
|
|
735
|
+
entry.btn.classList.toggle('sab-icon', isIcon);
|
|
736
|
+
} catch { }
|
|
737
|
+
let show = !hideAll;
|
|
738
|
+
if (show) {
|
|
739
|
+
if (typeof entry.shouldShow === 'function') {
|
|
740
|
+
try { show = !!entry.shouldShow(selection, viewer); } catch { show = false; }
|
|
741
|
+
} else {
|
|
742
|
+
show = selection.length > 0;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (show) utilityButtons.push(entry.btn);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const historySpecs = SelectionFilter._getHistoryContextActionSpecs(selection, viewer);
|
|
749
|
+
const contextButtons = [];
|
|
750
|
+
const desiredIds = new Set();
|
|
751
|
+
for (const spec of historySpecs) {
|
|
752
|
+
if (!spec || !spec.id) continue;
|
|
753
|
+
desiredIds.add(spec.id);
|
|
754
|
+
const existing = SelectionFilter._historyContextActions.get(spec.id) || { id: spec.id };
|
|
755
|
+
existing.label = spec.label ?? existing.label ?? '';
|
|
756
|
+
existing.title = spec.title ?? existing.title ?? existing.label ?? '';
|
|
757
|
+
existing.onClick = spec.onClick ?? existing.onClick ?? null;
|
|
758
|
+
existing.shouldShow = typeof spec.shouldShow === 'function' ? spec.shouldShow : null;
|
|
759
|
+
if (!existing.btn) {
|
|
760
|
+
existing.btn = SelectionFilter._createSelectionActionButton(existing);
|
|
761
|
+
}
|
|
762
|
+
if (!existing.btn) continue;
|
|
763
|
+
try {
|
|
764
|
+
existing.btn.textContent = String(existing.label ?? '');
|
|
765
|
+
existing.btn.title = String(existing.title ?? existing.label ?? '');
|
|
766
|
+
existing.btn.__sabOnClick = existing.onClick;
|
|
767
|
+
const isIcon = String(existing.label || '').length <= 2;
|
|
768
|
+
existing.btn.classList.toggle('sab-icon', isIcon);
|
|
769
|
+
} catch { }
|
|
770
|
+
let show = !hideAll;
|
|
771
|
+
if (show && typeof existing.shouldShow === 'function') {
|
|
772
|
+
try { show = !!existing.shouldShow(selection, viewer); } catch { show = false; }
|
|
773
|
+
} else if (!existing.shouldShow) {
|
|
774
|
+
show = show && selection.length > 0;
|
|
775
|
+
}
|
|
776
|
+
if (show) contextButtons.push(existing.btn);
|
|
777
|
+
SelectionFilter._historyContextActions.set(spec.id, existing);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
for (const [id, entry] of SelectionFilter._historyContextActions.entries()) {
|
|
781
|
+
if (desiredIds.has(id)) continue;
|
|
782
|
+
try { entry.btn?.remove?.(); } catch { }
|
|
783
|
+
SelectionFilter._historyContextActions.delete(id);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
try { bar.textContent = ''; } catch { }
|
|
787
|
+
for (const btn of utilityButtons) {
|
|
788
|
+
try { bar.appendChild(btn); } catch { }
|
|
789
|
+
}
|
|
790
|
+
if (utilityButtons.length && contextButtons.length) {
|
|
791
|
+
const sep = SelectionFilter._ensureSelectionActionSeparator();
|
|
792
|
+
if (sep) {
|
|
793
|
+
try { bar.appendChild(sep); } catch { }
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
for (const btn of contextButtons) {
|
|
797
|
+
try { bar.appendChild(btn); } catch { }
|
|
798
|
+
}
|
|
799
|
+
try { bar.style.display = (utilityButtons.length + contextButtons.length) > 0 ? 'flex' : 'none'; } catch { }
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
static _getHistoryContextActionSpecs(selection, viewer) {
|
|
803
|
+
const out = [];
|
|
804
|
+
const items = Array.isArray(selection) ? selection : [];
|
|
805
|
+
const safeId = (prefix, key) => {
|
|
806
|
+
const raw = String(key || '').toLowerCase().replace(/[^a-z0-9_-]+/g, '-');
|
|
807
|
+
return `${prefix}-${raw || 'item'}`;
|
|
808
|
+
};
|
|
809
|
+
const addSpec = (spec) => {
|
|
810
|
+
if (spec && spec.id) out.push(spec);
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
const featureRegistry = viewer?.partHistory?.featureRegistry || null;
|
|
814
|
+
const features = Array.isArray(featureRegistry?.features) ? featureRegistry.features : [];
|
|
815
|
+
for (const FeatureClass of features) {
|
|
816
|
+
if (!FeatureClass) continue;
|
|
817
|
+
let result = null;
|
|
818
|
+
try { result = FeatureClass.showContexButton?.(items); } catch { result = null; }
|
|
819
|
+
if (!result) continue;
|
|
820
|
+
if (result && typeof result === 'object' && result.show === false) continue;
|
|
821
|
+
const label = (result && typeof result === 'object' && result.label) || FeatureClass.longName || FeatureClass.shortName || FeatureClass.name || 'Feature';
|
|
822
|
+
const typeKey = FeatureClass.shortName || FeatureClass.type || FeatureClass.name || label;
|
|
823
|
+
const params = SelectionFilter._extractContextParams(result);
|
|
824
|
+
addSpec({
|
|
825
|
+
id: safeId('ctx-feature', typeKey),
|
|
826
|
+
label,
|
|
827
|
+
title: `Create ${label}`,
|
|
828
|
+
onClick: () => SelectionFilter._createFeatureFromContext(viewer, typeKey, params),
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const constraintRegistry = viewer?.partHistory?.assemblyConstraintRegistry || null;
|
|
833
|
+
const constraintClasses = typeof constraintRegistry?.listAvailable === 'function'
|
|
834
|
+
? constraintRegistry.listAvailable()
|
|
835
|
+
: (typeof constraintRegistry?.list === 'function' ? constraintRegistry.list() : []);
|
|
836
|
+
if (Array.isArray(constraintClasses)) {
|
|
837
|
+
for (const ConstraintClass of constraintClasses) {
|
|
838
|
+
if (!ConstraintClass) continue;
|
|
839
|
+
let result = null;
|
|
840
|
+
try { result = ConstraintClass.showContexButton?.(items); } catch { result = null; }
|
|
841
|
+
if (!result) continue;
|
|
842
|
+
if (result && typeof result === 'object' && result.show === false) continue;
|
|
843
|
+
const label = (result && typeof result === 'object' && result.label) || ConstraintClass.longName || ConstraintClass.shortName || ConstraintClass.name || 'Constraint';
|
|
844
|
+
const typeKey = ConstraintClass.constraintType || ConstraintClass.shortName || ConstraintClass.name || label;
|
|
845
|
+
const params = SelectionFilter._extractContextParams(result);
|
|
846
|
+
addSpec({
|
|
847
|
+
id: safeId('ctx-constraint', typeKey),
|
|
848
|
+
label,
|
|
849
|
+
title: `Create ${label}`,
|
|
850
|
+
onClick: () => SelectionFilter._createConstraintFromContext(viewer, typeKey, params),
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const pmimode = viewer?._pmiMode || null;
|
|
856
|
+
const annotationRegistry = viewer?.annotationRegistry || null;
|
|
857
|
+
if (pmimode && annotationRegistry && typeof annotationRegistry.list === 'function') {
|
|
858
|
+
const annClasses = annotationRegistry.list();
|
|
859
|
+
for (const AnnClass of annClasses) {
|
|
860
|
+
if (!AnnClass) continue;
|
|
861
|
+
let result = null;
|
|
862
|
+
try { result = AnnClass.showContexButton?.(items); } catch { result = null; }
|
|
863
|
+
if (!result) continue;
|
|
864
|
+
if (result && typeof result === 'object' && result.show === false) continue;
|
|
865
|
+
const label = (result && typeof result === 'object' && result.label) || AnnClass.longName || AnnClass.shortName || AnnClass.name || 'Annotation';
|
|
866
|
+
const typeKey = AnnClass.type || AnnClass.entityType || AnnClass.shortName || AnnClass.name || label;
|
|
867
|
+
const params = SelectionFilter._extractContextParams(result);
|
|
868
|
+
addSpec({
|
|
869
|
+
id: safeId('ctx-annotation', typeKey),
|
|
870
|
+
label,
|
|
871
|
+
title: `Create ${label}`,
|
|
872
|
+
onClick: () => SelectionFilter._createAnnotationFromContext(viewer, typeKey, params),
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
return out;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
static async _createFeatureFromContext(viewer, typeKey, params = null) {
|
|
881
|
+
if (!viewer || !typeKey) return;
|
|
882
|
+
SelectionFilter.setContextBarSuppressed('context-create', true);
|
|
883
|
+
setTimeout(() => SelectionFilter.setContextBarSuppressed('context-create', false), 0);
|
|
884
|
+
let entry = null;
|
|
885
|
+
if (viewer.historyWidget && typeof viewer.historyWidget._handleAddEntry === 'function') {
|
|
886
|
+
try { entry = await viewer.historyWidget._handleAddEntry(typeKey); } catch { }
|
|
887
|
+
} else {
|
|
888
|
+
try { entry = await viewer.partHistory?.newFeature?.(typeKey); } catch { }
|
|
889
|
+
}
|
|
890
|
+
if (entry && params && typeof params === 'object') {
|
|
891
|
+
SelectionFilter._applyContextParamsToEntry(viewer, entry, params);
|
|
892
|
+
}
|
|
893
|
+
return entry;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
static _createConstraintFromContext(viewer, typeKey, params = null) {
|
|
897
|
+
if (!viewer || !typeKey) return;
|
|
898
|
+
SelectionFilter.setContextBarSuppressed('context-create', true);
|
|
899
|
+
setTimeout(() => SelectionFilter.setContextBarSuppressed('context-create', false), 0);
|
|
900
|
+
try { viewer.partHistory?.assemblyConstraintHistory?.addConstraint?.(typeKey, params || null); } catch { }
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
static _createAnnotationFromContext(viewer, typeKey, params = null) {
|
|
904
|
+
if (!viewer || !typeKey) return;
|
|
905
|
+
SelectionFilter.setContextBarSuppressed('context-create', true);
|
|
906
|
+
setTimeout(() => SelectionFilter.setContextBarSuppressed('context-create', false), 0);
|
|
907
|
+
try { viewer._pmiMode?._annotationHistory?.createAnnotation?.(typeKey, params || null); } catch { }
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
static _extractContextParams(result) {
|
|
911
|
+
if (!result || result === true) return null;
|
|
912
|
+
if (typeof result !== 'object') return null;
|
|
913
|
+
if (result.params && typeof result.params === 'object') return result.params;
|
|
914
|
+
if (result.field) {
|
|
915
|
+
return { [result.field]: result.value };
|
|
916
|
+
}
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
static _applyContextParamsToEntry(viewer, entry, params = {}) {
|
|
921
|
+
if (!entry || !params || typeof params !== 'object') return;
|
|
922
|
+
try {
|
|
923
|
+
for (const [key, value] of Object.entries(params)) {
|
|
924
|
+
entry.inputParams = entry.inputParams || {};
|
|
925
|
+
entry.inputParams[key] = value;
|
|
926
|
+
}
|
|
927
|
+
} catch { }
|
|
928
|
+
const historyWidget = viewer?.historyWidget || null;
|
|
929
|
+
if (!historyWidget || typeof historyWidget._handleSchemaChange !== 'function') return;
|
|
930
|
+
try {
|
|
931
|
+
const id = entry?.inputParams?.id ?? entry?.id ?? null;
|
|
932
|
+
const entryId = String(id ?? '');
|
|
933
|
+
historyWidget._handleSchemaChange(entryId, entry, { key: '__context', value: params });
|
|
934
|
+
const refresh = () => {
|
|
935
|
+
try {
|
|
936
|
+
const form = historyWidget.getFormForEntry?.(entryId);
|
|
937
|
+
if (form && typeof form.refreshFromParams === 'function') {
|
|
938
|
+
form.refreshFromParams();
|
|
939
|
+
return true;
|
|
940
|
+
}
|
|
941
|
+
} catch { }
|
|
942
|
+
return false;
|
|
943
|
+
};
|
|
944
|
+
if (!refresh()) {
|
|
945
|
+
setTimeout(() => { try { refresh(); } catch { } }, 0);
|
|
946
|
+
}
|
|
947
|
+
} catch { }
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
static setContextBarSuppressed(key, active) {
|
|
951
|
+
if (!key) return;
|
|
952
|
+
const reasons = SelectionFilter._contextSuppressReasons || new Set();
|
|
953
|
+
SelectionFilter._contextSuppressReasons = reasons;
|
|
954
|
+
const had = reasons.has(key);
|
|
955
|
+
if (active) {
|
|
956
|
+
reasons.add(key);
|
|
957
|
+
} else {
|
|
958
|
+
reasons.delete(key);
|
|
959
|
+
}
|
|
960
|
+
if (had !== reasons.has(key)) {
|
|
961
|
+
SelectionFilter._syncSelectionActions();
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
static _ensureSelectionActionSeparator() {
|
|
966
|
+
if (SelectionFilter._selectionActionSeparator && SelectionFilter._selectionActionSeparator.isConnected) {
|
|
967
|
+
return SelectionFilter._selectionActionSeparator;
|
|
968
|
+
}
|
|
969
|
+
if (typeof document === 'undefined') return null;
|
|
970
|
+
const el = document.createElement('div');
|
|
971
|
+
el.className = 'selection-action-sep';
|
|
972
|
+
SelectionFilter._selectionActionSeparator = el;
|
|
973
|
+
return el;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
static _ensureSelectionActionBar(viewer) {
|
|
977
|
+
if (SelectionFilter._selectionActionBar && SelectionFilter._selectionActionBar.isConnected) {
|
|
978
|
+
return SelectionFilter._selectionActionBar;
|
|
979
|
+
}
|
|
980
|
+
if (typeof document === 'undefined') return null;
|
|
981
|
+
const host = viewer?.container || document.body || null;
|
|
982
|
+
if (!host) return null;
|
|
983
|
+
try {
|
|
984
|
+
if (!document.getElementById('selection-action-bar-styles')) {
|
|
985
|
+
const style = document.createElement('style');
|
|
986
|
+
style.id = 'selection-action-bar-styles';
|
|
987
|
+
style.textContent = `
|
|
988
|
+
.selection-action-bar {
|
|
989
|
+
position: absolute;
|
|
990
|
+
top: 100px;
|
|
991
|
+
right: 8px;
|
|
992
|
+
display: flex;
|
|
993
|
+
flex-direction: column;
|
|
994
|
+
gap: 6px;
|
|
995
|
+
align-items: stretch;
|
|
996
|
+
background: rgba(20,24,30,.85);
|
|
997
|
+
border: 1px solid #262b36;
|
|
998
|
+
border-radius: 8px;
|
|
999
|
+
padding: 6px;
|
|
1000
|
+
color: #ddd;
|
|
1001
|
+
min-width: 40px;
|
|
1002
|
+
max-width: 150px;
|
|
1003
|
+
z-index: 12;
|
|
1004
|
+
user-select: none;
|
|
1005
|
+
}
|
|
1006
|
+
.selection-action-bar .sab-btn {
|
|
1007
|
+
background: transparent;
|
|
1008
|
+
border-radius: 6px;
|
|
1009
|
+
padding: 4px 8px;
|
|
1010
|
+
width: 100%;
|
|
1011
|
+
min-height: 34px;
|
|
1012
|
+
box-sizing: border-box;
|
|
1013
|
+
color: #ddd;
|
|
1014
|
+
border: 1px solid #364053;
|
|
1015
|
+
cursor: pointer;
|
|
1016
|
+
}
|
|
1017
|
+
.selection-action-bar .sab-btn:hover { filter: brightness(1.08); }
|
|
1018
|
+
.selection-action-bar .sab-btn:active { filter: brightness(1.15); }
|
|
1019
|
+
.selection-action-bar .sab-btn.sab-icon {
|
|
1020
|
+
font-size: 16px;
|
|
1021
|
+
min-width: 36px;
|
|
1022
|
+
}
|
|
1023
|
+
.selection-action-bar .selection-action-sep {
|
|
1024
|
+
height: 1px;
|
|
1025
|
+
width: 100%;
|
|
1026
|
+
background: #2c3443;
|
|
1027
|
+
opacity: 0.9;
|
|
1028
|
+
margin: 4px 0;
|
|
1029
|
+
}
|
|
1030
|
+
`;
|
|
1031
|
+
document.head.appendChild(style);
|
|
1032
|
+
}
|
|
1033
|
+
} catch { }
|
|
1034
|
+
const bar = document.createElement('div');
|
|
1035
|
+
bar.className = 'selection-action-bar';
|
|
1036
|
+
host.appendChild(bar);
|
|
1037
|
+
SelectionFilter._selectionActionBar = bar;
|
|
1038
|
+
return bar;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
static _createSelectionActionButton(entry) {
|
|
1042
|
+
try {
|
|
1043
|
+
const btn = document.createElement('button');
|
|
1044
|
+
btn.className = 'sab-btn';
|
|
1045
|
+
btn.textContent = String(entry?.label ?? '');
|
|
1046
|
+
btn.title = String(entry?.title ?? entry?.label ?? '');
|
|
1047
|
+
btn.__sabOnClick = entry?.onClick ?? null;
|
|
1048
|
+
btn.addEventListener('click', (e) => {
|
|
1049
|
+
e.stopPropagation();
|
|
1050
|
+
try { btn.__sabOnClick && btn.__sabOnClick(); } catch { }
|
|
1051
|
+
});
|
|
1052
|
+
const isIcon = String(entry?.label || '').length <= 2;
|
|
1053
|
+
if (isIcon) btn.classList.add('sab-icon');
|
|
1054
|
+
return btn;
|
|
1055
|
+
} catch { return null; }
|
|
1056
|
+
}
|
|
1057
|
+
|
|
620
1058
|
static #logAllowedTypesChange(next, reason = '') {
|
|
621
1059
|
try {
|
|
622
1060
|
const desc = next === SelectionFilter.ALL
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { renderReferenceSelectionField } from './referenceSelectionField.js';
|
|
2
|
+
|
|
1
3
|
export function renderBooleanOperationField({ ui, key, def, controlWrap }) {
|
|
2
4
|
if (!ui.params[key] || typeof ui.params[key] !== 'object') {
|
|
3
|
-
ui.params[key] = { targets: [], operation: 'NONE'
|
|
5
|
+
ui.params[key] = { targets: [], operation: 'NONE' };
|
|
4
6
|
} else {
|
|
5
7
|
if (!Array.isArray(ui.params[key].targets)) ui.params[key].targets = [];
|
|
6
|
-
if (!ui.params[key].operation
|
|
8
|
+
if (!ui.params[key].operation) ui.params[key].operation = 'NONE';
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
const wrap = document.createElement('div');
|
|
@@ -19,70 +21,49 @@ export function renderBooleanOperationField({ ui, key, def, controlWrap }) {
|
|
|
19
21
|
opt.textContent = String(op);
|
|
20
22
|
sel.appendChild(opt);
|
|
21
23
|
}
|
|
22
|
-
sel.value = String(
|
|
24
|
+
sel.value = String(ui.params[key].operation || 'NONE');
|
|
23
25
|
sel.addEventListener('change', () => {
|
|
24
26
|
if (!ui.params[key] || typeof ui.params[key] !== 'object') ui.params[key] = { targets: [], operation: 'NONE' };
|
|
25
27
|
ui.params[key].operation = sel.value;
|
|
26
|
-
ui.params[key].operation = sel.value;
|
|
27
28
|
ui._emitParamsChange(key, ui.params[key]);
|
|
28
29
|
});
|
|
29
30
|
wrap.appendChild(sel);
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const inputElTargets = document.createElement('input');
|
|
38
|
-
inputElTargets.type = 'text';
|
|
39
|
-
inputElTargets.className = 'input';
|
|
40
|
-
inputElTargets.dataset.multiple = 'true';
|
|
41
|
-
inputElTargets.placeholder = 'Click then select solids…';
|
|
42
|
-
ui._renderChips(chipsWrap, key, Array.isArray(ui.params[key].targets) ? ui.params[key].targets : []);
|
|
43
|
-
|
|
44
|
-
const activate = () => {
|
|
45
|
-
ui._activateReferenceSelection(inputElTargets, { selectionFilter: ['SOLID'] });
|
|
32
|
+
const refMount = document.createElement('div');
|
|
33
|
+
const targetsDef = {
|
|
34
|
+
type: 'reference_selection',
|
|
35
|
+
multiple: true,
|
|
36
|
+
selectionFilter: ['SOLID'],
|
|
46
37
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
ui.
|
|
55
|
-
|
|
56
|
-
|
|
38
|
+
const valueAdapter = {
|
|
39
|
+
read: () => {
|
|
40
|
+
const current = ui.params[key];
|
|
41
|
+
if (!current || typeof current !== 'object') return [];
|
|
42
|
+
return Array.isArray(current.targets) ? current.targets : [];
|
|
43
|
+
},
|
|
44
|
+
write: (next) => {
|
|
45
|
+
if (!ui.params[key] || typeof ui.params[key] !== 'object') ui.params[key] = { targets: [], operation: sel.value || 'NONE' };
|
|
46
|
+
ui.params[key].targets = Array.isArray(next) ? next : [];
|
|
47
|
+
},
|
|
48
|
+
emit: () => {
|
|
57
49
|
ui._emitParamsChange(key, ui.params[key]);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
const cur = Array.isArray(ui.params[key].targets) ? ui.params[key].targets : [];
|
|
69
|
-
for (const name of incoming) {
|
|
70
|
-
if (!cur.includes(name)) cur.push(name);
|
|
71
|
-
}
|
|
72
|
-
ui.params[key].targets = cur;
|
|
73
|
-
ui._renderChips(chipsWrap, key, cur);
|
|
74
|
-
inputElTargets.value = '';
|
|
75
|
-
ui._emitParamsChange(key, ui.params[key]);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
const refField = renderReferenceSelectionField({
|
|
53
|
+
ui,
|
|
54
|
+
key,
|
|
55
|
+
def: targetsDef,
|
|
56
|
+
id: `${key}-targets`,
|
|
57
|
+
controlWrap: refMount,
|
|
58
|
+
valueAdapter,
|
|
76
59
|
});
|
|
77
|
-
|
|
78
|
-
refWrap.appendChild(inputElTargets);
|
|
79
|
-
wrap.appendChild(refWrap);
|
|
60
|
+
wrap.appendChild(refMount);
|
|
80
61
|
|
|
81
62
|
controlWrap.appendChild(wrap);
|
|
82
63
|
|
|
83
64
|
return {
|
|
84
|
-
inputEl:
|
|
85
|
-
activate,
|
|
65
|
+
inputEl: refField.inputEl,
|
|
66
|
+
activate: refField.activate,
|
|
86
67
|
readValue() {
|
|
87
68
|
const current = ui.params[key];
|
|
88
69
|
if (!current || typeof current !== 'object') {
|
|
@@ -5,6 +5,7 @@ export function renderReferenceSelectionField({ ui, key, def, id, controlWrap, v
|
|
|
5
5
|
inputEl.type = 'hidden';
|
|
6
6
|
inputEl.id = id;
|
|
7
7
|
try { inputEl.dataset.key = String(key); } catch (_) { }
|
|
8
|
+
try { inputEl.__refSelectionDef = def; } catch (_) { }
|
|
8
9
|
|
|
9
10
|
const isMulti = !!def.multiple;
|
|
10
11
|
if (isMulti) inputEl.dataset.multiple = 'true';
|
|
@@ -132,6 +133,15 @@ export function renderReferenceSelectionField({ ui, key, def, id, controlWrap, v
|
|
|
132
133
|
inputEl.value = initial ?? '';
|
|
133
134
|
|
|
134
135
|
valueWrap.addEventListener('click', () => ui._activateReferenceSelection(inputEl, def));
|
|
136
|
+
valueWrap.addEventListener('mouseenter', () => {
|
|
137
|
+
const normalized = normalizeReferenceName(inputEl.value || readRawValue());
|
|
138
|
+
if (normalized) {
|
|
139
|
+
try { ui._hoverReferenceSelectionItem?.(inputEl, def, normalized); } catch (_) { }
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
valueWrap.addEventListener('mouseleave', () => {
|
|
143
|
+
try { ui._clearReferenceSelectionHover?.(inputEl); } catch (_) { }
|
|
144
|
+
});
|
|
135
145
|
refWrap.appendChild(valueWrap);
|
|
136
146
|
|
|
137
147
|
inputEl.addEventListener('change', () => {
|