@glossarist/concept-browser 0.7.27 → 0.7.28

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.
@@ -2,30 +2,12 @@ import type { LocalizedConcept } from 'glossarist';
2
2
  import { ontology } from '../adapters/ontology-registry';
3
3
 
4
4
  export function entryStatusColor(status: string): string {
5
- const colors: Record<string, string> = {
6
- valid: 'badge-green',
7
- not_valid: 'bg-red-50 text-red-700',
8
- superseded: 'bg-red-50 text-red-700',
9
- retired: 'badge-gray',
10
- withdrawn: 'bg-red-100 text-red-800',
11
- draft: 'badge-yellow',
12
- Standard: 'badge-green',
13
- };
14
- return colors[status] ?? 'badge-gray';
5
+ return ontology.getColor('entryStatus', status) ?? 'badge-gray';
15
6
  }
16
7
 
17
8
  export function conceptStatusColor(status: string | null): string {
18
9
  if (!status) return 'badge-gray';
19
- const colors: Record<string, string> = {
20
- draft: 'badge-yellow',
21
- submitted: 'badge-blue',
22
- valid: 'badge-green',
23
- not_valid: 'bg-red-50 text-red-700',
24
- invalid: 'bg-red-50 text-red-700',
25
- superseded: 'bg-red-50 text-red-700',
26
- retired: 'badge-gray',
27
- };
28
- return colors[status] ?? 'badge-gray';
10
+ return ontology.getColor('conceptStatus', status) ?? 'badge-gray';
29
11
  }
30
12
 
