minerva-design 1.18.0 → 1.19.0

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/custom-elements.json +10 -6
  3. package/dist/angular/fesm2022/minerva-angular.mjs +147 -46
  4. package/dist/angular/fesm2022/minerva-angular.mjs.map +1 -1
  5. package/dist/angular/types/minerva-angular.d.ts +32 -20
  6. package/dist/charts/core.cjs +1 -1
  7. package/dist/charts/core.d.cts +1 -0
  8. package/dist/charts/core.d.ts +1 -0
  9. package/dist/charts/core.js +22 -21
  10. package/dist/charts/engine/chart.cjs +1 -1
  11. package/dist/charts/engine/chart.cjs.map +1 -1
  12. package/dist/charts/engine/chart.js +270 -240
  13. package/dist/charts/engine/chart.js.map +1 -1
  14. package/dist/charts/engine/overlay.cjs +1 -1
  15. package/dist/charts/engine/overlay.cjs.map +1 -1
  16. package/dist/charts/engine/overlay.js +23 -16
  17. package/dist/charts/engine/overlay.js.map +1 -1
  18. package/dist/charts/engine/quadrant-card.cjs +2 -0
  19. package/dist/charts/engine/quadrant-card.cjs.map +1 -0
  20. package/dist/charts/engine/quadrant-card.d.cts +83 -0
  21. package/dist/charts/engine/quadrant-card.d.ts +83 -0
  22. package/dist/charts/engine/quadrant-card.js +154 -0
  23. package/dist/charts/engine/quadrant-card.js.map +1 -0
  24. package/dist/charts/index.cjs +1 -1
  25. package/dist/charts/index.js +28 -27
  26. package/dist/charts/react/charts.cjs +1 -1
  27. package/dist/charts/react/charts.cjs.map +1 -1
  28. package/dist/charts/react/charts.js +378 -463
  29. package/dist/charts/react/charts.js.map +1 -1
  30. package/dist/core/contracts.cjs +1 -1
  31. package/dist/core/contracts.cjs.map +1 -1
  32. package/dist/core/contracts.js +1 -1
  33. package/dist/core/contracts.js.map +1 -1
  34. package/dist/native/charts/src/engine/overlay.cjs +1 -1
  35. package/dist/native/charts/src/engine/overlay.cjs.map +1 -1
  36. package/dist/native/charts/src/engine/overlay.js +23 -16
  37. package/dist/native/charts/src/engine/overlay.js.map +1 -1
  38. package/dist/taro/index.js +23 -16
  39. package/dist/uni/manifest.d.ts +34 -34
  40. package/dist/uni/manifest.js +34 -39
  41. package/dist/vue/components/Chart/QuadrantChart.vue.d.ts +4 -3
  42. package/dist/vue/components/Chart/QuadrantChart.vue.js.map +1 -1
  43. package/dist/vue/components/Chart/QuadrantChart.vue_vue_type_script_setup_true_lang.js +186 -142
  44. package/dist/vue/components/Chart/QuadrantChart.vue_vue_type_script_setup_true_lang.js.map +1 -1
  45. package/dist/vue/components/Chart/cartesian.d.ts +5 -1
  46. package/dist/vue/components/Chart/cartesian.js +2 -0
  47. package/dist/vue/components/Chart/cartesian.js.map +1 -1
  48. package/dist/vue/components/QuadrantTaskBoard/QuadrantTaskBoard.vue.js.map +1 -1
  49. package/dist/vue/components/QuadrantTaskBoard/QuadrantTaskBoard.vue_vue_type_script_setup_true_lang.js +4 -4
  50. package/dist/vue/components/QuadrantTaskBoard/QuadrantTaskBoard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  51. package/dist/weapp/charts/canvas.js +13 -4
  52. package/dist/web-components/components/chart/chart.d.ts +4 -1
  53. package/dist/web-components/components/chart/chart.js +3 -1
  54. package/dist/web-components/components/chart/chart.js.map +1 -1
  55. package/dist/web-components/components/chart/charts.d.ts +34 -14
  56. package/dist/web-components/components/chart/charts.js +145 -98
  57. package/dist/web-components/components/chart/charts.js.map +1 -1
  58. package/dist/web-components/components/quadrant-task-board/quadrant-task-board.d.ts +8 -8
  59. package/dist/web-components/components/quadrant-task-board/quadrant-task-board.js +9 -5
  60. package/dist/web-components/components/quadrant-task-board/quadrant-task-board.js.map +1 -1
  61. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # minerva-design
2
2
 
3
+ ## 1.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 734afb1: Quadrant charts: one shared point card on every renderer, and no Playwright.
8
+
9
+ - Fix: after a click, the card of the clicked point no longer stays on the chart and hides the others. A plain chart rings its selected point; only boards (with point actions) keep the card of the selected point. Escape closes the card, and a click beside the points drops a selection the chart made itself.
10
+ - The hovered point is kept while the parent passes new arrays of the same points, and through new data under a still pointer (e.g. the selection ring added by a click).
11
+ - Faster hover: the card is DOM of its own, so hovering renders no React, Vue or Angular component, and showing or hiding a drawn label redraws only the foreground canvas.
12
+ - New in `minerva-design/charts/core`: `createQuadrantPointCard`, `withQuadrantCard` and `QUADRANT_CARD_LINGER_MS`. Vue, Web Components and Angular quadrant charts now show the same card as React (no engine tooltip), with the point actions inside it; `<minerva-quadrant-chart>` adds the `point-card` part.
13
+ - Crowded cells: a label also tries beside its dot half a line up or down before it is skipped.
14
+ - Tests: the browser suites are replaced by Vitest tests with happy-dom.
15
+
3
16
  ## 1.18.0
4
17
 
5
18
  ### Minor Changes
@@ -7635,7 +7635,7 @@
7635
7635
  },
