@liquidcars/atlas-layout 0.1.13 → 0.1.15

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 CHANGED
@@ -118,6 +118,10 @@ const resolved = compileAtlasYaml(yamlText);
118
118
 
119
119
  La API principal recibe un objeto JavaScript ya parseado. Se aceptan entidades anidadas mediante `children` o entidades planas con referencias `parent`; internamente ambas formas se normalizan al contrato plano. Las entradas de texto se manejan mediante los subpaths `/yaml` y `/build` durante el build.
120
120
 
121
+ El entrypoint principal exporta `ATLAS_LAYOUT_VERSION`, obtenido del manifest
122
+ del paquete cargado. Las aplicaciones anfitrionas pueden compararlo con la
123
+ versión declarada en sus propias dependencias.
124
+
121
125
  En Markdown se acepta front matter YAML, un bloque completo ` ```atlas ` o bloques separados ` ```atlas-model `, ` ```atlas-layout `, ` ```atlas-render ` y ` ```atlas-relations `. Por ejemplo:
122
126
 
123
127
  ````markdown
@@ -131,6 +135,26 @@ layout:
131
135
  ```
132
136
  ````
133
137
 
138
+ Las etiquetas de los elementos se colocan debajo por defecto. El modelo puede
139
+ cambiar esa política de forma heredable mediante `style.label.anchor`; una
140
+ entidad concreta puede sobrescribirla con la misma propiedad. Los títulos de
141
+ los contenedores conservan su colocación automática salvo que el contenedor
142
+ declare un anclaje explícito:
143
+
144
+ ```yaml
145
+ style:
146
+ label:
147
+ anchor: bottom
148
+ entities:
149
+ - id: source
150
+ geometry: sphere
151
+ - id: exceptional
152
+ geometry: box
153
+ style:
154
+ label:
155
+ anchor: top
156
+ ```
157
+
134
158
  Las relaciones admiten el modo visual canónico `directed`, `bidirectional` o `broken`:
135
159
 
