@diagrammo/dgmo 0.5.1 → 0.5.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/d3.ts CHANGED
@@ -2817,7 +2817,8 @@ export function renderTimeline(
2817
2817
  exportDims?: D3ExportDimensions,
2818
2818
  activeTagGroup?: string | null,
2819
2819
  swimlaneTagGroup?: string | null,
2820
- onTagStateChange?: (activeTagGroup: string | null, swimlaneTagGroup: string | null) => void
2820
+ onTagStateChange?: (activeTagGroup: string | null, swimlaneTagGroup: string | null) => void,
2821
+ viewMode?: boolean
2821
2822
  ): void {
2822
2823
  d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
2823
2824
 
@@ -4151,8 +4152,9 @@ export function renderTimeline(
4151
4152
  };
4152
4153
  const legendGroups: LegendGroup[] = parsed.timelineTagGroups.map((g) => {
4153
4154
  const pillW = g.name.length * LG_PILL_FONT_W + LG_PILL_PAD;
4154
- // Expanded: pill + icon + entries
4155
- let entryX = LG_CAPSULE_PAD + pillW + LG_ICON_W + 4;
4155
+ // Expanded: pill + icon (unless viewMode) + entries
4156
+ const iconSpace = viewMode ? 8 : LG_ICON_W + 4;
4157
+ let entryX = LG_CAPSULE_PAD + pillW + iconSpace;
4156
4158
  for (const entry of g.entries) {
4157
4159
  const textX = entryX + LG_DOT_R * 2 + LG_ENTRY_DOT_GAP;
4158
4160
  entryX = textX + entry.value.length * LG_ENTRY_FONT_W + LG_ENTRY_TRAIL;
@@ -4204,7 +4206,7 @@ export function renderTimeline(
4204
4206
  function relayout() {
4205
4207
  renderTimeline(
4206
4208
  container, parsed, palette, isDark, onClickItem, exportDims,
4207
- currentActiveGroup, currentSwimlaneGroup, onTagStateChange
4209
+ currentActiveGroup, currentSwimlaneGroup, onTagStateChange, viewMode
4208
4210
  );
4209
4211
  }
4210
4212
 
@@ -4212,19 +4214,36 @@ export function renderTimeline(
4212
4214
  // Remove previous legend
4213
4215
  mainSvg.selectAll('.tl-tag-legend-group').remove();
4214
4216
 
4217
+ // Effective color source: explicit color group > swimlane group
4218
+ const effectiveColorKey = (currentActiveGroup ?? currentSwimlaneGroup)?.toLowerCase() ?? null;
4219
+
4220
+ // In view mode, only show the color-driving tag group (expanded, non-interactive).
4221
+ // Skip the swimlane group if it's separate from the color group (lane headers already label it).
4222
+ const visibleGroups = viewMode
4223
+ ? legendGroups.filter(
4224
+ (lg) =>
4225
+ effectiveColorKey != null &&
4226
+ lg.group.name.toLowerCase() === effectiveColorKey
4227
+ )
4228
+ : legendGroups;
4229
+
4230
+ if (visibleGroups.length === 0) return;
4231
+
4215
4232
  // Compute total width and center horizontally in SVG
4216
- const totalW = legendGroups.reduce((s, lg) => {
4217
- const isActive = currentActiveGroup != null &&
4218
- lg.group.name.toLowerCase() === currentActiveGroup.toLowerCase();
4233
+ const totalW = visibleGroups.reduce((s, lg) => {
4234
+ const isActive = viewMode ||
4235
+ (currentActiveGroup != null &&
4236
+ lg.group.name.toLowerCase() === currentActiveGroup.toLowerCase());
4219
4237
  return s + (isActive ? lg.expandedWidth : lg.minifiedWidth);
4220
- }, 0) + (legendGroups.length - 1) * LG_GROUP_GAP;
4238
+ }, 0) + (visibleGroups.length - 1) * LG_GROUP_GAP;
4221
4239
 
4222
4240
  let cx = (width - totalW) / 2;
4223
4241
 
4224
- for (const lg of legendGroups) {
4242
+ for (const lg of visibleGroups) {
4225
4243
  const groupKey = lg.group.name.toLowerCase();
4226
- const isActive = currentActiveGroup != null &&
4227
- currentActiveGroup.toLowerCase() === groupKey;
4244
+ const isActive = viewMode ||
4245
+ (currentActiveGroup != null &&
4246
+ currentActiveGroup.toLowerCase() === groupKey);
4228
4247
  const isSwimActive = currentSwimlaneGroup != null &&
4229
4248
  currentSwimlaneGroup.toLowerCase() === groupKey;
4230
4249
 
@@ -4237,14 +4256,18 @@ export function renderTimeline(
4237
4256
  .attr('class', 'tl-tag-legend-group tl-tag-legend-entry')
4238
4257
  .attr('data-legend-group', groupKey)
4239
4258
  .attr('data-tag-group', groupKey)
4240
- .attr('data-legend-entry', '__group__')
4241
- .style('cursor', 'pointer')
4242
- .on('click', () => {
4243
- currentActiveGroup = currentActiveGroup === groupKey ? null : groupKey;
4244
- drawLegend();
4245
- recolorEvents();
4246
- onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
4247
- });
4259
+ .attr('data-legend-entry', '__group__');
4260
+
4261
+ if (!viewMode) {
4262
+ gEl
4263
+ .style('cursor', 'pointer')
4264
+ .on('click', () => {
4265
+ currentActiveGroup = currentActiveGroup === groupKey ? null : groupKey;
4266
+ drawLegend();
4267
+ recolorEvents();
4268
+ onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
4269
+ });
4270
+ }
4248
4271
 
4249
4272
  // Outer capsule background (active only)
4250
4273
  if (isActive) {
@@ -4294,20 +4317,25 @@ export function renderTimeline(
4294
4317
 
4295
4318
  // Entries + swimlane icon inside capsule (active only)
4296
4319
  if (isActive) {
4297
- // Swimlane icon right after the pill label, with breathing room
4298
- const iconX = pillXOff + pillWidth + 5;
4299
- const iconY = (LG_HEIGHT - 10) / 2; // vertically centered
4300
- const iconEl = drawSwimlaneIcon(gEl, iconX, iconY, isSwimActive);
4301
- iconEl
4302
- .attr('data-swimlane-toggle', groupKey)
4303
- .on('click', (event: MouseEvent) => {
4304
- event.stopPropagation();
4305
- currentSwimlaneGroup = currentSwimlaneGroup === groupKey ? null : groupKey;
4306
- onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
4307
- relayout();
4308
- });
4320
+ // Swimlane icon (skip in view mode non-interactive)
4321
+ let entryX: number;
4322
+ if (!viewMode) {
4323
+ const iconX = pillXOff + pillWidth + 5;
4324
+ const iconY = (LG_HEIGHT - 10) / 2; // vertically centered
4325
+ const iconEl = drawSwimlaneIcon(gEl, iconX, iconY, isSwimActive);
4326
+ iconEl
4327
+ .attr('data-swimlane-toggle', groupKey)
4328
+ .on('click', (event: MouseEvent) => {
4329
+ event.stopPropagation();
4330
+ currentSwimlaneGroup = currentSwimlaneGroup === groupKey ? null : groupKey;
4331
+ onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
4332
+ relayout();
4333
+ });
4334
+ entryX = pillXOff + pillWidth + LG_ICON_W + 4;
4335
+ } else {
4336
+ entryX = pillXOff + pillWidth + 8;
4337
+ }
4309
4338
 
4310
- let entryX = pillXOff + pillWidth + LG_ICON_W + 4;
4311
4339
  for (const entry of lg.group.entries) {
4312
4340
  const tagKey = lg.group.name.toLowerCase();
4313
4341
  const tagVal = entry.value.toLowerCase();
@@ -4315,29 +4343,32 @@ export function renderTimeline(
4315
4343
  const entryG = gEl.append('g')
4316
4344
  .attr('class', 'tl-tag-legend-entry')
4317
4345
  .attr('data-tag-group', tagKey)
4318
- .attr('data-legend-entry', tagVal)
4319
- .style('cursor', 'pointer')
4320
- .on('mouseenter', (event: MouseEvent) => {
4321
- event.stopPropagation();
4322
- fadeToTagValue(mainG, tagKey, tagVal);
4323
- // Also fade legend entries on the SVG level
4324
- mainSvg.selectAll<SVGGElement, unknown>('.tl-tag-legend-entry').each(function () {
4325
- const el = d3Selection.select(this);
4326
- const ev = el.attr('data-legend-entry');
4327
- if (ev === '__group__') return;
4328
- const eg = el.attr('data-tag-group');
4329
- el.attr('opacity', eg === tagKey && ev === tagVal ? 1 : FADE_OPACITY);
4346
+ .attr('data-legend-entry', tagVal);
4347
+
4348
+ if (!viewMode) {
4349
+ entryG
4350
+ .style('cursor', 'pointer')
4351
+ .on('mouseenter', (event: MouseEvent) => {
4352
+ event.stopPropagation();
4353
+ fadeToTagValue(mainG, tagKey, tagVal);
4354
+ mainSvg.selectAll<SVGGElement, unknown>('.tl-tag-legend-entry').each(function () {
4355
+ const el = d3Selection.select(this);
4356
+ const ev = el.attr('data-legend-entry');
4357
+ if (ev === '__group__') return;
4358
+ const eg = el.attr('data-tag-group');
4359
+ el.attr('opacity', eg === tagKey && ev === tagVal ? 1 : FADE_OPACITY);
4360
+ });
4361
+ })
4362
+ .on('mouseleave', (event: MouseEvent) => {
4363
+ event.stopPropagation();
4364
+ fadeReset(mainG);
4365
+ mainSvg.selectAll<SVGGElement, unknown>('.tl-tag-legend-entry')
4366
+ .attr('opacity', 1);
4367
+ })
4368
+ .on('click', (event: MouseEvent) => {
4369
+ event.stopPropagation();
4330
4370
  });
4331
- })
4332
- .on('mouseleave', (event: MouseEvent) => {
4333
- event.stopPropagation();
4334
- fadeReset(mainG);
4335
- mainSvg.selectAll<SVGGElement, unknown>('.tl-tag-legend-entry')
4336
- .attr('opacity', 1);
4337
- })
4338
- .on('click', (event: MouseEvent) => {
4339
- event.stopPropagation(); // don't toggle group
4340
- });
4371
+ }
4341
4372
 
4342
4373
  entryG.append('circle')
4343
4374
  .attr('cx', entryX + LG_DOT_R)