7636
7636
  {
7637
7637
  "kind": "class",
7638
- "description": "Four-quadrant matrix (priority matrix, Eisenhower, BCG...) of\n`minerva-design/charts/core` (the `<QuadrantChart>` of React): tinted\nquadrants with titles, dividers and labelled dots placed without\noverlapping labels. With `drag-points`, dots can be dragged to another\nquadrant (`minerva-point-drop`; update `points` to move them).\n\nA visually hidden list of the dots per quadrant lets screen readers and\nkeyboards reach every dot: arrows move between them, Enter clicks\n(`minerva-point-click`) and, with `drag-points`, Shift + arrows move the\nfocused dot to the neighbouring quadrant (`minerva-point-drop`, announced\nin a live region). The focused dot is drawn with a focus ring and\nreported by `minerva-hover`.\n\nSlotted content is placed over the plot: a header per quadrant (in its\nouter corner, replacing the drawn title, badge, description and count)\nand actions next to the active dot (hovered, keyboard-focused, else\n`selected-index`; read it from `activeIndex`). Both receive pointer\nevents; the rest of the plot stays interactive.\n\nPublished as the optional entry `minerva-design/web-components/chart`.",
7638
+ "description": "Four-quadrant matrix (priority matrix, Eisenhower, BCG...) of\n`minerva-design/charts/core` (the `<QuadrantChart>` of React): tinted\nquadrants with titles, dividers and labelled dots placed without\noverlapping labels. With `drag-points`, dots can be dragged to another\nquadrant (`minerva-point-drop`; update `points` to move them).\n\nA visually hidden list of the dots per quadrant lets screen readers and\nkeyboards reach every dot: arrows move between them, Enter clicks\n(`minerva-point-click`) and, with `drag-points`, Shift + arrows move the\nfocused dot to the neighbouring quadrant (`minerva-point-drop`, announced\nin a live region). The focused dot is drawn with a focus ring and\nreported by `minerva-hover`.\n\nOne glass point card names the hovered, keyboard-focused or dragged dot\n(its name and quadrant) in place of the engine tooltip; with slotted\n`point-actions`, the card also shows them and stays on the\n`selected-index` dot (read the dot they act on from `activeIndex`).\n\nSlotted content is placed over the plot: a header per quadrant (in its\nouter corner, replacing the drawn title, badge, description and count)\nand the actions in the point card. Both receive pointer events; the rest\nof the plot stays interactive.\n\nPublished as the optional entry `minerva-design/web-components/chart`.",
7639
7639
  "name": "MinervaQuadrantChart",
7640
7640
  "cssParts": [
7641
7641
  {
@@ -7647,7 +7647,7 @@
7647
7647
  "name": "surface"
7648
7648
  },
7649
7649
  {
7650
- "description": "The box over the plot holding the slotted headers and actions",
7650
+ "description": "The box over the plot holding the slotted headers and the point card",
7651
7651
  "name": "overlay"
7652
7652
  },
7653
7653
  {
@@ -7655,7 +7655,11 @@
7655
7655
  "name": "quadrant-header"
7656
7656
  },
7657
7657
  {
7658
- "description": "The zero-size box at the active dot holding the `point-actions` slot",
7658
+ "description": "The glass card of the hovered, focused, dragged (or, with actions, selected) dot",
7659
+ "name": "point-card"
7660
+ },
7661
+ {
7662
+ "description": "The actions box in the point card, holding the `point-actions` slot",
7659
7663
  "name": "point-actions"
7660
7664
  }
7661
7665
  ],
@@ -7677,7 +7681,7 @@
7677
7681
  "name": "quadrant-header-bottom-right"
7678
7682
  },
7679
7683
  {
7680
- "description": "Content shown at the active dot (e.g. edit / delete buttons), positioned by the slotted element",
7684
+ "description": "Actions shown in the point card of the active dot, beside its name (e.g. compact edit / delete buttons); the card then also stays on the selected dot",
7681
7685
  "name": "point-actions"
7682
7686
  }
7683
7687
  ],
@@ -7859,7 +7863,7 @@
7859
7863
  "type": {
7860
7864
  "text": "number | null"
7861
7865
  },
7862
- "description": "Index of the active dot, the one showing the `point-actions` slot: the\nhovered dot, else the keyboard-focused one, else `selectedIndex`\n(`null`: none)",
7866
+ "description": "Index of the active dot, the one whose point card shows the\n`point-actions` slot (hovered, keyboard-focused, else `selectedIndex`\nwhen actions are slotted); `null` while no card is shown or during a\ndrag",
7863
7867
  "readonly": true
7864
7868
  },