31
13
  export function conceptStatusLabel(status: string | null): string {
@@ -8,45 +8,28 @@ export interface DesignationTypeInfo {
8
8
  definition?: string;
9
9
  }
10
10
 
11
- const TYPE_COLORS: Record<string, string> = {
12
- expression: 'bg-sky-50 text-sky-700',
13
- abbreviation: 'bg-amber-50 text-amber-700',
14
- symbol: 'bg-violet-50 text-violet-700',
15
- letter_symbol: 'bg-violet-50 text-violet-700',
16
- graphical_symbol: 'bg-violet-50 text-violet-700',
17
- };
18
-
19
11
  export function designationTypeInfo(designation: Designation): DesignationTypeInfo {
20
12
  const type = designation.type;
21
13
  const concept = ontology.getConcept('designationType', type);
22
14
  return {
23
15
  label: concept?.prefLabel ?? type,
24
- color: TYPE_COLORS[type] ?? 'bg-gray-50 text-gray-700',
16
+ color: ontology.getColor('designationType', type) ?? 'bg-gray-50 text-gray-700',
25
17
  definition: concept?.definition ?? undefined,
26
18
  };
27
19
  }
28
20
 
29
21
  export function normativeStatusInfo(status: string | null): { label: string; color: string; definition?: string } {
30
22
  if (!status) return { label: '', color: 'bg-gray-50 text-gray-700' };
31
-
32
- const colors: Record<string, string> = {
33
- preferred: 'bg-emerald-50 text-emerald-700',
34
- admitted: 'bg-amber-50 text-amber-700',
35
- deprecated: 'bg-red-50 text-red-700',
36
- superseded: 'bg-red-50 text-red-700',
37
- };
38
-
39
23
  const concept = ontology.getConcept('normativeStatus', status);
40
24
  return {
41
25
  label: concept?.prefLabel ?? status,
42
- color: colors[status] ?? 'bg-gray-50 text-gray-700',
26
+ color: ontology.getColor('normativeStatus', status) ?? 'bg-gray-50 text-gray-700',
43
27
  definition: concept?.definition ?? undefined,
44
28
  };
45
29
  }
46
30
 
47
31
  export function sourceStatusInfo(status: string | null): { label: string; color: string; definition?: string } {
48
32
  if (!status) return { label: '', color: 'badge-gray' };
49
-
50
33
  const concept = ontology.getConcept('sourceStatus', status);
51
34
  return {
52
35
  label: concept?.prefLabel ?? status,
@@ -57,16 +40,10 @@ export function sourceStatusInfo(status: string | null): { label: string; color:
57
40
 
58
41
  export function sourceTypeInfo(type: string | null): { label: string; color: string; definition?: string } {
59
42
  if (!type) return { label: '', color: 'badge-gray' };
60
-
61
- const colors: Record<string, string> = {
62
- authoritative: 'badge-purple',
63
- lineage: 'badge-blue',
64
- };
65
-
66
43
  const concept = ontology.getConcept('sourceType', type);
67
44
  return {
68
45
  label: concept?.prefLabel ?? type,
69
- color: colors[type] ?? 'badge-gray',
46
+ color: ontology.getColor('sourceType', type) ?? 'badge-gray',
70
47
  definition: concept?.definition ?? undefined,
71
48
  };
72
49
  }
@@ -1,4 +1,12 @@
1
+ /**
2
+ * Relationship categorization — fully derived from the ontology taxonomy.
3
+ *
4
+ * RELATIONSHIP_CATEGORIES, INVERSE_RELATIONSHIPS, and the category lookup
5
+ * map are all computed from `taxonomies.json` at module load time.
6
+ * Adding a new relationship type requires only a taxonomy edit — no code changes.
7
+ */
1
8
  import { ontology } from '../adapters/ontology-registry';
9
+ import type { TaxonomyCategory } from '../adapters/ontology-registry';
2
10
 
3
11
  export interface RelationshipCategory {
4
12
  id: string;
@@ -7,125 +15,64 @@ export interface RelationshipCategory {
7
15
  color: string;
8
16
  }
9
17
 
10
- export const RELATIONSHIP_CATEGORIES: RelationshipCategory[] = [
11
- {
12
- id: 'hierarchical',
13
- label: 'Hierarchy',
14
- types: ['broader', 'narrower', 'broader_generic', 'narrower_generic',
15
- 'broader_partitive', 'narrower_partitive', 'broader_instantial', 'narrower_instantial',
16
- 'has_concept', 'is_concept_of', 'instance_of', 'has_instance',
17
- 'has_part', 'is_part_of', 'inherits', 'inherited_by'],
18
- color: 'text-blue-600 bg-blue-50',
19
- },
20
- {
21
- id: 'mapping',
22
- label: 'Equivalence',
23
- types: ['equivalent', 'exact_match', 'close_match', 'broad_match', 'narrow_match', 'related_match'],
24
- color: 'text-emerald-600 bg-emerald-50',
25
- },
26
- {
27
- id: 'associative',
28
- label: 'Associative',
29
- types: ['see', 'related_concept', 'related_concept_broader', 'related_concept_narrower', 'references'],
30
- color: 'text-violet-600 bg-violet-50',
31
- },
32
- {
33
- id: 'lifecycle',
34
- label: 'Lifecycle',
35
- types: ['deprecates', 'deprecated_by', 'supersedes', 'superseded_by',
36
- 'replaces', 'replaced_by', 'invalidates', 'invalidated_by',
37
- 'retires', 'retired_by'],
38
- color: 'text-red-600 bg-red-50',
39
- },
40
- {
41
- id: 'comparative',
42
- label: 'Comparison',
43
- types: ['compare', 'contrast'],
44
- color: 'text-amber-600 bg-amber-50',
45
- },
46
- {
47
- id: 'definitional',
48
- label: 'Definitional',
49
- types: ['has_definition', 'definition_of', 'has_version', 'version_of',
50
- 'current_version', 'current_version_of'],
51
- color: 'text-cyan-600 bg-cyan-50',
52
- },
53
- {
54
- id: 'spatiotemporal',
55
- label: 'Spatiotemporal',
56
- types: ['sequentially_related', 'spatially_related', 'temporally_related'],
57
- color: 'text-teal-600 bg-teal-50',
58
- },
59
- {
60
- id: 'lexical',
61
- label: 'Lexical',
62
- types: ['homograph', 'false_friend'],
63
- color: 'text-pink-600 bg-pink-50',
64
- },
65
- {
66
- id: 'designation',
67
- label: 'Designation',
68
- types: ['abbreviated_form_for', 'short_form_for'],
69
- color: 'text-gray-600 bg-gray-50',
70
- },
71
- ];
72
-
73
-
74
-
75
- export const INVERSE_RELATIONSHIPS: Record<string, string> = {
76
- // Lifecycle (ISO 10241-1)
77
- supersedes: 'superseded_by',
78
- superseded_by: 'supersedes',
79
- deprecates: 'deprecated_by',
80
- deprecated_by: 'deprecates',
81
- replaces: 'replaced_by',
82
- replaced_by: 'replaces',
83
- invalidates: 'invalidated_by',
84
- invalidated_by: 'invalidates',
85
- retires: 'retired_by',
86
- retired_by: 'retires',
87
-
88
- // Hierarchical (generic — SKOS)
89
- broader: 'narrower',
90
- narrower: 'broader',
91
- broader_generic: 'narrower_generic',
92
- narrower_generic: 'broader_generic',
93
-
94
- // Hierarchical (partitive — ISO 25964)
95
- broader_partitive: 'narrower_partitive',
96
- narrower_partitive: 'broader_partitive',
97
- has_part: 'is_part_of',
98
- is_part_of: 'has_part',
99
-
100
- // Hierarchical (instantial — ISO 25964)
101
- broader_instantial: 'narrower_instantial',
102
- narrower_instantial: 'broader_instantial',
103
- instance_of: 'has_instance',
104
- has_instance: 'instance_of',
105
-
106
- // ISO 19135 concept-to-concept
107
- has_concept: 'is_concept_of',
108
- is_concept_of: 'has_concept',
109
- inherits: 'inherited_by',
110
- inherited_by: 'inherits',
111
- has_definition: 'definition_of',
112
- definition_of: 'has_definition',
113
-
114
- // ISO 19135 versioning
115
- has_version: 'version_of',
116
- version_of: 'has_version',
117
- current_version: 'current_version_of',
118
- current_version_of: 'current_version',
119
-
120
- // Symmetric (self-inverse)
121
- equivalent: 'equivalent',
122
- exact_match: 'exact_match',
123
- compare: 'compare',
124
- contrast: 'contrast',
125
- close_match: 'close_match',
126
- related_match: 'related_match',
127
- related_concept: 'related_concept',
128
- };
18
+ // ── Derived data ──────────────────────────────────────────────────────────
19
+
20
+ const CATEGORY_ORDER = [
21
+ 'hierarchical', 'mapping', 'associative', 'lifecycle',
22
+ 'comparative', 'definitional', 'spatiotemporal', 'lexical', 'designation',
23
+ ] as const;
24
+
25
+ function buildRelationshipData() {
26
+ const allTypes = ontology.getAll('relationshipType');
27
+ const categoryTypes = new Map<string, string[]>();
28
+ const inverseMap: Record<string, string> = {};
29
+
30
+ for (const concept of allTypes) {
31
+ if (concept.inverseOf) {
32
+ inverseMap[concept.id] = concept.inverseOf;
33
+ }
34
+
35
+ if (concept.category) {
36
+ const list = categoryTypes.get(concept.category) ?? [];
37
+ list.push(concept.id);
38
+ categoryTypes.set(concept.category, list);
39
+ }
40
+ }
41
+
42
+ const categories: RelationshipCategory[] = [];
43
+ for (const catId of CATEGORY_ORDER) {
44
+ const types = categoryTypes.get(catId);
45
+ if (!types) continue;
46
+ const config = ontology.getCategoryConfig('relationshipType', catId);
47
+ categories.push({
48
+ id: catId,
49
+ label: config?.label ?? catId,
50
+ types,
51
+ color: config?.color ?? 'text-gray-600 bg-gray-50',
52
+ });
53
+ }
54
+
55
+ // Add any categories not in the canonical order
56
+ for (const [catId, types] of categoryTypes) {
57
+ if (categories.some(c => c.id === catId)) continue;
58
+ const config = ontology.getCategoryConfig('relationshipType', catId);
59
+ categories.push({
60
+ id: catId,
61
+ label: config?.label ?? catId,
62
+ types,
63
+ color: config?.color ?? 'text-gray-600 bg-gray-50',
64
+ });
65
+ }
66
+
67
+ return { categories, inverseMap };
68
+ }
69
+
70
+ const { categories: RELATIONSHIP_CATEGORIES, inverseMap: INVERSE_RELATIONSHIPS } = buildRelationshipData();
71
+
72
+ export { RELATIONSHIP_CATEGORIES, INVERSE_RELATIONSHIPS };
73
+
74
+ // ── Lookup maps ───────────────────────────────────────────────────────────
75
+
129
76
  const CATEGORY_MAP = new Map<string, RelationshipCategory>();
130
77
  for (const cat of RELATIONSHIP_CATEGORIES) {
131
78
  for (const t of cat.types) {
@@ -133,16 +80,15 @@ for (const cat of RELATIONSHIP_CATEGORIES) {
133
80
  }
134
81
  }
135
82
 
83
+ // ── Public API ────────────────────────────────────────────────────────────
84
+
136
85
  export function categorizeRelationship(type: string): RelationshipCategory {
137
86
  return CATEGORY_MAP.get(type) ?? { id: 'other', label: 'Other', types: [type], color: 'text-gray-600 bg-gray-50' };
138
87
  }
139
88
 
140
89
  export function relationshipLabel(type: string): string {
141
- // Check the ontology taxonomy first (for glossarist-specific types)
142
90
  const concept = ontology.getConcept('relationshipType', type);
143
91
  if (concept?.prefLabel) return concept.prefLabel;
144
-
145
- // Fallback: humanize the type string
146
92
  return type.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
147
93
  }
148
94