fix(linkml-viewer): 3D cube visualization bugs - prevent click-to-filter and parse JSON custodian_types
- Remove onFaceClick prop from CustodianTypeIndicator3D in class/slot/enum detail views to prevent accidental filtering when clicking decorative cubes (Bug 3) - Add parseCustodianTypesAnnotation() helper to handle JSON-stringified arrays like '["A"]' in YAML annotations, fixing Bug 2 where all 19 letters appeared on every cube - Legend bar retains onTypeClick for intentional filtering functionality
This commit is contained in:
parent
5e8a432ef0
commit
54869589d1
2 changed files with 37 additions and 26 deletions
|
|
@ -1018,14 +1018,43 @@ class LinkMLSchemaService {
|
|||
if (!annotations?.custodian_types) {
|
||||
return null;
|
||||
}
|
||||
// Handle both string array and comma-separated string formats
|
||||
const types = annotations.custodian_types;
|
||||
return this.parseCustodianTypesAnnotation(annotations.custodian_types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse custodian_types annotation value into string array
|
||||
* Handles multiple formats:
|
||||
* - Array: ["A", "L", "M"]
|
||||
* - JSON stringified array: '["A", "L"]' or '["A"]'
|
||||
* - Comma-separated string: "A, L, M"
|
||||
* - Single value: "A"
|
||||
*/
|
||||
private parseCustodianTypesAnnotation(types: unknown): string[] | null {
|
||||
// Already an array
|
||||
if (Array.isArray(types)) {
|
||||
return types as string[];
|
||||
}
|
||||
|
||||
if (typeof types === 'string') {
|
||||
return types.split(',').map((t: string) => t.trim());
|
||||
const trimmed = types.trim();
|
||||
|
||||
// Check if it's a JSON stringified array (starts with '[' and ends with ']')
|
||||
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed);
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed.map((t: unknown) => String(t).trim());
|
||||
}
|
||||
} catch {
|
||||
// JSON parse failed, fall through to comma-separated handling
|
||||
console.warn(`[LinkMLSchemaService] Failed to parse custodian_types JSON: ${trimmed}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Comma-separated string or single value
|
||||
return trimmed.split(',').map((t: string) => t.trim()).filter(t => t.length > 0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -1039,14 +1068,7 @@ class LinkMLSchemaService {
|
|||
if (!slot?.annotations?.custodian_types) {
|
||||
return null;
|
||||
}
|
||||
const types = slot.annotations.custodian_types;
|
||||
if (Array.isArray(types)) {
|
||||
return types as string[];
|
||||
}
|
||||
if (typeof types === 'string') {
|
||||
return types.split(',').map((t: string) => t.trim());
|
||||
}
|
||||
return null;
|
||||
return this.parseCustodianTypesAnnotation(slot.annotations.custodian_types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1061,12 +1083,7 @@ class LinkMLSchemaService {
|
|||
// Currently enum annotations might be on individual values
|
||||
if ((enumDef as unknown as { annotations?: Record<string, unknown> })?.annotations?.custodian_types) {
|
||||
const types = (enumDef as unknown as { annotations: Record<string, unknown> }).annotations.custodian_types;
|
||||
if (Array.isArray(types)) {
|
||||
return types as string[];
|
||||
}
|
||||
if (typeof types === 'string') {
|
||||
return types.split(',').map((t: string) => t.trim());
|
||||
}
|
||||
return this.parseCustodianTypesAnnotation(types);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1145,7 +1145,7 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
showTooltip={true}
|
||||
hoveredType={hoveredCustodianType}
|
||||
onTypeHover={setHoveredCustodianType}
|
||||
onFaceClick={handleCustodianTypeFilter}
|
||||
// Removed onFaceClick - cube should be decorative, not trigger filtering
|
||||
className="linkml-viewer__custodian-indicator"
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -1229,7 +1229,7 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
showTooltip={true}
|
||||
hoveredType={hoveredCustodianType}
|
||||
onTypeHover={setHoveredCustodianType}
|
||||
onFaceClick={handleCustodianTypeFilter}
|
||||
// Removed onFaceClick - cube should be decorative, not trigger filtering
|
||||
className="linkml-viewer__custodian-indicator"
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -1242,12 +1242,6 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
)
|
||||
)}
|
||||
</h4>
|
||||
{slot.slot_uri && (
|
||||
<div className="linkml-viewer__uri">
|
||||
<span className="linkml-viewer__label">{t('uri')}</span>
|
||||
<code>{slot.slot_uri}</code>
|
||||
</div>
|
||||
)}
|
||||
{slot.range && (
|
||||
<div className="linkml-viewer__range">
|
||||
<span className="linkml-viewer__label">{t('range')}</span>
|
||||
|
|
@ -1322,7 +1316,7 @@ const LinkMLViewerPage: React.FC = () => {
|
|||
showTooltip={true}
|
||||
hoveredType={hoveredCustodianType}
|
||||
onTypeHover={setHoveredCustodianType}
|
||||
onFaceClick={handleCustodianTypeFilter}
|
||||
// Removed onFaceClick - cube should be decorative, not trigger filtering
|
||||
className="linkml-viewer__custodian-indicator"
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Reference in a new issue