7865
7869
  {
@@ -31186,7 +31190,7 @@
31186
31190
  "declarations": [
31187
31191
  {
31188
31192
  "kind": "class",
31189
- "description": "Task board on a four-quadrant matrix (`<QuadrantTaskBoard>` of React),\nbuilt on `<minerva-quadrant-chart>`: each quadrant header shows its\ntitle, the number of tasks and an add button; the active task (hovered,\nkeyboard-focused or selected) shows edit and delete buttons; an inline\npanel, anchored inside the board at the button or the task, names a task\nand picks its quadrant, or confirms a delete (Escape or a press outside\ncloses it). Tasks can be dragged to another quadrant, or moved with\nShift + arrows once focused; a double click edits a task, a click on an\nempty area adds one there.\n\nControlled, like `<minerva-dashboard-columns>`: the board never changes its\nown `points`. It fires `minerva-points-change` with the next list after\nan add, an edit, a delete, a drag and drop or a keyboard move; set it\nback as `points` to apply it (or keep the old list to refuse it).\n\nTexts are localized (the `locale` attribute, else the library language)\nand can be overridden with `labels`. The host's `aria-label` names the\nchart.\n\nPublished as the optional entry\n`minerva-design/web-components/quadrant-task-board` (it loads the chart\nengine, minerva-design/charts/core).",
31193
+ "description": "Task board on a four-quadrant matrix (`<QuadrantTaskBoard>` of React),\nbuilt on `<minerva-quadrant-chart>`: each quadrant header shows its\ntitle, the number of tasks and an add button; the point card of the\nactive task (hovered, keyboard-focused or selected) shows edit and delete\nbuttons beside its name; an inline panel, anchored inside the board at\nthe button or the task, names a task and picks its quadrant, or confirms\na delete (Escape or a press outside closes it). Tasks can be dragged to\nanother quadrant, or moved with Shift + arrows once focused; a double\nclick edits a task, a click on an empty area adds one there.\n\nControlled, like `<minerva-dashboard-columns>`: the board never changes its\nown `points`. It fires `minerva-points-change` with the next list after\nan add, an edit, a delete, a drag and drop or a keyboard move; set it\nback as `points` to apply it (or keep the old list to refuse it).\n\nTexts are localized (the `locale` attribute, else the library language)\nand can be overridden with `labels`. The host's `aria-label` names the\nchart.\n\nPublished as the optional entry\n`minerva-design/web-components/quadrant-task-board` (it loads the chart\nengine, minerva-design/charts/core).",
31190
31194
  "name": "MinervaQuadrantTaskBoard",
31191
31195
  "cssParts": [
31192
31196
  {
@@ -5,7 +5,7 @@ import { CODE_MARK_COLORS, DEFAULT_LANGUAGE, DEFAULT_PLAYBACK_RATES, DEFAULT_SKI
5
5
  import { FOCUS_SCOPE_UNMOUNT_EVENT, PALETTE_COOKIE_NAME, THEME_COOKIE_NAME, applyDesignAttributes, applyPosition, autoPosition, bindMediaCaptions, centerInScroller, clickedCodeMark, createDashboardDrag, createDismissableLayer, createFocusScope, dashboardDataAdapter, focusElement, generateCSSVariables, getCodeSelectionRange, getDirection, getSystemTheme, getTabbables, hideOthers, isBilingualTheme, isInteractiveEventTarget, lockScroll, logicalArrowKey, nearestInRow, parsePaletteCookie, parsePlacement, parseThemeCookie, primaryColorCss, primaryColorTheme, readCookieValue, resolveTheme, serializeThemeCookie, waitForExitAnimation } from "../../dom/index.js";
6
6
  import { FormGroupDirective, NG_VALUE_ACCESSOR, NgControl, NgForm } from "@angular/forms";
7
7
  import { DomSanitizer } from "@angular/platform-browser";
8
- import { QUADRANT_CORNERS, QUADRANT_TASK_ICONS, applyQuadrantTaskDraft, boxplotData, bubbleSeries, canSaveQuadrantTask, createChart, createChart3D, createDepthChart, createGraphChart, createOrderBook, createOrderBookHeatmap, createQuadrantPointLayer, createShapeChart, createTimeAndSales, createTradingChart, editQuadrantTaskDraft, ganttSeries, heatmapChartOptions, histogramSeries, moveQuadrantTask, newQuadrantTaskDraft, placeQuadrantTaskPanel, quadrantChartOptions, quadrantCountText, quadrantGroups, quadrantLabels, quadrantOf, quadrantTaskColor, quadrantTaskDeleteText, quadrantTaskLabels, removeQuadrantTask, selectionAfterRemove, sparklineOptions, waterfallSeries } from "../../charts/core.js";
8
+ import { QUADRANT_CORNERS, QUADRANT_TASK_ICONS, applyQuadrantTaskDraft, boxplotData, bubbleSeries, canSaveQuadrantTask, createChart, createChart3D, createDepthChart, createGraphChart, createOrderBook, createOrderBookHeatmap, createQuadrantPointCard, createQuadrantPointLayer, createShapeChart, createTimeAndSales, createTradingChart, editQuadrantTaskDraft, ganttSeries, heatmapChartOptions, histogramSeries, moveQuadrantTask, newQuadrantTaskDraft, placeQuadrantTaskPanel, quadrantChartOptions, quadrantCountText, quadrantGroups, quadrantLabels, quadrantOf, quadrantTaskColor, quadrantTaskDeleteText, quadrantTaskLabels, removeQuadrantTask, selectionAfterRemove, sparklineOptions, waterfallSeries, withQuadrantCard } from "../../charts/core.js";
9
9
  import { createIconCanvas, getIcon } from "../../icons/core.js";
10
10
  const injectIsBrowser = () => isPlatformBrowser(inject(PLATFORM_ID));
11
11
  const staticInputs = (config = {}) => ({
@@ -39634,15 +39634,22 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39634
39634
  this.pointDrop = output();
39635
39635
  this.lastClick = null;
39636
39636
  this.focusedIndex = signal(null, ...ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []);
39637
- this.hoveredIndex = signal(null, ...ngDevMode ? [{ debugName: "hoveredIndex" }] : /* istanbul ignore next */ []);
39638
39637
  this.plot = signal(null, ...ngDevMode ? [{ debugName: "plot" }] : /* istanbul ignore next */ []);
39639
39638
  this.pointsHost = viewChild.required("points", ...ngDevMode ? [{ debugName: "pointsHost" }] : /* istanbul ignore next */ []);
39639
+ this.cardHost = viewChild("cardHost", ...ngDevMode ? [{ debugName: "cardHost" }] : /* istanbul ignore next */ []);
39640
+ this.viewContainer = inject(ViewContainerRef);
39641
+ this.card = null;
39642
+ this.actionsView = null;
39640
39643
  this.destroyed = false;
39641
39644
  this.overlayStyle = computed(() => {
39642
39645
  const rect = this.plot();
39643
- if (!rect || !(this.quadrantHeader() || this.pointActions())) return null;
39646
+ if (!rect) return null;
39644
39647
  return `position:absolute;left:${rect.x}px;top:${rect.y}px;width:${rect.width}px;height:${rect.height}px;pointer-events:none`;
39645
39648
  }, ...ngDevMode ? [{ debugName: "overlayStyle" }] : /* istanbul ignore next */ []);
39649
+ this.cardHostStyle = computed(() => {
39650
+ const rect = this.plot();
39651
+ return `position:absolute;left:0;top:0;width:${rect?.width ?? 0}px;height:${rect?.height ?? 0}px;pointer-events:none`;
39652
+ }, ...ngDevMode ? [{ debugName: "cardHostStyle" }] : /* istanbul ignore next */ []);
39646
39653
  this.headers = computed(() => {
39647
39654
  const rect = this.plot() ?? {
39648
39655
  width: 0,
@@ -39679,20 +39686,6 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39679
39686
  };
39680
39687
  });
39681
39688
  }, ...ngDevMode ? [{ debugName: "headers" }] : /* istanbul ignore next */ []);
39682
- this.activePoint = computed(() => {
39683
- const index = this.hoveredIndex() ?? this.focusedIndex() ?? this.selectedIndex() ?? null;
39684
- const point = index === null ? void 0 : this.points()[index];
39685
- if (index === null || !point || !Number.isFinite(point.x) || !Number.isFinite(point.y)) return [];
39686
- const at = this.visual(point.x, point.y);
39687
- return [{
39688
- context: {
39689
- $implicit: point,
39690
- point,
39691
- index
39692
- },
39693
- style: `position:absolute;left:${100 * at.x}%;top:${100 * at.y}%;width:0;height:0;pointer-events:none`
39694
- }];
39695
- }, ...ngDevMode ? [{ debugName: "activePoint" }] : /* istanbul ignore next */ []);
39696
39689
  const injector = inject(Injector);
39697
39690
  let layer = null;
39698
39691
  afterNextRender(() => {
@@ -39709,12 +39702,91 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39709
39702
  });
39710
39703
  }, { injector });
39711
39704
  });
39705
+ let cardHost = null;
39706
+ afterRenderEffect(() => {
39707
+ const host = this.cardHost()?.nativeElement ?? null;
39708
+ const options = this.cardOptions(this.instance());
39709
+ const focused = this.accessiblePoints() ? this.focusedIndex() : null;
39710
+ this.plot();
39711
+ untracked(() => {
39712
+ if (this.card && cardHost !== host) this.destroyCard();
39713
+ if (!host || !options.chart) return;
39714
+ if (this.card) this.card.update(options);
39715
+ else {
39716
+ cardHost = host;
39717
+ this.card = createQuadrantPointCard(host, options);
39718
+ }
39719
+ this.card.focus(focused);
39720
+ this.renderActions(this.card.index);
39721
+ });
39722
+ });
39712
39723
  inject(DestroyRef).onDestroy(() => {
39713
39724
  this.destroyed = true;
39714
39725
  layer?.destroy();
39715
39726
  layer = null;
39727
+ this.destroyCard();
39716
39728
  });
39717
39729
  }
39730
+ cardOptions(chart) {
39731
+ const actions = !!this.pointActions();
39732
+ return {
39733
+ config: {
39734
+ ...this.geometry(),
39735
+ points: this.points(),
39736
+ quadrants: this.quadrants(),
39737
+ xLabel: this.xLabel(),
39738
+ yLabel: this.yLabel(),
39739
+ name: this.name()
39740
+ },
39741
+ locale: this.locale(),
39742
+ pointSize: this.pointSize(),
39743
+ hover: this.tooltip() !== false,
39744
+ selectedIndex: this.selectedIndex(),
39745
+ showSelected: actions,
39746
+ hasActions: () => actions,
39747
+ chart,
39748
+ onClick: (index) => this.clickPoint(index),
39749
+ onChange: (index) => this.renderActions(index)
39750
+ };
39751
+ }
39752
+ renderActions(index) {
39753
+ const card = this.card;
39754
+ if (!card) return;
39755
+ const tpl = untracked(this.pointActions);
39756
+ const point = index === null ? void 0 : untracked(this.points)[index];
39757
+ const current = this.actionsView;
39758
+ if (current && (!point || current.tpl !== tpl || current.ref.context.index !== index)) {
39759
+ if (!current.ref.destroyed) current.ref.destroy();
39760
+ this.actionsView = null;
39761
+ }
39762
+ if (!tpl || !point || index === null) return;
39763
+ let ref = this.actionsView?.ref;
39764
+ if (ref) {
39765
+ if (ref.context.point === point) return;
39766
+ ref.context.$implicit = point;
39767
+ ref.context.point = point;
39768
+ } else {
39769
+ ref = this.viewContainer.createEmbeddedView(tpl, {
39770
+ $implicit: point,
39771
+ point,
39772
+ index
39773
+ });
39774
+ card.actions.append(...ref.rootNodes);
39775
+ this.actionsView = {
39776
+ tpl,
39777
+ ref
39778
+ };
39779
+ }
39780
+ ref.detectChanges();
39781
+ card.place();
39782
+ }
39783
+ destroyCard() {
39784
+ const view = this.actionsView?.ref;
39785
+ if (view && !view.destroyed) view.destroy();
39786
+ this.actionsView = null;
39787
+ this.card?.destroy();
39788
+ this.card = null;
39789
+ }
39718
39790
  layerOptions() {
39719
39791
  const selectedIndex = this.selectedIndex();
39720
39792
  return {
@@ -39732,6 +39804,7 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39732
39804
  onFocusChange: (index, hover) => {
39733
39805
  if (this.destroyed) return;
39734
39806
  this.focusedIndex.set(index);
39807
+ this.card?.focus(index);
39735
39808
  this.hover.emit(hover);
39736
39809
  },
39737
39810
  onClick: (index) => {
@@ -39762,7 +39835,7 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39762
39835
  }
39763
39836
  engineOptions() {
39764
39837
  const headers = !!this.quadrantHeader();
39765
- const options = quadrantChartOptions({
39838
+ const options = withQuadrantCard(quadrantChartOptions({
39766
39839
  ...this.geometry(),
39767
39840
  points: this.points(),
39768
39841
  quadrants: this.quadrants(),
@@ -39777,7 +39850,7 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39777
39850
  showCounts: headers ? void 0 : this.showCounts(),
39778
39851
  selectedIndex: this.selectedIndex(),
39779
39852
  selectedIndices: this.selectedIndices()
39780
- }, this.commonOptions(), { focusedIndex: this.accessiblePoints() ? this.focusedIndex() : null });
39853
+ }, this.commonOptions(), { focusedIndex: this.accessiblePoints() ? this.focusedIndex() : null }));
39781
39854
  if (headers) options.annotations = options.annotations?.map((a, i) => i < 4 && a.type === "rect" ? {
39782
39855
  ...a,
39783
39856
  label: void 0,
@@ -39790,14 +39863,22 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39790
39863
  bindEngine(chart) {
39791
39864
  return [
39792
39865
  ...super.bindEngine(chart),
39793
- chart.on("hover", (event) => this.hoveredIndex.set(event?.items?.[0]?.index ?? null)),
39794
- chart.on("layout", (rect) => this.plot.set({ ...rect }))
39866
+ chart.on("hover", (event) => this.card?.hover(event?.items?.[0]?.index ?? null)),
39867
+ chart.on("layout", (rect) => this.plot.set({ ...rect })),
39868
+ chart.on("dragStart", (event) => this.card?.drag(event.item.index, {
39869
+ x: event.x,
39870
+ y: event.y
39871
+ })),
39872
+ chart.on("dragEnd", () => this.card?.drag(null))
39795
39873
  ];
39796
39874
  }
39797
39875
  clicked(event) {
39798
39876
  const index = event.items[0]?.index;
39799
- const point = index === void 0 ? void 0 : this.points()[index];
39800
- if (!point || index === void 0) return;
39877
+ if (index !== void 0) this.clickPoint(index);
39878
+ }
39879
+ clickPoint(index) {
39880
+ const point = this.points()[index];
39881
+ if (!point) return;
39801
39882
  const now = Date.now();
39802
39883
  const last = this.lastClick;
39803
39884
  const double = last?.index === index && now - last.time < DOUBLE_CLICK_MS;
@@ -39834,6 +39915,10 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
39834
39915
  };
39835
39916
  }
39836
39917
  dragged(event) {
39918
+ this.card?.move({
39919
+ x: event.x,
39920
+ y: event.y
39921
+ });
39837
39922
  const target = this.dropAt(event);
39838
39923
  if (target) this.pointDrag.emit(target);
39839
39924
  }
@@ -40020,10 +40105,16 @@ var MnQuadrantChart = class MnQuadrantChart extends MnCartesianChartBase {
40020
40105
  predicate: ["points"],
40021
40106
  descendants: true,
40022
40107
  isSignal: true
40108
+ }, {
40109
+ propertyName: "cardHost",
40110
+ first: true,
40111
+ predicate: ["cardHost"],
40112
+ descendants: true,
40113
+ isSignal: true
40023
40114
  }],
40024
40115
  usesInheritance: true,
40025
40116
  ngImport: i0,
40026
- template: "<div #surface style=\"position:absolute;inset:0\"></div>\n @if (overlayStyle(); as style) {\n <div class=\"minerva-chart-overlay\" [style]=\"style\">\n @if (quadrantHeader(); as tpl) {\n @for (header of headers(); track header.corner) {\n <div [style]=\"header.box\">\n <div\n [class]=\"\n 'minerva-chart-quadrant-header minerva-chart-quadrant-header-' +\n header.corner\n \"\n [style]=\"header.style\"\n >\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"header.context\"\n />\n </div>\n </div>\n }\n }\n @if (pointActions(); as tpl) {\n @for (active of activePoint(); track active.context.index) {\n <div\n class=\"minerva-chart-quadrant-point\"\n [attr.data-point-index]=\"active.context.index\"\n [attr.data-label]=\"active.context.point.label ?? null\"\n [style]=\"active.style\"\n >\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"active.context\"\n />\n </div>\n }\n }\n </div>\n }\n <div #points style=\"display:contents\"></div>",
40117
+ template: "<div #surface style=\"position:absolute;inset:0\"></div>\n @if (overlayStyle(); as style) {\n <div class=\"minerva-chart-overlay\" [style]=\"style\">\n @if (quadrantHeader(); as tpl) {\n @for (header of headers(); track header.corner) {\n <div [style]=\"header.box\">\n <div\n [class]=\"\n 'minerva-chart-quadrant-header minerva-chart-quadrant-header-' +\n header.corner\n \"\n [style]=\"header.style\"\n >\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"header.context\"\n />\n </div>\n </div>\n }\n }\n <div #cardHost [style]=\"cardHostStyle()\"></div>\n </div>\n }\n <div #points style=\"display:contents\"></div>",
40027
40118
  isInline: true,
40028
40119
  dependencies: [{
40029
40120
  kind: "directive",
@@ -40071,21 +40162,7 @@ i0.ɵɵngDeclareClassMetadata({
40071
40162
  </div>
40072
40163
  }
40073
40164
  }
40074
- @if (pointActions(); as tpl) {
40075
- @for (active of activePoint(); track active.context.index) {
40076
- <div
40077
- class="minerva-chart-quadrant-point"
40078
- [attr.data-point-index]="active.context.index"
40079
- [attr.data-label]="active.context.point.label ?? null"
40080
- [style]="active.style"
40081
- >
40082
- <ng-container
40083
- [ngTemplateOutlet]="tpl"
40084
- [ngTemplateOutletContext]="active.context"
40085
- />
40086
- </div>
40087
- }
40088
- }
40165
+ <div #cardHost [style]="cardHostStyle()"></div>
40089
40166
  </div>
40090
40167
  }
40091
40168
  <div #points style="display:contents"></div>`
@@ -40284,6 +40361,10 @@ i0.ɵɵngDeclareClassMetadata({
40284
40361
  pointsHost: [{
40285
40362
  type: i0.ViewChild,
40286
40363
  args: ["points", { isSignal: true }]
40364
+ }],
40365
+ cardHost: [{
40366
+ type: i0.ViewChild,
40367
+ args: ["cardHost", { isSignal: true }]
40287
40368
  }]
40288
40369
  }
40289
40370
  });
@@ -46674,6 +46755,10 @@ var MnQuadrantTaskBoard = class MnQuadrantTaskBoard {
46674
46755
  this.corners = QUADRANT_CORNERS;
46675
46756
  this.scope = injectScope();
46676
46757
  this.chart = viewChild.required(MnQuadrantChart, ...ngDevMode ? [{ debugName: "chart" }] : /* istanbul ignore next */ []);
46758
+ this.chartElement = viewChild.required(MnQuadrantChart, {
46759
+ ...ngDevMode ? { debugName: "chartElement" } : /* istanbul ignore next */ {},
46760
+ read: ElementRef
46761
+ });
46677
46762
  this.host = inject(ElementRef).nativeElement;
46678
46763
  this.document = inject(DOCUMENT);
46679
46764
  this.panelRef = viewChild("panel", ...ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []);
@@ -46811,12 +46896,13 @@ var MnQuadrantTaskBoard = class MnQuadrantTaskBoard {
46811
46896
  return this.lastPointer ? { ...this.lastPointer } : emptyBox();
46812
46897
  }
46813
46898
  pointBox(index) {
46814
- const el = this.host.querySelector(`.minerva-chart-quadrant-point[data-point-index="${index}"]`);
46815
- if (!el) return this.pointerBox();
46816
- const box = this.boxOf(el);
46899
+ const point = this.points()[index];
46900
+ const at = point ? this.chart().instance()?.toPixel(point.x, point.y) : null;
46901
+ if (!at) return this.pointerBox();
46902
+ const box = this.boxOf(this.chartElement().nativeElement);
46817
46903
  return {
46818
- x: box.x - DOT,
46819
- y: box.y - DOT,
46904
+ x: box.x + at.x - DOT,
46905
+ y: box.y + at.y - DOT,
46820
46906
  width: 20,
46821
46907
  height: 20
46822
46908
  };
@@ -47181,6 +47267,14 @@ var MnQuadrantTaskBoard = class MnQuadrantTaskBoard {
47181
47267
  descendants: true,
47182
47268
  isSignal: true
47183
47269
  },
47270
+ {
47271
+ propertyName: "chartElement",
47272
+ first: true,
47273
+ predicate: MnQuadrantChart,
47274
+ descendants: true,
47275
+ read: ElementRef,
47276
+ isSignal: true
47277
+ },
47184
47278
  {
47185
47279
  propertyName: "panelRef",
47186
47280
  first: true,
@@ -47206,7 +47300,7 @@ var MnQuadrantTaskBoard = class MnQuadrantTaskBoard {
47206
47300
  ],
47207
47301
  exportAs: ["mnQuadrantTaskBoard"],
47208
47302
  ngImport: i0,
47209
- template: "\n <mn-quadrant-chart\n [points]=\"points()\"\n [locale]=\"language()\"\n [selectedIndex]=\"selected()\"\n [draggable]=\"draggable() ?? true\"\n [height]=\"height()\"\n [width]=\"width()\"\n [renderer]=\"renderer()\"\n [legend]=\"legend()\"\n [tooltip]=\"tooltip()\"\n [crosshair]=\"crosshair()\"\n [annotations]=\"annotations()\"\n [interaction]=\"interaction()\"\n [padding]=\"padding()\"\n [palette]=\"palette()\"\n [ariaLabel]=\"ariaLabel()\"\n [xDomain]=\"xDomain()\"\n [yDomain]=\"yDomain()\"\n [xSplit]=\"xSplit()\"\n [ySplit]=\"ySplit()\"\n [quadrants]=\"quadrants()\"\n [xLabel]=\"xLabel()\"\n [yLabel]=\"yLabel()\"\n [axisLabels]=\"axisLabels()\"\n [showTicks]=\"showTicks()\"\n [reverseX]=\"reverseX()\"\n [showLabels]=\"showLabels()\"\n [pointSize]=\"pointSize()\"\n [name]=\"name()\"\n [selectedIndices]=\"selectedIndices()\"\n [accessiblePoints]=\"accessiblePoints()\"\n [quadrantHeader]=\"header\"\n [pointActions]=\"actions\"\n (hover)=\"hover.emit($event)\"\n (zoom)=\"zoom.emit($event)\"\n (rendererChange)=\"rendererChange.emit($event)\"\n (pointClick)=\"onPointClick($event)\"\n (pointDoubleClick)=\"edit($event.index)\"\n (pointDrag)=\"pointDrag.emit($event)\"\n (pointDrop)=\"onPointDrop($event)\"\n (quadrantClick)=\"add($event.quadrant, pointerBox(), $event)\"\n />\n <ng-template #header let-corner let-info=\"info\">\n <div [class]=\"s.header\" [attr.data-corner]=\"corner\">\n @if (info.region?.badge) {\n <span [class]=\"s.badge\">{{ info.region.badge }}</span>\n }\n <span [class]=\"s.title\">{{ title(corner) }}</span>\n @if (showCounts() !== false) {\n <span [class]=\"s.count\">{{ countText(info.points) }}</span>\n }\n <button\n mnIconButton\n size=\"xsmall\"\n variant=\"ghost\"\n [class]=\"s.add\"\n [label]=\"texts().add + ' · ' + title(corner)\"\n [attr.aria-expanded]=\"adding() === corner\"\n (click)=\"add(corner, boxOf($event.currentTarget))\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"icons.add\" /></svg>\n </button>\n </div>\n </ng-template>\n <ng-template #actions let-index=\"index\">\n <div\n role=\"toolbar\"\n [attr.aria-label]=\"texts().actions\"\n [class]=\"s.actions\"\n (pointerdown)=\"$event.stopPropagation()\"\n >\n <button\n mnIconButton\n size=\"xsmall\"\n variant=\"ghost\"\n [label]=\"texts().edit\"\n (click)=\"edit(index)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"icons.edit\" /></svg>\n </button>\n <button\n mnIconButton\n size=\"xsmall\"\n variant=\"ghost\"\n color=\"danger\"\n [label]=\"texts().delete\"\n (click)=\"confirmRemove(index)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"icons.delete\" /></svg>\n </button>\n </div>\n </ng-template>\n @if (draft(); as current) {\n <div\n #panel\n role=\"dialog\"\n [attr.aria-labelledby]=\"ids.title\"\n [class]=\"s.panel\"\n [attr.data-quadrant-task-panel]=\"\n current.index === null ? 'add' : 'edit'\n \"\n (keydown)=\"onPanelKeydown($event)\"\n >\n <form [class]=\"s.form\" (submit)=\"save($event)\">\n <h3 [id]=\"ids.title\" [class]=\"s.panelTitle\">\n {{ current.index === null ? texts().add : texts().edit }}\n </h3>\n <label [class]=\"s.field\">\n <span>{{ texts().name }}</span>\n <mn-input\n required\n size=\"small\"\n [value]=\"current.label\"\n [aria-label]=\"texts().name\"\n (valueChange)=\"setLabel($event ?? '')\"\n />\n </label>\n <fieldset [class]=\"s.quadrants\">\n <legend>{{ texts().quadrant }}</legend>\n <div [class]=\"s.quadrantGrid\">\n @for (corner of corners; track corner) {\n <label [class]=\"s.quadrant\" [attr.data-corner]=\"corner\">\n <input\n type=\"radio\"\n [name]=\"ids.quadrant\"\n [value]=\"corner\"\n [checked]=\"current.quadrant === corner\"\n (change)=\"setQuadrant(corner)\"\n />\n <span\n [class]=\"s.swatch\"\n [style.background]=\"color(corner)\"\n aria-hidden=\"true\"\n ></span>\n <span [class]=\"s.quadrantName\">{{ title(corner) }}</span>\n </label>\n }\n </div>\n </fieldset>\n <div [class]=\"s.footer\">\n <button\n mnButton\n size=\"small\"\n color=\"neutral\"\n variant=\"ghost\"\n (click)=\"close()\"\n >\n {{ texts().cancel }}\n </button>\n <button mnButton size=\"small\" type=\"submit\" [disabled]=\"!canSave()\">\n {{ texts().save }}\n </button>\n </div>\n </form>\n </div>\n }\n @if (deletingPoint()) {\n <div\n #panel\n role=\"alertdialog\"\n [attr.aria-labelledby]=\"ids.title\"\n [attr.aria-describedby]=\"ids.text\"\n [class]=\"s.panel\"\n data-quadrant-task-panel=\"delete\"\n (keydown)=\"onPanelKeydown($event)\"\n >\n <h3 [id]=\"ids.title\" [class]=\"s.panelTitle\">{{ texts().delete }}</h3>\n <p [id]=\"ids.text\" [class]=\"s.panelText\">{{ deleteText() }}</p>\n <div [class]=\"s.footer\">\n <button\n #deleteCancel\n mnButton\n size=\"small\"\n color=\"neutral\"\n variant=\"ghost\"\n (click)=\"close()\"\n >\n {{ texts().cancel }}\n </button>\n <button mnButton size=\"small\" color=\"danger\" (click)=\"remove()\">\n {{ texts().confirmDelete }}\n </button>\n </div>\n </div>\n }\n ",
47303
+ template: "\n <mn-quadrant-chart\n [points]=\"points()\"\n [locale]=\"language()\"\n [selectedIndex]=\"selected()\"\n [draggable]=\"draggable() ?? true\"\n [height]=\"height()\"\n [width]=\"width()\"\n [renderer]=\"renderer()\"\n [legend]=\"legend()\"\n [tooltip]=\"tooltip()\"\n [crosshair]=\"crosshair()\"\n [annotations]=\"annotations()\"\n [interaction]=\"interaction()\"\n [padding]=\"padding()\"\n [palette]=\"palette()\"\n [ariaLabel]=\"ariaLabel()\"\n [xDomain]=\"xDomain()\"\n [yDomain]=\"yDomain()\"\n [xSplit]=\"xSplit()\"\n [ySplit]=\"ySplit()\"\n [quadrants]=\"quadrants()\"\n [xLabel]=\"xLabel()\"\n [yLabel]=\"yLabel()\"\n [axisLabels]=\"axisLabels()\"\n [showTicks]=\"showTicks()\"\n [reverseX]=\"reverseX()\"\n [showLabels]=\"showLabels()\"\n [pointSize]=\"pointSize()\"\n [name]=\"name()\"\n [selectedIndices]=\"selectedIndices()\"\n [accessiblePoints]=\"accessiblePoints()\"\n [quadrantHeader]=\"header\"\n [pointActions]=\"actions\"\n (hover)=\"hover.emit($event)\"\n (zoom)=\"zoom.emit($event)\"\n (rendererChange)=\"rendererChange.emit($event)\"\n (pointClick)=\"onPointClick($event)\"\n (pointDoubleClick)=\"edit($event.index)\"\n (pointDrag)=\"pointDrag.emit($event)\"\n (pointDrop)=\"onPointDrop($event)\"\n (quadrantClick)=\"add($event.quadrant, pointerBox(), $event)\"\n />\n <ng-template #header let-corner let-info=\"info\">\n <div [class]=\"s.header\" [attr.data-corner]=\"corner\">\n @if (info.region?.badge) {\n <span [class]=\"s.badge\">{{ info.region.badge }}</span>\n }\n <span [class]=\"s.title\">{{ title(corner) }}</span>\n @if (showCounts() !== false) {\n <span [class]=\"s.count\">{{ countText(info.points) }}</span>\n }\n <button\n mnIconButton\n size=\"xsmall\"\n variant=\"ghost\"\n [class]=\"s.add\"\n [label]=\"texts().add + ' · ' + title(corner)\"\n [attr.aria-expanded]=\"adding() === corner\"\n (click)=\"add(corner, boxOf($event.currentTarget))\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"icons.add\" /></svg>\n </button>\n </div>\n </ng-template>\n <ng-template #actions let-index=\"index\">\n <div\n role=\"toolbar\"\n [attr.aria-label]=\"texts().actions\"\n [class]=\"s.cardActions\"\n (pointerdown)=\"$event.stopPropagation()\"\n >\n <button\n mnIconButton\n size=\"xsmall\"\n variant=\"ghost\"\n [label]=\"texts().edit\"\n (click)=\"edit(index)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"icons.edit\" /></svg>\n </button>\n <button\n mnIconButton\n size=\"xsmall\"\n variant=\"ghost\"\n color=\"danger\"\n [label]=\"texts().delete\"\n (click)=\"confirmRemove(index)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"icons.delete\" /></svg>\n </button>\n </div>\n </ng-template>\n @if (draft(); as current) {\n <div\n #panel\n role=\"dialog\"\n [attr.aria-labelledby]=\"ids.title\"\n [class]=\"s.panel\"\n [attr.data-quadrant-task-panel]=\"\n current.index === null ? 'add' : 'edit'\n \"\n (keydown)=\"onPanelKeydown($event)\"\n >\n <form [class]=\"s.form\" (submit)=\"save($event)\">\n <h3 [id]=\"ids.title\" [class]=\"s.panelTitle\">\n {{ current.index === null ? texts().add : texts().edit }}\n </h3>\n <label [class]=\"s.field\">\n <span>{{ texts().name }}</span>\n <mn-input\n required\n size=\"small\"\n [value]=\"current.label\"\n [aria-label]=\"texts().name\"\n (valueChange)=\"setLabel($event ?? '')\"\n />\n </label>\n <fieldset [class]=\"s.quadrants\">\n <legend>{{ texts().quadrant }}</legend>\n <div [class]=\"s.quadrantGrid\">\n @for (corner of corners; track corner) {\n <label [class]=\"s.quadrant\" [attr.data-corner]=\"corner\">\n <input\n type=\"radio\"\n [name]=\"ids.quadrant\"\n [value]=\"corner\"\n [checked]=\"current.quadrant === corner\"\n (change)=\"setQuadrant(corner)\"\n />\n <span\n [class]=\"s.swatch\"\n [style.background]=\"color(corner)\"\n aria-hidden=\"true\"\n ></span>\n <span [class]=\"s.quadrantName\">{{ title(corner) }}</span>\n </label>\n }\n </div>\n </fieldset>\n <div [class]=\"s.footer\">\n <button\n mnButton\n size=\"small\"\n color=\"neutral\"\n variant=\"ghost\"\n (click)=\"close()\"\n >\n {{ texts().cancel }}\n </button>\n <button mnButton size=\"small\" type=\"submit\" [disabled]=\"!canSave()\">\n {{ texts().save }}\n </button>\n </div>\n </form>\n </div>\n }\n @if (deletingPoint()) {\n <div\n #panel\n role=\"alertdialog\"\n [attr.aria-labelledby]=\"ids.title\"\n [attr.aria-describedby]=\"ids.text\"\n [class]=\"s.panel\"\n data-quadrant-task-panel=\"delete\"\n (keydown)=\"onPanelKeydown($event)\"\n >\n <h3 [id]=\"ids.title\" [class]=\"s.panelTitle\">{{ texts().delete }}</h3>\n <p [id]=\"ids.text\" [class]=\"s.panelText\">{{ deleteText() }}</p>\n <div [class]=\"s.footer\">\n <button\n #deleteCancel\n mnButton\n size=\"small\"\n color=\"neutral\"\n variant=\"ghost\"\n (click)=\"close()\"\n >\n {{ texts().cancel }}\n </button>\n <button mnButton size=\"small\" color=\"danger\" (click)=\"remove()\">\n {{ texts().confirmDelete }}\n </button>\n </div>\n </div>\n }\n ",
47210
47304
  isInline: true,
47211
47305
  dependencies: [
47212
47306
  {
@@ -47417,7 +47511,7 @@ i0.ɵɵngDeclareClassMetadata({
47417
47511
  <div
47418
47512
  role="toolbar"
47419
47513
  [attr.aria-label]="texts().actions"
47420
- [class]="s.actions"
47514
+ [class]="s.cardActions"
47421
47515
  (pointerdown)="$event.stopPropagation()"
47422
47516
  >
47423
47517
  <button
@@ -47823,6 +47917,13 @@ i0.ɵɵngDeclareClassMetadata({
47823
47917
  type: i0.ViewChild,
47824
47918
  args: [i0.forwardRef(() => MnQuadrantChart), { isSignal: true }]
47825
47919
  }],
47920
+ chartElement: [{
47921
+ type: i0.ViewChild,
47922
+ args: [i0.forwardRef(() => MnQuadrantChart), {
47923
+ read: ElementRef,
47924
+ isSignal: true
47925
+ }]
47926
+ }],
47826
47927
  panelRef: [{
47827
47928
  type: i0.ViewChild,
47828
47929
  args: ["panel", { isSignal: true }]