136
160
  ```yaml
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liquidcars/atlas-layout",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Declarative layout compiler for LiquidCars Atlas models",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -5,7 +5,7 @@ The executable projection of this contract is packages/atlas-layout/src/spec.js.
5
5
 
6
6
  ## Root model
7
7
 
8
- A model may contain version, palette, theme, render, layout, entities and relations.
8
+ A model may contain version, palette, theme, style, render, layout, entities and relations.
9
9
  Entities may be declared at the root or nested under children. The compiler normalizes
10
10
  nested entities to a flat list with parent references.
11
11
 
@@ -189,7 +189,9 @@ in the information panel.
189
189
  ## Rendering
190
190
 
191
191
  palette maps tokens to CSS colours. style.color or c can reference a token or direct
192
- CSS colour. render can additionally set relationMode, selectionMode, toolbar,
192
+ CSS colour. At model level, `style.label.anchor` supplies the inherited item-label
193
+ position and defaults to `bottom`; an entity may override it. Container titles stay
194
+ automatic unless their entity declares an explicit label anchor. render can additionally set relationMode, selectionMode, toolbar,
193
195
  shellLabels (the text-label contrast outline), focus and camera. `render.focus`
194
196
  accepts `mode: all|connected`, `effect: dim|hide`, `layout: preserve|reflow`
195
197
  and an optional dim `opacity`. Connected focus retains the selection, its directly
package/src/index.js CHANGED
@@ -1,6 +1,15 @@
1
1
  import { ATLAS_LAYOUT_SPEC_V1 } from "./spec.js";
2
+ import atlasLayoutPackage from "../package.json" with { type: "json" };
3
+
4
+ // Consumers can compare this value with their declared dependency to verify
5
+ // the exact layout module that is executing.
6
+ export const ATLAS_LAYOUT_VERSION = atlasLayoutPackage.version;
2
7
 
3
8
  const DEFAULT_COLOR = "#b9d8eb";
9
+ const DEFAULT_LABEL_ANCHOR = "bottom";
10
+ const DEFAULT_MODEL_STYLE = Object.freeze({
11
+ label: Object.freeze({ anchor: DEFAULT_LABEL_ANCHOR })
12
+ });
4
13
 
5
14
  const DEFAULT_SIZES = Object.freeze({
6
15
  box: [2.5, 1.2, 2.2],
@@ -279,7 +288,7 @@ function resolveColor(value, inherited, palette, diagnostics, entityId) {
279
288
  return palette.default_color;
280
289
  }
281
290
 
282
- function resolveStyles(entities, sourceById, childrenById, palette, diagnostics) {
291
+ function resolveStyles(entities, sourceById, childrenById, palette, diagnostics, modelStyle) {
283
292
  const byId = new Map(entities.map(entity => [entity.id, entity]));
284
293
  const resolved = new Map();
285
294
 
@@ -291,12 +300,15 @@ function resolveStyles(entities, sourceById, childrenById, palette, diagnostics)
291
300
  style.color = resolveColor(style.color, parentStyle.color, palette, diagnostics, id);
292
301
  entity.style = style;
293
302
  entity.c = style.color;
303
+ const authoredLabelAnchor = source.style?.label?.anchor ?? source.labelAnchor ?? source.label_anchor;
304
+ if (authoredLabelAnchor != null) entity.labelAnchor = String(authoredLabelAnchor);
305
+ else if (!isContainerType(entity.type) && style.label?.anchor != null) entity.labelAnchor = String(style.label.anchor);
294
306
  if (style.radius != null && entity.radius == null) entity.radius = scalar(style.radius, 0);
295
307
  resolved.set(id, style);
296
308
  for (const childId of childrenById.get(id) || []) visit(childId, style);
297
309
  }
298
310
 
299
- for (const entity of entities) if (!entity.parent) visit(entity.id);
311
+ for (const entity of entities) if (!entity.parent) visit(entity.id, modelStyle);
300
312
  return resolved;
301
313
  }
302
314
 
@@ -1838,7 +1850,9 @@ function compileResolvedAtlasModel(input, options = {}) {
1838
1850
  if (!flat.entities.length) diagnostic(diagnostics, "fatal", "NO_VALID_ENTITIES", "The model does not contain any valid entities.");
1839
1851
  const validIds = new Set(flat.entities.map(entity => entity.id));
1840
1852
  const relations = normalizeRelations(source.relations, validIds, diagnostics);
1841
- resolveStyles(flat.entities, flat.sourceById, flat.childrenById, palette, diagnostics);
1853
+ const modelStyle = mergeStyle(DEFAULT_MODEL_STYLE, source.style || {});
1854
+ if (!Object.keys(modelStyle.edges || {}).length) delete modelStyle.edges;
1855
+ resolveStyles(flat.entities, flat.sourceById, flat.childrenById, palette, diagnostics, modelStyle);
1842
1856
  const byId = new Map(flat.entities.map(entity => [entity.id, entity]));
1843
1857
  const graphContext = { relations, byId };
1844
1858
  graphContext.graphOrderHints = globalGraphOrderHints(source, flat, relations, byId);
@@ -1901,6 +1915,7 @@ function compileResolvedAtlasModel(input, options = {}) {
1901
1915
  const output = {
1902
1916
  palette,
1903
1917
  theme: resolveTheme(source),
1918
+ style: modelStyle,
1904
1919
  entities: flat.entities.map(entity => {
1905
1920
  const copy = { ...entity };
1906
1921
  delete copy.style;
@@ -1955,4 +1970,4 @@ export function compileAtlasModelWithDiagnostics(input, options = {}) {
1955
1970
  return { model, diagnostics: model.diagnostics || [] };
1956
1971
  }
1957
1972
 
1958
- export const layoutDefaults = Object.freeze({ defaultColor: DEFAULT_COLOR, sizes: DEFAULT_SIZES, aspectFactors: ASPECT_FACTORS });
1973
+ export const layoutDefaults = Object.freeze({ defaultColor: DEFAULT_COLOR, labelAnchor: DEFAULT_LABEL_ANCHOR, sizes: DEFAULT_SIZES, aspectFactors: ASPECT_FACTORS });
package/src/spec.js CHANGED
@@ -17,6 +17,7 @@ export const ATLAS_LAYOUT_SPEC_V1 = freeze({
17
17
  palette: field("Named model colours.", null, { context: "palette" }),
18
18
  theme: field("Renderer theme configuration.", null, { context: "theme" }),
19
19
  render: field("Renderer configuration.", null, { context: "render" }),
20
+ style: field("Inherited model appearance defaults.", null, { context: "style" }),
20
21
  layout: field("Assisted layout for root entities.", null, { context: "layout" }),
21
22
  constraints: field("Equal-size and edge-alignment rules for root siblings.", null, { context: "constraint", sequence: true }),
22
23
  entities: field("Top-level entities and containers.", null, { context: "entity", sequence: true }),