diff --git a/src/ifctester/webapp/src/pages/Home/IdsViewer.svelte b/src/ifctester/webapp/src/pages/Home/IdsViewer.svelte index e69ca73f7a..beadd151b8 100644 --- a/src/ifctester/webapp/src/pages/Home/IdsViewer.svelte +++ b/src/ifctester/webapp/src/pages/Home/IdsViewer.svelte @@ -4,7 +4,7 @@ import { error, success } from "$src/modules/utils/toast.svelte"; import * as Tooltip from "$src/lib/components/ui/tooltip"; import type { AuditReport, AuditReportData } from "$src/types/report"; - import type { DocumentState, IdsDocument, Specification } from "$src/types/ids"; + import type { DocumentState, Facet, IdsDocument, Specification } from "$src/types/ids"; let activeDocument = $derived( IDS.Module.activeDocument ? (IDS.Module.documents[IDS.Module.activeDocument] as IdsDocument) : null @@ -122,6 +122,31 @@ return spec.requirements[reqIndex]; } + type RequirementGroup = { + facetType: string; + items: { facet: Facet; reqIndex: number }[]; + }; + + function getRequirementGroups(spec: Specification | undefined | null): RequirementGroup[] { + if (!spec?.requirements) return []; + + const groups: RequirementGroup[] = []; + let reqIndex = 0; + + for (const [facetType, facets] of Object.entries(spec.requirements)) { + if (!Array.isArray(facets) || facets.length === 0) continue; + groups.push({ + facetType, + items: facets.map((facet) => ({ + facet, + reqIndex: reqIndex++ + })) + }); + } + + return groups; + } + function toggleRequirementDetails(specIndex: number, reqIndex: number) { const key = `${specIndex}-${reqIndex}`; if (expandedRequirements.has(key)) { @@ -230,6 +255,7 @@ {#each activeDocument.specifications.specification as spec, index} {@const usage = getDocumentSpecificationUsage(spec)} + {@const requirementGroups = getRequirementGroups(spec)}
- {#if Array.isArray(spec.requirements) && spec.requirements.length > 0} + {#if requirementGroups.length > 0}

Requirements

- {#each Object.entries(spec.requirements || {}) as [facetType, facets]} - {#if Array.isArray(facets) && facets.length > 0} -
- {#each facets as facet, facetIndex} - {@const reqAuditData = auditReport ? getRequirementStatus(index, facetIndex, auditReport.data) : null} - {@const specStatus = auditReport ? getSpecificationStatus(index, auditReport.data) : null} -
- - {#if reqAuditData && isRequirementDetailsExpanded(index, facetIndex)} + {/if} + + {#if reqAuditData && isRequirementDetailsExpanded(index, item.reqIndex)}
{#if reqAuditData.passed_entities && reqAuditData.passed_entities.length > 0} @@ -623,11 +648,10 @@ {/if}
- {/if} -
- {/each} -
- {/if} + {/if} +
+ {/each} +
{/each}
diff --git a/src/ifctester/webapp/src/pages/Home/RestrictionEditor.svelte b/src/ifctester/webapp/src/pages/Home/RestrictionEditor.svelte index 513d9f18b1..c8942b2a52 100644 --- a/src/ifctester/webapp/src/pages/Home/RestrictionEditor.svelte +++ b/src/ifctester/webapp/src/pages/Home/RestrictionEditor.svelte @@ -48,6 +48,27 @@ const uniqueId = Math.random().toString(36).slice(2, 8); let baseId = $derived(`restriction-${fieldName}-${uniqueId}`); + + const activeDocument = $derived( + IDS.Module.activeDocument ? (IDS.Module.documents[IDS.Module.activeDocument] as IdsDocument) : null + ); + const documentState = $derived( + IDS.Module.activeDocument ? (IDS.Module.states[IDS.Module.activeDocument] as DocumentState) : null + ); + const activeSpecification = $derived( + activeDocument && documentState && documentState.activeSpecification !== null && activeDocument.specifications?.specification + ? (activeDocument.specifications.specification[documentState.activeSpecification] as Specification) + : null + ); + + const getFieldValue = () => (facet as Record)[fieldName] as FacetValue | string | undefined; + const setFieldValue = (value: FacetValue | string) => { + (facet as Record)[fieldName] = value; + }; + const getFacetValue = () => { + const value = getFieldValue(); + return typeof value === 'object' && value !== null ? (value as FacetValue) : undefined; + }; // Predefined Types autocompletions let predefinedTypeOptions: string[] = $state([]); @@ -60,17 +81,6 @@ // Get the active specification's IFC schemas const getIfcVersions = () => { - const activeDocument = IDS.Module.activeDocument - ? (IDS.Module.documents[IDS.Module.activeDocument] as IdsDocument) - : null; - const documentState = IDS.Module.activeDocument - ? (IDS.Module.states[IDS.Module.activeDocument] as DocumentState) - : null; - const activeSpecification = - activeDocument && documentState && documentState.activeSpecification !== null && activeDocument.specifications?.specification - ? (activeDocument.specifications.specification[documentState.activeSpecification] as Specification) - : null; - const versions = activeSpecification?.["@ifcVersion"] || ['IFC4']; // TODO: Fix: Filter out IFC4X3 because it's buggy return versions.filter(version => version !== 'IFC4X3_ADD2'); @@ -86,17 +96,6 @@ // Get all entity names from Entity facets in Applicability const getApplicabilityEntityNames = () => { - const activeDocument = IDS.Module.activeDocument - ? (IDS.Module.documents[IDS.Module.activeDocument] as IdsDocument) - : null; - const documentState = IDS.Module.activeDocument - ? (IDS.Module.states[IDS.Module.activeDocument] as DocumentState) - : null; - const activeSpecification = - activeDocument && documentState && documentState.activeSpecification !== null && activeDocument.specifications?.specification - ? (activeDocument.specifications.specification[documentState.activeSpecification] as Specification) - : null; - if (!activeSpecification?.applicability?.entity) return []; const entityFacets = activeSpecification.applicability.entity as Facet[]; @@ -122,17 +121,6 @@ // Get entity facets with their predefined types const getApplicabilityEntityFacets = () => { - const activeDocument = IDS.Module.activeDocument - ? (IDS.Module.documents[IDS.Module.activeDocument] as IdsDocument) - : null; - const documentState = IDS.Module.activeDocument - ? (IDS.Module.states[IDS.Module.activeDocument] as DocumentState) - : null; - const activeSpecification = - activeDocument && documentState && documentState.activeSpecification !== null && activeDocument.specifications?.specification - ? (activeDocument.specifications.specification[documentState.activeSpecification] as Specification) - : null; - if (!activeSpecification?.applicability?.entity) return []; return (activeSpecification.applicability.entity as Facet[]).map(entityFacet => { @@ -295,7 +283,7 @@ }; const getRestrictionType = () => { - const fieldValue = (facet as Record)[fieldName] as FacetValue | undefined; + const fieldValue = getFacetValue(); if (!fieldValue) return 'Simple'; if (fieldValue.simpleValue !== undefined) return 'Simple'; if (fieldValue['restriction']) { @@ -311,7 +299,7 @@ }; const getSimpleValue = (): string => { - const fieldValue = (facet as Record)[fieldName] as FacetValue | string | undefined; + const fieldValue = getFieldValue(); // For special properties (eg. @dataType), we return the value directly if (typeof fieldValue === 'string') return fieldValue; @@ -323,7 +311,7 @@ }; const getEnumerationValues = () => { - const fieldValue = (facet as Record)[fieldName] as FacetValue | undefined; + const fieldValue = getFacetValue(); if (!fieldValue?.['restriction']) return ['']; const restriction = fieldValue['restriction'] as Restriction; const enumValues = restriction['enumeration'] as RestrictionValue[] | undefined; @@ -332,7 +320,7 @@ }; const getPatternValue = () => { - const fieldValue = (facet as Record)[fieldName] as FacetValue | undefined; + const fieldValue = getFacetValue(); if (!fieldValue?.['restriction']) return ''; const restriction = fieldValue['restriction'] as Restriction; const pattern = restriction['pattern'] as RestrictionValue[] | undefined; @@ -341,7 +329,7 @@ }; const getRangeValues = () => { - const fieldValue = (facet as Record)[fieldName] as FacetValue | undefined; + const fieldValue = getFacetValue(); if (!fieldValue?.['restriction']) return { min: '', max: '', minType: 'Inclusive', maxType: 'Inclusive' }; const restriction = fieldValue['restriction'] as Restriction; @@ -367,7 +355,7 @@ }; const getLengthValue = () => { - const fieldValue = (facet as Record)[fieldName] as FacetValue | undefined; + const fieldValue = getFacetValue(); if (!fieldValue?.['restriction']) return ''; const restriction = fieldValue['restriction'] as Restriction; const length = restriction['length'] as RestrictionValue[] | undefined; @@ -376,7 +364,7 @@ }; const getLengthRangeValues = () => { - const fieldValue = (facet as Record)[fieldName] as FacetValue | undefined; + const fieldValue = getFacetValue(); if (!fieldValue?.['restriction']) return { min: '', max: '' }; const restriction = fieldValue['restriction'] as Restriction; @@ -395,29 +383,29 @@ const setSimpleValue = (value: string) => { // For special properties (eg. @dataType), we set the value directly if (isSpecialProp) { - (facet as Record)[fieldName] = value; + setFieldValue(value); return; } - (facet as Record)[fieldName] = { simpleValue: value }; + setFieldValue({ simpleValue: value }); }; const setEnumerationValues = (values: string[]) => { const enumItems = values.filter(v => v && typeof v === 'string' && v.trim() !== '').map(v => ({ '@value': v })); - (facet as Record)[fieldName] = { + setFieldValue({ 'restriction': { '@base': 'xs:string', 'enumeration': enumItems } - }; + }); }; const setPatternValue = (value: string) => { - (facet as Record)[fieldName] = { + setFieldValue({ 'restriction': { '@base': 'xs:string', 'pattern': [{ '@value': value }] } - }; + }); }; const setRangeValues = (min: string, max: string, minType: string, maxType: string) => { @@ -432,16 +420,16 @@ (restriction as Record)[maxKey] = [{ '@value': max }]; } - (facet as Record)[fieldName] = { 'restriction': restriction }; + setFieldValue({ 'restriction': restriction }); }; const setLengthValue = (value: string) => { - (facet as Record)[fieldName] = { + setFieldValue({ 'restriction': { '@base': 'xs:string', 'length': [{ '@value': value }] } - }; + }); }; const setLengthRangeValues = (min: string, max: string) => { @@ -450,13 +438,27 @@ if (min !== '') restriction['minLength'] = [{ '@value': min }]; if (max !== '') restriction['maxLength'] = [{ '@value': max }]; - (facet as Record)[fieldName] = { 'restriction': restriction }; + setFieldValue({ 'restriction': restriction }); }; - let restrictionType = $derived(getRestrictionType()); + let restrictionType = $state(getRestrictionType()); + let hasUserSelectedType = $state(false); + let lastFacetRef = $state(facet); let enumerationValues = $derived(getEnumerationValues()); + $effect(() => { + if (facet !== lastFacetRef) { + lastFacetRef = facet; + hasUserSelectedType = false; + } + const detected = getRestrictionType(); + if (!hasUserSelectedType || detected !== 'Simple' || restrictionType === 'Simple') { + restrictionType = detected; + } + }); + const handleTypeChange = (newType: string) => { + hasUserSelectedType = true; restrictionType = newType; switch (newType) { @@ -583,6 +585,7 @@ getPatternValue(), (v) => setPatternValue(v)} placeholder="Enter regex pattern (e.g., DT[0-9]{2})" aria-label={`${label} pattern`}> {:else if restrictionType === 'Range'} + {@const range = getRangeValues()}
@@ -590,13 +593,13 @@ class="form-input" type="text" id={`${baseId}-range-min`} - bind:value={() => getRangeValues().min, (v) => { const range = getRangeValues(); setRangeValues(v, range.max, range.minType, range.maxType); }} + bind:value={() => range.min, (v) => setRangeValues(v, range.max, range.minType, range.maxType)} placeholder="0" > getRangeValues().maxType, (v) => { const range = getRangeValues(); setRangeValues(range.min, range.max, range.minType, v); }} + bind:value={() => range.maxType, (v) => setRangeValues(range.min, range.max, range.minType, v)} > @@ -626,14 +629,15 @@ getLengthValue(), (v) => setLengthValue(v)} placeholder="Enter exact length" aria-label={`${label} length`}> {:else if restrictionType === 'Length Range'} + {@const lengthRange = getLengthRangeValues()}
- getLengthRangeValues().min, (v) => { const range = getLengthRangeValues(); setLengthRangeValues(v, range.max); }} placeholder="0"> + lengthRange.min, (v) => setLengthRangeValues(v, lengthRange.max)} placeholder="0">
- getLengthRangeValues().max, (v) => { const range = getLengthRangeValues(); setLengthRangeValues(range.min, v); }} placeholder="0"> + lengthRange.max, (v) => setLengthRangeValues(lengthRange.min, v)} placeholder="0">
{/if}