@joanroucoux/cairn-ui 0.8.2 → 0.9.1

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 (124) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs +57 -53
  3. package/fesm2022/joanroucoux-cairn-ui-action-bar.mjs.map +1 -1
  4. package/fesm2022/joanroucoux-cairn-ui-alert.mjs +155 -47
  5. package/fesm2022/joanroucoux-cairn-ui-alert.mjs.map +1 -1
  6. package/fesm2022/joanroucoux-cairn-ui-amount.mjs +220 -110
  7. package/fesm2022/joanroucoux-cairn-ui-amount.mjs.map +1 -1
  8. package/fesm2022/joanroucoux-cairn-ui-async.mjs +392 -216
  9. package/fesm2022/joanroucoux-cairn-ui-async.mjs.map +1 -1
  10. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs +145 -68
  11. package/fesm2022/joanroucoux-cairn-ui-avatar.mjs.map +1 -1
  12. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs +87 -47
  13. package/fesm2022/joanroucoux-cairn-ui-back-link.mjs.map +1 -1
  14. package/fesm2022/joanroucoux-cairn-ui-badge.mjs +83 -43
  15. package/fesm2022/joanroucoux-cairn-ui-badge.mjs.map +1 -1
  16. package/fesm2022/joanroucoux-cairn-ui-button.mjs +184 -104
  17. package/fesm2022/joanroucoux-cairn-ui-button.mjs.map +1 -1
  18. package/fesm2022/joanroucoux-cairn-ui-card.mjs +232 -137
  19. package/fesm2022/joanroucoux-cairn-ui-card.mjs.map +1 -1
  20. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs +298 -102
  21. package/fesm2022/joanroucoux-cairn-ui-choice-chips.mjs.map +1 -1
  22. package/fesm2022/joanroucoux-cairn-ui-control.mjs +4 -17
  23. package/fesm2022/joanroucoux-cairn-ui-control.mjs.map +1 -1
  24. package/fesm2022/joanroucoux-cairn-ui-delta.mjs +150 -83
  25. package/fesm2022/joanroucoux-cairn-ui-delta.mjs.map +1 -1
  26. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs +508 -471
  27. package/fesm2022/joanroucoux-cairn-ui-dialog.mjs.map +1 -1
  28. package/fesm2022/joanroucoux-cairn-ui-donut.mjs +255 -157
  29. package/fesm2022/joanroucoux-cairn-ui-donut.mjs.map +1 -1
  30. package/fesm2022/joanroucoux-cairn-ui-drawer.mjs +303 -0
  31. package/fesm2022/joanroucoux-cairn-ui-drawer.mjs.map +1 -0
  32. package/fesm2022/joanroucoux-cairn-ui-empty.mjs +76 -35
  33. package/fesm2022/joanroucoux-cairn-ui-empty.mjs.map +1 -1
  34. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs +40 -31
  35. package/fesm2022/joanroucoux-cairn-ui-external-link.mjs.map +1 -1
  36. package/fesm2022/joanroucoux-cairn-ui-fact.mjs +188 -79
  37. package/fesm2022/joanroucoux-cairn-ui-fact.mjs.map +1 -1
  38. package/fesm2022/joanroucoux-cairn-ui-field.mjs +309 -173
  39. package/fesm2022/joanroucoux-cairn-ui-field.mjs.map +1 -1
  40. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs +165 -87
  41. package/fesm2022/joanroucoux-cairn-ui-filter-chips.mjs.map +1 -1
  42. package/fesm2022/joanroucoux-cairn-ui-group-header.mjs +269 -0
  43. package/fesm2022/joanroucoux-cairn-ui-group-header.mjs.map +1 -0
  44. package/fesm2022/joanroucoux-cairn-ui-input.mjs +205 -85
  45. package/fesm2022/joanroucoux-cairn-ui-input.mjs.map +1 -1
  46. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs +838 -512
  47. package/fesm2022/joanroucoux-cairn-ui-line-chart.mjs.map +1 -1
  48. package/fesm2022/joanroucoux-cairn-ui-menu.mjs +401 -225
  49. package/fesm2022/joanroucoux-cairn-ui-menu.mjs.map +1 -1
  50. package/fesm2022/joanroucoux-cairn-ui-meter.mjs +143 -57
  51. package/fesm2022/joanroucoux-cairn-ui-meter.mjs.map +1 -1
  52. package/fesm2022/joanroucoux-cairn-ui-motion.mjs +367 -399
  53. package/fesm2022/joanroucoux-cairn-ui-motion.mjs.map +1 -1
  54. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs +73 -44
  55. package/fesm2022/joanroucoux-cairn-ui-nav-item.mjs.map +1 -1
  56. package/fesm2022/joanroucoux-cairn-ui-result-group.mjs +191 -0
  57. package/fesm2022/joanroucoux-cairn-ui-result-group.mjs.map +1 -0
  58. package/fesm2022/joanroucoux-cairn-ui-row.mjs +408 -146
  59. package/fesm2022/joanroucoux-cairn-ui-row.mjs.map +1 -1
  60. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs +183 -81
  61. package/fesm2022/joanroucoux-cairn-ui-segmented.mjs.map +1 -1
  62. package/fesm2022/joanroucoux-cairn-ui-select-pill.mjs +270 -0
  63. package/fesm2022/joanroucoux-cairn-ui-select-pill.mjs.map +1 -0
  64. package/fesm2022/joanroucoux-cairn-ui-select.mjs +121 -67
  65. package/fesm2022/joanroucoux-cairn-ui-select.mjs.map +1 -1
  66. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs +129 -51
  67. package/fesm2022/joanroucoux-cairn-ui-skeleton.mjs.map +1 -1
  68. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs +80 -39
  69. package/fesm2022/joanroucoux-cairn-ui-stale-link.mjs.map +1 -1
  70. package/fesm2022/joanroucoux-cairn-ui-stat.mjs +85 -55
  71. package/fesm2022/joanroucoux-cairn-ui-stat.mjs.map +1 -1
  72. package/fesm2022/joanroucoux-cairn-ui-switch.mjs +48 -35
  73. package/fesm2022/joanroucoux-cairn-ui-switch.mjs.map +1 -1
  74. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs +113 -69
  75. package/fesm2022/joanroucoux-cairn-ui-tab-bar.mjs.map +1 -1
  76. package/fesm2022/joanroucoux-cairn-ui-table.mjs +1036 -310
  77. package/fesm2022/joanroucoux-cairn-ui-table.mjs.map +1 -1
  78. package/fesm2022/joanroucoux-cairn-ui-toast.mjs +240 -127
  79. package/fesm2022/joanroucoux-cairn-ui-toast.mjs.map +1 -1
  80. package/fesm2022/joanroucoux-cairn-ui.mjs +0 -4
  81. package/package.json +17 -1
  82. package/styles/motion.css +1 -13
  83. package/styles/tokens.css +35 -64
  84. package/types/joanroucoux-cairn-ui-action-bar.d.ts +9 -14
  85. package/types/joanroucoux-cairn-ui-alert.d.ts +29 -15
  86. package/types/joanroucoux-cairn-ui-amount.d.ts +64 -32
  87. package/types/joanroucoux-cairn-ui-async.d.ts +85 -38
  88. package/types/joanroucoux-cairn-ui-avatar.d.ts +34 -22
  89. package/types/joanroucoux-cairn-ui-back-link.d.ts +20 -15
  90. package/types/joanroucoux-cairn-ui-badge.d.ts +22 -14
  91. package/types/joanroucoux-cairn-ui-button.d.ts +36 -18
  92. package/types/joanroucoux-cairn-ui-card.d.ts +48 -23
  93. package/types/joanroucoux-cairn-ui-choice-chips.d.ts +91 -35
  94. package/types/joanroucoux-cairn-ui-control.d.ts +9 -18
  95. package/types/joanroucoux-cairn-ui-delta.d.ts +46 -24
  96. package/types/joanroucoux-cairn-ui-dialog.d.ts +124 -57
  97. package/types/joanroucoux-cairn-ui-donut.d.ts +83 -46
  98. package/types/joanroucoux-cairn-ui-drawer.d.ts +73 -0
  99. package/types/joanroucoux-cairn-ui-empty.d.ts +20 -13
  100. package/types/joanroucoux-cairn-ui-external-link.d.ts +6 -10
  101. package/types/joanroucoux-cairn-ui-fact.d.ts +49 -25
  102. package/types/joanroucoux-cairn-ui-field.d.ts +72 -49
  103. package/types/joanroucoux-cairn-ui-filter-chips.d.ts +35 -20
  104. package/types/joanroucoux-cairn-ui-group-header.d.ts +58 -0
  105. package/types/joanroucoux-cairn-ui-input.d.ts +59 -37
  106. package/types/joanroucoux-cairn-ui-line-chart.d.ts +199 -89
  107. package/types/joanroucoux-cairn-ui-menu.d.ts +81 -44
  108. package/types/joanroucoux-cairn-ui-meter.d.ts +44 -22
  109. package/types/joanroucoux-cairn-ui-motion.d.ts +61 -75
  110. package/types/joanroucoux-cairn-ui-nav-item.d.ts +15 -13
  111. package/types/joanroucoux-cairn-ui-result-group.d.ts +52 -0
  112. package/types/joanroucoux-cairn-ui-row.d.ts +92 -37
  113. package/types/joanroucoux-cairn-ui-segmented.d.ts +51 -27
  114. package/types/joanroucoux-cairn-ui-select-pill.d.ts +59 -0
  115. package/types/joanroucoux-cairn-ui-select.d.ts +28 -18
  116. package/types/joanroucoux-cairn-ui-skeleton.d.ts +35 -18
  117. package/types/joanroucoux-cairn-ui-stale-link.d.ts +23 -16
  118. package/types/joanroucoux-cairn-ui-stat.d.ts +26 -21
  119. package/types/joanroucoux-cairn-ui-switch.d.ts +7 -11
  120. package/types/joanroucoux-cairn-ui-tab-bar.d.ts +20 -17
  121. package/types/joanroucoux-cairn-ui-table.d.ts +251 -89
  122. package/types/joanroucoux-cairn-ui-toast.d.ts +46 -36
  123. package/types/joanroucoux-cairn-ui.d.ts +1 -2
  124. package/fesm2022/joanroucoux-cairn-ui.mjs.map +0 -1
@@ -1,553 +1,718 @@
1
- import * as i0 from '@angular/core';
2
- import { signal, inject, DestroyRef, input, booleanAttribute, ElementRef, viewChild, computed, afterNextRender, effect, untracked, Component } from '@angular/core';
3
- import { injectReducedMotion } from '@joanroucoux/cairn-ui/motion';
4
- import { scaleLinear } from 'd3-scale';
5
- import { curveMonotoneX, line } from 'd3-shape';
6
-
1
+ import * as i0 from "@angular/core";
2
+ import { Component, DestroyRef, ElementRef, afterNextRender, booleanAttribute, computed, effect, inject, input, signal, untracked, viewChild } from "@angular/core";
3
+ import { injectReducedMotion } from "@joanroucoux/cairn-ui/motion";
4
+ import { scaleLinear } from "d3-scale";
5
+ import { curveMonotoneX, line } from "d3-shape";
7
6
  const axisTicks = (points, maxTicks = 5) => {
8
- if (points.length <= maxTicks) {
9
- return [...points];
10
- }
11
- const count = Math.max(3, Math.min(maxTicks, points.length));
12
- const step = (points.length - 1) / (count - 1);
13
- const indexes = new Set();
14
- for (let tick = 0; tick < count; tick++) {
15
- indexes.add(Math.round(tick * step));
16
- }
17
- return [...indexes].sort((a, b) => a - b).map((index) => points[index]);
7
+ if (points.length <= maxTicks) return [...points];
8
+ const count = Math.max(3, Math.min(maxTicks, points.length));
9
+ const step = (points.length - 1) / (count - 1);
10
+ const indexes = /* @__PURE__ */ new Set();
11
+ for (let tick = 0; tick < count; tick++) indexes.add(Math.round(tick * step));
12
+ return [...indexes].sort((a, b) => a - b).map((index) => points[index]);
18
13
  };
19
14
  const isCoreTick = (index, count) => {
20
- if (count <= 3) {
21
- return true;
22
- }
23
- const middle = Math.floor((count - 1) / 2);
24
- return index === 0 || index === count - 1 || index === middle;
15
+ if (count <= 3) return true;
16
+ const middle = Math.floor((count - 1) / 2);
17
+ return index === 0 || index === count - 1 || index === middle;
25
18
  };
26
19
  const firstOfEachRun = (labels, included) => {
27
- let previous = null;
28
- return labels.map((text, index) => {
29
- if (!included(index)) {
30
- return false;
31
- }
32
- const distinct = text !== previous;
33
- previous = text;
34
- return distinct;
35
- });
20
+ let previous = null;
21
+ return labels.map((text, index) => {
22
+ if (!included(index)) return false;
23
+ const distinct = text !== previous;
24
+ previous = text;
25
+ return distinct;
26
+ });
36
27
  };
37
28
  const tickLabels = (ticks, format, mode) => {
38
- const labels = ticks.map((tick) => format(tick.t));
39
- const count = labels.length;
40
- const core = (index) => isCoreTick(index, count);
41
- const everyTick = firstOfEachRun(labels, () => true);
42
- const coreTicks = firstOfEachRun(labels, core);
43
- return labels.map((text, index) => {
44
- const wide = mode === 3 ? coreTicks[index] : everyTick[index];
45
- const narrow = coreTicks[index];
46
- if (!wide && !narrow) {
47
- return null;
48
- }
49
- const classes = mode === 'auto' ? `${narrow ? '' : 'max-sm:hidden'} ${wide ? '' : 'sm:hidden'}`.trim() : wide ? '' : 'hidden';
50
- return { text, classes };
51
- });
29
+ const labels = ticks.map((tick) => format(tick.t));
30
+ const count = labels.length;
31
+ const core = (index) => isCoreTick(index, count);
32
+ const everyTick = firstOfEachRun(labels, () => true);
33
+ const coreTicks = firstOfEachRun(labels, core);
34
+ return labels.map((text, index) => {
35
+ const wide = mode === 3 ? coreTicks[index] : everyTick[index];
36
+ const narrow = coreTicks[index];
37
+ if (!wide && !narrow) return null;
38
+ return {
39
+ text,
40
+ classes: mode === "auto" ? `${narrow ? "" : "max-sm:hidden"} ${wide ? "" : "sm:hidden"}`.trim() : wide ? "" : "hidden"
41
+ };
42
+ });
52
43
  };
53
-
54
44
  const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
55
45
  const nearestPointIndex = (points, x) => {
56
- let nearest = 0;
57
- let nearestDistance = Infinity;
58
- points.forEach((point, index) => {
59
- const distance = Math.abs(point.x - x);
60
- if (distance < nearestDistance) {
61
- nearestDistance = distance;
62
- nearest = index;
63
- }
64
- });
65
- return nearest;
46
+ let nearest = 0;
47
+ let nearestDistance = Infinity;
48
+ points.forEach((point, index) => {
49
+ const distance = Math.abs(point.x - x);
50
+ if (distance < nearestDistance) {
51
+ nearestDistance = distance;
52
+ nearest = index;
53
+ }
54
+ });
55
+ return nearest;
66
56
  };
67
57
  const navigateIndex = (key, current, last) => {
68
- switch (key) {
69
- case 'ArrowRight':
70
- return { index: clamp((current ?? -1) + 1, 0, last), preventDefault: true };
71
- case 'ArrowLeft':
72
- return { index: clamp((current ?? last + 1) - 1, 0, last), preventDefault: true };
73
- case 'Home':
74
- return { index: 0, preventDefault: true };
75
- case 'End':
76
- return { index: last, preventDefault: true };
77
- case 'Escape':
78
- return { index: null, preventDefault: false };
79
- default:
80
- return null;
81
- }
58
+ switch (key) {
59
+ case "ArrowRight": return {
60
+ index: clamp((current ?? -1) + 1, 0, last),
61
+ preventDefault: true
62
+ };
63
+ case "ArrowLeft": return {
64
+ index: clamp((current ?? last + 1) - 1, 0, last),
65
+ preventDefault: true
66
+ };
67
+ case "Home": return {
68
+ index: 0,
69
+ preventDefault: true
70
+ };
71
+ case "End": return {
72
+ index: last,
73
+ preventDefault: true
74
+ };
75
+ case "Escape": return {
76
+ index: null,
77
+ preventDefault: false
78
+ };
79
+ default: return null;
80
+ }
82
81
  };
83
-
84
- const START_LABEL_HEIGHT = 16;
85
82
  const AXIS_ROW = 17;
86
- const TOOLTIP_SIZES = ['compact', 'large'];
87
83
  const TOOLTIP_CLASSES = {
88
- compact: { box: '-top-3 px-2.5', value: 'text-label', delta: 'text-caption' },
89
- large: { box: 'top-0 px-3', value: 'text-body', delta: 'text-label' },
84
+ compact: {
85
+ box: "-top-3 px-2.5",
86
+ value: "text-label",
87
+ delta: "text-caption"
88
+ },
89
+ large: {
90
+ box: "top-0 px-3",
91
+ value: "text-body",
92
+ delta: "text-label"
93
+ }
90
94
  };
91
95
  const PLOT_PADDING_RATIO = 16 / 240;
92
96
  const SPARKLINE_PADDING = 4;
93
97
  const START_LABEL_BASELINE_OFFSET = 4;
94
98
  const TOOLTIP_OFFSET = 14;
95
- const TOOLTIP_FLIP_RATIO = 0.55;
99
+ const TOOLTIP_FLIP_RATIO = .55;
96
100
  const plotHeightFor = (height, sparkline, axisGap, wide) => sparkline ? height : Math.max(height - resolveGap(axisGap, wide) - AXIS_ROW, 1);
97
101
  const plotPaddingFor = (plotHeight, sparkline) => sparkline ? SPARKLINE_PADDING : plotHeight * PLOT_PADDING_RATIO;
98
102
  const startLabelBaseline = (boxBottom) => boxBottom - START_LABEL_BASELINE_OFFSET;
99
103
  const tooltipPlacement = (ratio) => {
100
- const flipped = ratio > TOOLTIP_FLIP_RATIO;
101
- return `left: calc(${ratio * 100}% ${flipped ? '-' : '+'} ${TOOLTIP_OFFSET}px); transform: ${flipped ? 'translateX(-100%)' : 'none'}`;
104
+ const flipped = ratio > TOOLTIP_FLIP_RATIO;
105
+ return `left: calc(${ratio * 100}% ${flipped ? "-" : "+"} ${TOOLTIP_OFFSET}px); transform: ${flipped ? "translateX(-100%)" : "none"}`;
102
106
  };
103
- const deltaTone = (delta) => delta > 0 ? 'text-(--positive)' : delta < 0 ? 'text-(--negative)' : 'text-(--muted-foreground)';
104
- const DEFAULT_WIDTH = 640;
105
- const DEFAULT_HEIGHT = 240;
107
+ const deltaTone = (delta) => delta > 0 ? "text-(--positive)" : delta < 0 ? "text-(--negative)" : "text-(--muted-foreground)";
106
108
  const identityValue = (value) => `${value}`;
107
- const identityDelta = (delta) => `${delta > 0 ? '+' : ''}${delta}`;
109
+ const identityDelta = (delta) => `${delta > 0 ? "+" : ""}${delta}`;
108
110
  const identityTime = (time) => new Date(time).toISOString();
109
- const parseAxisTicks = (value) => value === 'auto' ? 'auto' : Number(value);
110
- const resolveGap = (gap, wide) => (gap === 'auto' ? (wide ? 16 : 12) : gap);
111
- const resolveTooltip = (size, wide) => size === 'auto' ? (wide ? 'large' : 'compact') : size;
111
+ const parseAxisTicks = (value) => value === "auto" ? "auto" : Number(value);
112
+ const resolveGap = (gap, wide) => gap === "auto" ? wide ? 16 : 12 : gap;
113
+ const resolveTooltip = (size, wide) => size === "auto" ? wide ? "large" : "compact" : size;
112
114
  const tooltipClassesFor = (size, wide) => TOOLTIP_CLASSES[resolveTooltip(size, wide)];
113
-
114
115
  const CURVE = curveMonotoneX;
115
116
  const buildGeometry = (points, width, height, padding = {}) => {
116
- if (points.length === 0) {
117
- return null;
118
- }
119
- const { x: paddingX = 4, top: paddingTop = 4, bottom: paddingBottom = 4 } = padding;
120
- const plotTop = paddingTop;
121
- const plotBottom = height - paddingBottom;
122
- const times = points.map((point) => point.t);
123
- const values = points.map((point) => point.v);
124
- const minValue = Math.min(...values);
125
- const maxValue = Math.max(...values);
126
- const x = scaleLinear()
127
- .domain([Math.min(...times), Math.max(...times)])
128
- .range([paddingX, width - paddingX]);
129
- const y = minValue === maxValue
130
- ? () => (plotTop + plotBottom) / 2
131
- : scaleLinear().domain([minValue, maxValue]).range([plotBottom, plotTop]);
132
- const toX = (point) => (points.length === 1 ? paddingX : x(point.t));
133
- const lineGenerator = line()
134
- .x(toX)
135
- .y((point) => y(point.v))
136
- .curve(CURVE);
137
- const first = points[0];
138
- const last = points.at(-1);
139
- return {
140
- line: lineGenerator(points),
141
- end: { x: toX(last), y: y(last.v) },
142
- start: { y: y(first.v) },
143
- points: points.map((point) => ({ ...point, x: toX(point), y: y(point.v) })),
144
- plotBottom,
145
- plotTop,
146
- };
117
+ if (points.length === 0) return null;
118
+ const { x: paddingX = 4, top: paddingTop = 4, bottom: paddingBottom = 4 } = padding;
119
+ const plotTop = paddingTop;
120
+ const plotBottom = height - paddingBottom;
121
+ const times = points.map((point) => point.t);
122
+ const values = points.map((point) => point.v);
123
+ const minValue = Math.min(...values);
124
+ const maxValue = Math.max(...values);
125
+ const x = scaleLinear().domain([Math.min(...times), Math.max(...times)]).range([paddingX, width - paddingX]);
126
+ const y = minValue === maxValue ? () => (plotTop + plotBottom) / 2 : scaleLinear().domain([minValue, maxValue]).range([plotBottom, plotTop]);
127
+ const toX = (point) => points.length === 1 ? paddingX : x(point.t);
128
+ const lineGenerator = line().x(toX).y((point) => y(point.v)).curve(CURVE);
129
+ const first = points[0];
130
+ const last = points.at(-1);
131
+ return {
132
+ line: lineGenerator(points),
133
+ end: {
134
+ x: toX(last),
135
+ y: y(last.v)
136
+ },
137
+ start: { y: y(first.v) },
138
+ points: points.map((point) => ({
139
+ ...point,
140
+ x: toX(point),
141
+ y: y(point.v)
142
+ })),
143
+ plotBottom,
144
+ plotTop
145
+ };
147
146
  };
148
-
149
147
  const observeSize = (element, destroyRef, measured) => {
150
- if (typeof ResizeObserver === 'undefined') {
151
- return;
152
- }
153
- const { clientWidth: width, clientHeight: height } = element;
154
- if (width > 0 && height > 0) {
155
- measured({ width, height });
156
- }
157
- const observer = new ResizeObserver(([entry]) => {
158
- if (entry) {
159
- measured({ width: entry.contentRect.width, height: entry.contentRect.height });
160
- }
161
- });
162
- observer.observe(element);
163
- destroyRef.onDestroy(() => observer.disconnect());
148
+ if (typeof ResizeObserver === "undefined") return;
149
+ const { clientWidth: width, clientHeight: height } = element;
150
+ if (width > 0 && height > 0) measured({
151
+ width,
152
+ height
153
+ });
154
+ const observer = new ResizeObserver(([entry]) => {
155
+ if (entry) measured({
156
+ width: entry.contentRect.width,
157
+ height: entry.contentRect.height
158
+ });
159
+ });
160
+ observer.observe(element);
161
+ destroyRef.onDestroy(() => observer.disconnect());
164
162
  };
165
-
166
163
  const LINE_GAP = 6;
167
164
  const COLLISION_MARGIN = 6;
168
165
  const EDGE_TOLERANCE = 3;
169
166
  const candidateSlots = (startY, box, label) => {
170
- const aboveTop = startY - LINE_GAP - label.height;
171
- const aboveBottom = startY - LINE_GAP;
172
- const belowTop = startY + LINE_GAP;
173
- const belowBottom = startY + LINE_GAP + label.height;
174
- const slots = [
175
- { anchor: 'end', x: box.width, y: aboveBottom, top: aboveTop, bottom: aboveBottom },
176
- { anchor: 'end', x: box.width, y: belowBottom, top: belowTop, bottom: belowBottom },
177
- { anchor: 'start', x: 0, y: aboveBottom, top: aboveTop, bottom: aboveBottom },
178
- { anchor: 'start', x: 0, y: belowBottom, top: belowTop, bottom: belowBottom },
179
- { anchor: 'end', x: box.width, y: label.height, top: 0, bottom: label.height },
180
- { anchor: 'start', x: 0, y: label.height, top: 0, bottom: label.height },
181
- { anchor: 'end', x: box.width, y: box.height, top: box.height - label.height, bottom: box.height },
182
- { anchor: 'start', x: 0, y: box.height, top: box.height - label.height, bottom: box.height },
183
- ];
184
- return slots.filter((slot) => slot.top >= -EDGE_TOLERANCE && slot.bottom <= box.height + EDGE_TOLERANCE);
167
+ const aboveTop = startY - LINE_GAP - label.height;
168
+ const aboveBottom = startY - LINE_GAP;
169
+ const belowTop = startY + LINE_GAP;
170
+ const belowBottom = startY + LINE_GAP + label.height;
171
+ return [
172
+ {
173
+ anchor: "end",
174
+ x: box.width,
175
+ y: aboveBottom,
176
+ top: aboveTop,
177
+ bottom: aboveBottom
178
+ },
179
+ {
180
+ anchor: "end",
181
+ x: box.width,
182
+ y: belowBottom,
183
+ top: belowTop,
184
+ bottom: belowBottom
185
+ },
186
+ {
187
+ anchor: "start",
188
+ x: 0,
189
+ y: aboveBottom,
190
+ top: aboveTop,
191
+ bottom: aboveBottom
192
+ },
193
+ {
194
+ anchor: "start",
195
+ x: 0,
196
+ y: belowBottom,
197
+ top: belowTop,
198
+ bottom: belowBottom
199
+ },
200
+ {
201
+ anchor: "end",
202
+ x: box.width,
203
+ y: label.height,
204
+ top: 0,
205
+ bottom: label.height
206
+ },
207
+ {
208
+ anchor: "start",
209
+ x: 0,
210
+ y: label.height,
211
+ top: 0,
212
+ bottom: label.height
213
+ },
214
+ {
215
+ anchor: "end",
216
+ x: box.width,
217
+ y: box.height,
218
+ top: box.height - label.height,
219
+ bottom: box.height
220
+ },
221
+ {
222
+ anchor: "start",
223
+ x: 0,
224
+ y: box.height,
225
+ top: box.height - label.height,
226
+ bottom: box.height
227
+ }
228
+ ].filter((slot) => slot.top >= -3 && slot.bottom <= box.height + EDGE_TOLERANCE);
185
229
  };
186
230
  const hitsFor = (slot, label, points) => {
187
- const left = slot.anchor === 'end' ? slot.x - label.width : slot.x;
188
- const right = slot.anchor === 'end' ? slot.x : slot.x + label.width;
189
- const top = slot.top - COLLISION_MARGIN;
190
- const bottom = slot.bottom + COLLISION_MARGIN;
191
- return points.filter((point) => point.x >= left && point.x <= right && point.y >= top && point.y <= bottom).length;
231
+ const left = slot.anchor === "end" ? slot.x - label.width : slot.x;
232
+ const right = slot.anchor === "end" ? slot.x : slot.x + label.width;
233
+ const top = slot.top - COLLISION_MARGIN;
234
+ const bottom = slot.bottom + COLLISION_MARGIN;
235
+ return points.filter((point) => point.x >= left && point.x <= right && point.y >= top && point.y <= bottom).length;
192
236
  };
193
237
  const placeStartLabel = (points, startY, box, label) => {
194
- let best = null;
195
- for (const slot of candidateSlots(startY, box, label)) {
196
- const hits = hitsFor(slot, label, points);
197
- if (!best || hits < best.hits) {
198
- best = { slot, hits };
199
- }
200
- if (hits === 0) {
201
- break;
202
- }
203
- }
204
- if (!best) {
205
- return { anchor: 'end', x: box.width, y: label.height };
206
- }
207
- const { anchor, x, y } = best.slot;
208
- return { anchor, x, y };
238
+ let best = null;
239
+ for (const slot of candidateSlots(startY, box, label)) {
240
+ const hits = hitsFor(slot, label, points);
241
+ if (!best || hits < best.hits) best = {
242
+ slot,
243
+ hits
244
+ };
245
+ if (hits === 0) break;
246
+ }
247
+ if (!best) return {
248
+ anchor: "end",
249
+ x: box.width,
250
+ y: label.height
251
+ };
252
+ const { anchor, x, y } = best.slot;
253
+ return {
254
+ anchor,
255
+ x,
256
+ y
257
+ };
209
258
  };
210
-
211
- const TRANSITION_SAMPLES = 120;
212
- const TRANSITION_DURATION = 260;
213
259
  const easeOutQuint = (t) => 1 - (1 - t) ** 5;
214
260
  const durationFrom = (value) => {
215
- const match = /^\s*([\d.]+)(ms|s)\s*$/.exec(value);
216
- const duration = match ? Number(match[1]) * (match[2] === 's' ? 1000 : 1) : Number.NaN;
217
- return Number.isFinite(duration) ? duration : TRANSITION_DURATION;
261
+ const match = /^\s*([\d.]+)(ms|s)\s*$/.exec(value);
262
+ const duration = match ? Number(match[1]) * (match[2] === "s" ? 1e3 : 1) : NaN;
263
+ return Number.isFinite(duration) ? duration : 260;
218
264
  };
219
- const sameSeries = (a, b) => a === b ||
220
- (b !== null &&
221
- a.length === b.length &&
222
- a.every((point, index) => point.t === b[index].t && point.v === b[index].v));
265
+ const sameSeries = (a, b) => a === b || b !== null && a.length === b.length && a.every((point, index) => point.t === b[index].t && point.v === b[index].v);
223
266
  const monotoneTangents = (xs, ys) => {
224
- const n = xs.length;
225
- const slopes = [];
226
- for (let i = 0; i < n - 1; i++) {
227
- const dx = xs[i + 1] - xs[i];
228
- slopes.push(dx === 0 ? 0 : (ys[i + 1] - ys[i]) / dx);
229
- }
230
- const tangents = new Array(n).fill(0);
231
- tangents[0] = slopes[0];
232
- tangents[n - 1] = slopes[n - 2];
233
- for (let i = 1; i < n - 1; i++) {
234
- const a = slopes[i - 1];
235
- const b = slopes[i];
236
- tangents[i] = a * b <= 0 ? 0 : (a + b) / 2;
237
- }
238
- for (let i = 0; i < n - 1; i++) {
239
- const slope = slopes[i];
240
- if (slope === 0) {
241
- tangents[i] = 0;
242
- tangents[i + 1] = 0;
243
- continue;
244
- }
245
- const a = tangents[i] / slope;
246
- const b = tangents[i + 1] / slope;
247
- const s = a * a + b * b;
248
- if (s > 9) {
249
- const t = 3 / Math.sqrt(s);
250
- tangents[i] = t * a * slope;
251
- tangents[i + 1] = t * b * slope;
252
- }
253
- }
254
- return tangents;
267
+ const n = xs.length;
268
+ const slopes = [];
269
+ for (let i = 0; i < n - 1; i++) {
270
+ const dx = xs[i + 1] - xs[i];
271
+ slopes.push(dx === 0 ? 0 : (ys[i + 1] - ys[i]) / dx);
272
+ }
273
+ const tangents = new Array(n).fill(0);
274
+ tangents[0] = slopes[0];
275
+ tangents[n - 1] = slopes[n - 2];
276
+ for (let i = 1; i < n - 1; i++) {
277
+ const a = slopes[i - 1];
278
+ const b = slopes[i];
279
+ tangents[i] = a * b <= 0 ? 0 : (a + b) / 2;
280
+ }
281
+ for (let i = 0; i < n - 1; i++) {
282
+ const slope = slopes[i];
283
+ if (slope === 0) {
284
+ tangents[i] = 0;
285
+ tangents[i + 1] = 0;
286
+ continue;
287
+ }
288
+ const a = tangents[i] / slope;
289
+ const b = tangents[i + 1] / slope;
290
+ const s = a * a + b * b;
291
+ if (s > 9) {
292
+ const t = 3 / Math.sqrt(s);
293
+ tangents[i] = t * a * slope;
294
+ tangents[i + 1] = t * b * slope;
295
+ }
296
+ }
297
+ return tangents;
255
298
  };
256
299
  const sampleMonotone = (points, sampleXs) => {
257
- const xs = points.map((point) => point.x);
258
- const ys = points.map((point) => point.y);
259
- const tangents = monotoneTangents(xs, ys);
260
- let segment = 0;
261
- return sampleXs.map((x) => {
262
- while (segment < xs.length - 2 && x > xs[segment + 1]) {
263
- segment++;
264
- }
265
- const x0 = xs[segment];
266
- const x1 = xs[segment + 1];
267
- const dx = x1 - x0;
268
- const f = dx === 0 ? 0 : (x - x0) / dx;
269
- const f2 = f * f;
270
- const f3 = f2 * f;
271
- const y0 = ys[segment];
272
- const y1 = ys[segment + 1];
273
- const m0 = tangents[segment];
274
- const m1 = tangents[segment + 1];
275
- return (2 * f3 - 3 * f2 + 1) * y0 + (f3 - 2 * f2 + f) * m0 * dx + (-2 * f3 + 3 * f2) * y1 + (f3 - f2) * m1 * dx;
276
- });
300
+ const xs = points.map((point) => point.x);
301
+ const ys = points.map((point) => point.y);
302
+ const tangents = monotoneTangents(xs, ys);
303
+ let segment = 0;
304
+ return sampleXs.map((x) => {
305
+ while (segment < xs.length - 2 && x > xs[segment + 1]) segment++;
306
+ const x0 = xs[segment];
307
+ const dx = xs[segment + 1] - x0;
308
+ const f = dx === 0 ? 0 : (x - x0) / dx;
309
+ const f2 = f * f;
310
+ const f3 = f2 * f;
311
+ const y0 = ys[segment];
312
+ const y1 = ys[segment + 1];
313
+ const m0 = tangents[segment];
314
+ const m1 = tangents[segment + 1];
315
+ return (2 * f3 - 3 * f2 + 1) * y0 + (f3 - 2 * f2 + f) * m0 * dx + (-2 * f3 + 3 * f2) * y1 + (f3 - f2) * m1 * dx;
316
+ });
277
317
  };
278
- const polyline = (xs, ys) => xs.map((x, index) => `${index === 0 ? 'M' : 'L'}${x.toFixed(2)},${ys[index].toFixed(2)}`).join(' ');
318
+ const polyline = (xs, ys) => xs.map((x, index) => `${index === 0 ? "M" : "L"}${x.toFixed(2)},${ys[index].toFixed(2)}`).join(" ");
279
319
  const shapeTransition = (from, to) => {
280
- const left = to[0].x;
281
- const right = to.at(-1).x;
282
- const xs = Array.from({ length: TRANSITION_SAMPLES }, (_, index) => left + ((right - left) * index) / (TRANSITION_SAMPLES - 1));
283
- return { xs, from: sampleMonotone(from, xs), to: sampleMonotone(to, xs) };
320
+ const left = to[0].x;
321
+ const right = to.at(-1).x;
322
+ const xs = Array.from({ length: 120 }, (_, index) => left + (right - left) * index / 119);
323
+ return {
324
+ xs,
325
+ from: sampleMonotone(from, xs),
326
+ to: sampleMonotone(to, xs)
327
+ };
284
328
  };
285
329
  const shapeAt = (transition, progress) => transition.from.map((fromY, index) => fromY + (transition.to[index] - fromY) * progress);
286
330
  const animateShape = (from, to, duration, draw, done) => {
287
- const transition = shapeTransition(from, to);
288
- const start = performance.now();
289
- let frame = 0;
290
- const step = (now) => {
291
- const t = Math.min(1, Math.max(0, (now - start) / duration));
292
- if (t === 1) {
293
- done();
294
- return;
295
- }
296
- const ys = shapeAt(transition, easeOutQuint(t));
297
- draw(transition.xs.map((x, index) => ({ x, y: ys[index] })), polyline(transition.xs, ys));
298
- frame = requestAnimationFrame(step);
299
- };
300
- frame = requestAnimationFrame(step);
301
- return () => cancelAnimationFrame(frame);
331
+ const transition = shapeTransition(from, to);
332
+ const start = performance.now();
333
+ let frame = 0;
334
+ const step = (now) => {
335
+ const t = Math.min(1, Math.max(0, (now - start) / duration));
336
+ if (t === 1) {
337
+ done();
338
+ return;
339
+ }
340
+ const ys = shapeAt(transition, easeOutQuint(t));
341
+ draw(transition.xs.map((x, index) => ({
342
+ x,
343
+ y: ys[index]
344
+ })), polyline(transition.xs, ys));
345
+ frame = requestAnimationFrame(step);
346
+ };
347
+ frame = requestAnimationFrame(step);
348
+ return () => cancelAnimationFrame(frame);
302
349
  };
303
- class ShapeMotion {
304
- #view;
305
- #points = null;
306
- #rangeKey = null;
307
- #target = null;
308
- #held = null;
309
- #drawn = null;
310
- #stop = null;
311
- constructor(view) {
312
- this.#view = view;
313
- }
314
- follow(points, geometry, options) {
315
- const newSeries = !sameSeries(points, this.#points);
316
- const newRange = options.rangeKey === null || options.rangeKey !== this.#rangeKey;
317
- this.#points = points;
318
- if (newSeries) {
319
- this.#rangeKey = options.rangeKey;
320
- }
321
- if (this.#stop && !newSeries && !options.reduced && geometry?.line === this.#target?.line) {
322
- this.#target = geometry;
323
- return;
324
- }
325
- const from = this.#drawn;
326
- const held = this.#held ?? this.#target;
327
- this.#target = geometry;
328
- this.stop();
329
- if (!newSeries ||
330
- !newRange ||
331
- !geometry ||
332
- !from ||
333
- from.length < 2 ||
334
- geometry.points.length < 2 ||
335
- options.reduced) {
336
- this.#settle(geometry);
337
- return;
338
- }
339
- const duration = options.duration();
340
- if (duration <= 0) {
341
- this.#settle(geometry);
342
- return;
343
- }
344
- this.#hold(held);
345
- this.#stop = animateShape(from, geometry.points, duration, (shape, line) => this.#draw(shape, line, shape.at(-1)), () => this.#settle(geometry));
346
- }
347
- stop() {
348
- this.#stop?.();
349
- this.#stop = null;
350
- }
351
- #hold(geometry) {
352
- this.#held = geometry;
353
- this.#view.hold(geometry);
354
- }
355
- #settle(geometry) {
356
- this.#stop = null;
357
- this.#hold(null);
358
- this.#drawn = null;
359
- if (geometry) {
360
- this.#draw(geometry.points, geometry.line, geometry.end);
361
- }
362
- }
363
- #draw(shape, line, end) {
364
- this.#drawn = shape;
365
- this.#view.draw(line, end);
366
- }
367
- }
368
-
369
- const WIDE_QUERY = '(min-width: 64rem)';
350
+ var ShapeMotion = class {
351
+ #view;
352
+ #points = null;
353
+ #rangeKey = null;
354
+ #target = null;
355
+ #held = null;
356
+ #drawn = null;
357
+ #stop = null;
358
+ constructor(view) {
359
+ this.#view = view;
360
+ }
361
+ follow(points, geometry, options) {
362
+ const newSeries = !sameSeries(points, this.#points);
363
+ const newRange = options.rangeKey === null || options.rangeKey !== this.#rangeKey;
364
+ this.#points = points;
365
+ if (newSeries) this.#rangeKey = options.rangeKey;
366
+ if (this.#stop && !newSeries && !options.reduced && geometry?.line === this.#target?.line) {
367
+ this.#target = geometry;
368
+ return;
369
+ }
370
+ const from = this.#drawn;
371
+ const held = this.#held ?? this.#target;
372
+ this.#target = geometry;
373
+ this.stop();
374
+ if (!newSeries || !newRange || !geometry || !from || from.length < 2 || geometry.points.length < 2 || options.reduced) {
375
+ this.#settle(geometry);
376
+ return;
377
+ }
378
+ const duration = options.duration();
379
+ if (duration <= 0) {
380
+ this.#settle(geometry);
381
+ return;
382
+ }
383
+ this.#hold(held);
384
+ this.#stop = animateShape(from, geometry.points, duration, (shape, line) => this.#draw(shape, line, shape.at(-1)), () => this.#settle(geometry));
385
+ }
386
+ stop() {
387
+ this.#stop?.();
388
+ this.#stop = null;
389
+ }
390
+ #hold(geometry) {
391
+ this.#held = geometry;
392
+ this.#view.hold(geometry);
393
+ }
394
+ #settle(geometry) {
395
+ this.#stop = null;
396
+ this.#hold(null);
397
+ this.#drawn = null;
398
+ if (geometry) this.#draw(geometry.points, geometry.line, geometry.end);
399
+ }
400
+ #draw(shape, line, end) {
401
+ this.#drawn = shape;
402
+ this.#view.draw(line, end);
403
+ }
404
+ };
405
+ const WIDE_QUERY = "(min-width: 64rem)";
370
406
  const wideViewport = () => {
371
- const query = typeof matchMedia === 'function' ? matchMedia(WIDE_QUERY) : null;
372
- const wide = signal(query?.matches ?? false, /* @ts-ignore */
373
- ...(ngDevMode ? [{ debugName: "wide" }] : /* istanbul ignore next */ []));
374
- const onChange = (event) => wide.set(event.matches);
375
- if (query) {
376
- query.onchange = onChange;
377
- inject(DestroyRef).onDestroy(() => (query.onchange = null));
378
- }
379
- return wide;
407
+ const query = typeof matchMedia === "function" ? matchMedia(WIDE_QUERY) : null;
408
+ const wide = signal(query?.matches ?? false, ...ngDevMode ? [{ debugName: "wide" }] : /* istanbul ignore next */ []);
409
+ const onChange = (event) => wide.set(event.matches);
410
+ if (query) {
411
+ query.onchange = onChange;
412
+ inject(DestroyRef).onDestroy(() => query.onchange = null);
413
+ }
414
+ return wide;
380
415
  };
381
-
382
- /**
383
- * Value over time: a monotone curve with a dashed line at the starting value and a tooltip.
384
- *
385
- * Axis labels are never repeated: a tick whose `axisFormat` text equals the previous drawn label is
386
- * not rendered. The first tick always is, so only the last edge label can drop. With `axisTicks="3"`
387
- * only the first, middle and last ticks count; with `auto` every tick counts from `sm` up and the
388
- * three core ticks below it. Tick positions are unchanged.
389
- *
390
- * A new series interpolates from the drawn curve, fading the start label and the dashed line. With
391
- * `rangeKey` bound, only a series that comes with a new key does so: a new series under the same key
392
- * (a reload after a buy) is drawn at once, with no fade. The key may change before its series arrives.
393
- *
394
- * @example
395
- * <ui-line-chart [points]="points" label="Net worth over one month" startLabel="Since" [valueFormat]="formatEur" />
396
- */
397
- class UiLineChart {
398
- points = input.required(/* @ts-ignore */
399
- ...(ngDevMode ? [{ debugName: "points" }] : /* istanbul ignore next */ []));
400
- label = input.required(/* @ts-ignore */
401
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
402
- valueFormat = input(identityValue, /* @ts-ignore */
403
- ...(ngDevMode ? [{ debugName: "valueFormat" }] : /* istanbul ignore next */ []));
404
- tooltipFormat = input(/* @ts-ignore */
405
- ...(ngDevMode ? [undefined, { debugName: "tooltipFormat" }] : /* istanbul ignore next */ []));
406
- deltaFormat = input(identityDelta, /* @ts-ignore */
407
- ...(ngDevMode ? [{ debugName: "deltaFormat" }] : /* istanbul ignore next */ []));
408
- timeFormat = input(identityTime, /* @ts-ignore */
409
- ...(ngDevMode ? [{ debugName: "timeFormat" }] : /* istanbul ignore next */ []));
410
- axisFormat = input(identityTime, /* @ts-ignore */
411
- ...(ngDevMode ? [{ debugName: "axisFormat" }] : /* istanbul ignore next */ []));
412
- startLabel = input('', /* @ts-ignore */
413
- ...(ngDevMode ? [{ debugName: "startLabel" }] : /* istanbul ignore next */ []));
414
- timeColumnLabel = input('Time', /* @ts-ignore */
415
- ...(ngDevMode ? [{ debugName: "timeColumnLabel" }] : /* istanbul ignore next */ []));
416
- valueColumnLabel = input('Value', /* @ts-ignore */
417
- ...(ngDevMode ? [{ debugName: "valueColumnLabel" }] : /* istanbul ignore next */ []));
418
- sparkline = input(false, { ...(ngDevMode ? { debugName: "sparkline" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
419
- tooltip = input('compact', /* @ts-ignore */
420
- ...(ngDevMode ? [{ debugName: "tooltip" }] : /* istanbul ignore next */ []));
421
- tooltipDelta = input(true, { ...(ngDevMode ? { debugName: "tooltipDelta" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
422
- axisGap = input(12, /* @ts-ignore */
423
- ...(ngDevMode ? [{ debugName: "axisGap" }] : /* istanbul ignore next */ []));
424
- axisTicks = input('auto', { ...(ngDevMode ? { debugName: "axisTicks" } : /* istanbul ignore next */ {}), transform: parseAxisTicks });
425
- rangeKey = input(null, /* @ts-ignore */
426
- ...(ngDevMode ? [{ debugName: "rangeKey" }] : /* istanbul ignore next */ []));
427
- #destroyRef = inject(DestroyRef);
428
- #host = inject(ElementRef);
429
- #wide = wideViewport();
430
- #reducedMotion = injectReducedMotion();
431
- #motion = new ShapeMotion({
432
- hold: (geometry) => {
433
- this.held.set(geometry);
434
- this.activeIndex.update((index) => (geometry ? null : index));
435
- },
436
- draw: (line, end) => {
437
- this.lineRef()?.nativeElement.setAttribute('d', line);
438
- this.endRef()?.nativeElement.setAttribute('cx', String(end.x));
439
- this.endRef()?.nativeElement.setAttribute('cy', String(end.y));
440
- },
441
- });
442
- svgRef = viewChild.required('svgRef', /* @ts-ignore */
443
- ...(ngDevMode ? [{ debugName: "svgRef" }] : /* istanbul ignore next */ []));
444
- lineRef = viewChild('lineRef', /* @ts-ignore */
445
- ...(ngDevMode ? [{ debugName: "lineRef" }] : /* istanbul ignore next */ []));
446
- endRef = viewChild('endRef', /* @ts-ignore */
447
- ...(ngDevMode ? [{ debugName: "endRef" }] : /* istanbul ignore next */ []));
448
- activeIndex = signal(null, /* @ts-ignore */
449
- ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
450
- held = signal(null, /* @ts-ignore */
451
- ...(ngDevMode ? [{ debugName: "held" }] : /* istanbul ignore next */ []));
452
- measuredSize = signal(null, /* @ts-ignore */
453
- ...(ngDevMode ? [{ debugName: "measuredSize" }] : /* istanbul ignore next */ []));
454
- effectiveWidth = computed(() => this.measuredSize()?.width ?? DEFAULT_WIDTH, /* @ts-ignore */
455
- ...(ngDevMode ? [{ debugName: "effectiveWidth" }] : /* istanbul ignore next */ []));
456
- effectiveHeight = computed(() => this.measuredSize()?.height ?? DEFAULT_HEIGHT, /* @ts-ignore */
457
- ...(ngDevMode ? [{ debugName: "effectiveHeight" }] : /* istanbul ignore next */ []));
458
- plotHeight = computed(() => plotHeightFor(this.effectiveHeight(), this.sparkline(), this.axisGap(), this.#wide()), /* @ts-ignore */
459
- ...(ngDevMode ? [{ debugName: "plotHeight" }] : /* istanbul ignore next */ []));
460
- tooltipStyle = (point) => tooltipPlacement(point.x / this.effectiveWidth());
461
- tooltipClasses = computed(() => tooltipClassesFor(this.tooltip(), this.#wide()), /* @ts-ignore */
462
- ...(ngDevMode ? [{ debugName: "tooltipClasses" }] : /* istanbul ignore next */ []));
463
- geometry = computed(() => {
464
- const plotHeight = this.plotHeight();
465
- const padding = plotPaddingFor(plotHeight, this.sparkline());
466
- return buildGeometry(this.points(), this.effectiveWidth(), plotHeight, { x: 0, top: padding, bottom: padding });
467
- }, /* @ts-ignore */
468
- ...(ngDevMode ? [{ debugName: "geometry" }] : /* istanbul ignore next */ []));
469
- shown = computed(() => this.held() ?? this.geometry(), /* @ts-ignore */
470
- ...(ngDevMode ? [{ debugName: "shown" }] : /* istanbul ignore next */ []));
471
- startFade = computed(() => this.held() ? 'opacity-0 duration-(--duration-exit)' : 'duration-(--duration-fast)', /* @ts-ignore */
472
- ...(ngDevMode ? [{ debugName: "startFade" }] : /* istanbul ignore next */ []));
473
- ticks = computed(() => axisTicks(this.geometry().points), /* @ts-ignore */
474
- ...(ngDevMode ? [{ debugName: "ticks" }] : /* istanbul ignore next */ []));
475
- tickTexts = computed(() => tickLabels(this.ticks(), this.axisFormat(), this.axisTicks()), /* @ts-ignore */
476
- ...(ngDevMode ? [{ debugName: "tickTexts" }] : /* istanbul ignore next */ []));
477
- active = computed(() => {
478
- const geometry = this.geometry();
479
- const index = this.activeIndex();
480
- return geometry && index !== null && !this.held() ? geometry.points[index] : null;
481
- }, /* @ts-ignore */
482
- ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
483
- startLabelInfo = computed(() => {
484
- const text = this.startLabel();
485
- const geometry = this.shown();
486
- if (!text || !geometry) {
487
- return null;
488
- }
489
- const label = `${text} ${this.valueFormat()(geometry.points[0].v)}`;
490
- const size = { width: label.length * 0.56 * 12 + 8, height: START_LABEL_HEIGHT };
491
- const placement = placeStartLabel(geometry.points, geometry.start.y, { width: this.effectiveWidth(), height: this.plotHeight() }, size);
492
- return { label, placement: { ...placement, y: startLabelBaseline(placement.y) } };
493
- }, /* @ts-ignore */
494
- ...(ngDevMode ? [{ debugName: "startLabelInfo" }] : /* istanbul ignore next */ []));
495
- #renderRef = afterNextRender(() => {
496
- this.#destroyRef.onDestroy(() => this.#renderRef.destroy());
497
- observeSize(this.svgRef().nativeElement, this.#destroyRef, (size) => this.measuredSize.set(size));
498
- });
499
- constructor() {
500
- effect(() => {
501
- const points = this.points();
502
- const geometry = this.geometry();
503
- const reduced = this.#reducedMotion();
504
- untracked(() => this.#motion.follow(points, geometry, {
505
- reduced,
506
- rangeKey: this.rangeKey(),
507
- duration: () => durationFrom(getComputedStyle(this.#host.nativeElement).getPropertyValue('--duration-base')),
508
- }));
509
- });
510
- this.#destroyRef.onDestroy(() => this.#motion.stop());
511
- }
512
- deltaFor(point) {
513
- return point.v - this.points()[0].v;
514
- }
515
- deltaClasses(point) {
516
- return `${this.tooltipClasses().delta} ${deltaTone(this.deltaFor(point))}`;
517
- }
518
- onPointerMove(event) {
519
- const geometry = this.geometry();
520
- if (this.sparkline() || !geometry || geometry.points.length === 0) {
521
- return;
522
- }
523
- const rect = this.svgRef().nativeElement.getBoundingClientRect();
524
- const ratio = rect.width === 0 ? 1 : this.effectiveWidth() / rect.width;
525
- const x = (event.clientX - rect.left) * ratio;
526
- this.activeIndex.set(nearestPointIndex(geometry.points, x));
527
- }
528
- onPointerLeave() {
529
- this.activeIndex.set(null);
530
- }
531
- onKeydown(event) {
532
- const geometry = this.geometry();
533
- if (this.sparkline() || !geometry || geometry.points.length === 0) {
534
- return;
535
- }
536
- const navigation = navigateIndex(event.key, this.activeIndex(), geometry.points.length - 1);
537
- if (!navigation) {
538
- return;
539
- }
540
- if (navigation.preventDefault) {
541
- event.preventDefault();
542
- }
543
- this.activeIndex.set(navigation.index);
544
- }
545
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiLineChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
546
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: UiLineChart, isStandalone: true, selector: "ui-line-chart", inputs: { points: { classPropertyName: "points", publicName: "points", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, tooltipFormat: { classPropertyName: "tooltipFormat", publicName: "tooltipFormat", isSignal: true, isRequired: false, transformFunction: null }, deltaFormat: { classPropertyName: "deltaFormat", publicName: "deltaFormat", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, axisFormat: { classPropertyName: "axisFormat", publicName: "axisFormat", isSignal: true, isRequired: false, transformFunction: null }, startLabel: { classPropertyName: "startLabel", publicName: "startLabel", isSignal: true, isRequired: false, transformFunction: null }, timeColumnLabel: { classPropertyName: "timeColumnLabel", publicName: "timeColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, valueColumnLabel: { classPropertyName: "valueColumnLabel", publicName: "valueColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, sparkline: { classPropertyName: "sparkline", publicName: "sparkline", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipDelta: { classPropertyName: "tooltipDelta", publicName: "tooltipDelta", isSignal: true, isRequired: false, transformFunction: null }, axisGap: { classPropertyName: "axisGap", publicName: "axisGap", isSignal: true, isRequired: false, transformFunction: null }, axisTicks: { classPropertyName: "axisTicks", publicName: "axisTicks", isSignal: true, isRequired: false, transformFunction: null }, rangeKey: { classPropertyName: "rangeKey", publicName: "rangeKey", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block h-full" }, viewQueries: [{ propertyName: "svgRef", first: true, predicate: ["svgRef"], descendants: true, isSignal: true }, { propertyName: "lineRef", first: true, predicate: ["lineRef"], descendants: true, isSignal: true }, { propertyName: "endRef", first: true, predicate: ["endRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
416
+ var UiLineChart = class UiLineChart {
417
+ points = input.required(...ngDevMode ? [{ debugName: "points" }] : /* istanbul ignore next */ []);
418
+ label = input.required(...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
419
+ valueFormat = input(identityValue, ...ngDevMode ? [{ debugName: "valueFormat" }] : /* istanbul ignore next */ []);
420
+ tooltipFormat = input(...ngDevMode ? [void 0, { debugName: "tooltipFormat" }] : /* istanbul ignore next */ []);
421
+ deltaFormat = input(identityDelta, ...ngDevMode ? [{ debugName: "deltaFormat" }] : /* istanbul ignore next */ []);
422
+ deltaSuffix = input("", ...ngDevMode ? [{ debugName: "deltaSuffix" }] : /* istanbul ignore next */ []);
423
+ timeFormat = input(identityTime, ...ngDevMode ? [{ debugName: "timeFormat" }] : /* istanbul ignore next */ []);
424
+ axisFormat = input(identityTime, ...ngDevMode ? [{ debugName: "axisFormat" }] : /* istanbul ignore next */ []);
425
+ startLabel = input("", ...ngDevMode ? [{ debugName: "startLabel" }] : /* istanbul ignore next */ []);
426
+ timeColumnLabel = input("Time", ...ngDevMode ? [{ debugName: "timeColumnLabel" }] : /* istanbul ignore next */ []);
427
+ valueColumnLabel = input("Value", ...ngDevMode ? [{ debugName: "valueColumnLabel" }] : /* istanbul ignore next */ []);
428
+ sparkline = input(false, {
429
+ ...ngDevMode ? { debugName: "sparkline" } : /* istanbul ignore next */ {},
430
+ transform: booleanAttribute
431
+ });
432
+ tooltip = input("compact", ...ngDevMode ? [{ debugName: "tooltip" }] : /* istanbul ignore next */ []);
433
+ tooltipDelta = input(true, {
434
+ ...ngDevMode ? { debugName: "tooltipDelta" } : /* istanbul ignore next */ {},
435
+ transform: booleanAttribute
436
+ });
437
+ axisGap = input(12, ...ngDevMode ? [{ debugName: "axisGap" }] : /* istanbul ignore next */ []);
438
+ axisTicks = input("auto", {
439
+ ...ngDevMode ? { debugName: "axisTicks" } : /* istanbul ignore next */ {},
440
+ transform: parseAxisTicks
441
+ });
442
+ rangeKey = input(null, ...ngDevMode ? [{ debugName: "rangeKey" }] : /* istanbul ignore next */ []);
443
+ #destroyRef = inject(DestroyRef);
444
+ #host = inject(ElementRef);
445
+ #wide = wideViewport();
446
+ #reducedMotion = injectReducedMotion();
447
+ #motion = new ShapeMotion({
448
+ hold: (geometry) => {
449
+ this.held.set(geometry);
450
+ this.activeIndex.update((index) => geometry ? null : index);
451
+ },
452
+ draw: (line, end) => {
453
+ this.lineRef()?.nativeElement.setAttribute("d", line);
454
+ this.endRef()?.nativeElement.setAttribute("cx", String(end.x));
455
+ this.endRef()?.nativeElement.setAttribute("cy", String(end.y));
456
+ }
457
+ });
458
+ svgRef = viewChild.required("svgRef", ...ngDevMode ? [{ debugName: "svgRef" }] : /* istanbul ignore next */ []);
459
+ lineRef = viewChild("lineRef", ...ngDevMode ? [{ debugName: "lineRef" }] : /* istanbul ignore next */ []);
460
+ endRef = viewChild("endRef", ...ngDevMode ? [{ debugName: "endRef" }] : /* istanbul ignore next */ []);
461
+ activeIndex = signal(null, ...ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []);
462
+ held = signal(null, ...ngDevMode ? [{ debugName: "held" }] : /* istanbul ignore next */ []);
463
+ measuredSize = signal(null, ...ngDevMode ? [{ debugName: "measuredSize" }] : /* istanbul ignore next */ []);
464
+ spacedDeltaSuffix = computed(() => this.deltaSuffix() ? ` ${this.deltaSuffix()}` : "", ...ngDevMode ? [{ debugName: "spacedDeltaSuffix" }] : /* istanbul ignore next */ []);
465
+ effectiveWidth = computed(() => this.measuredSize()?.width ?? 640, ...ngDevMode ? [{ debugName: "effectiveWidth" }] : /* istanbul ignore next */ []);
466
+ effectiveHeight = computed(() => this.measuredSize()?.height ?? 240, ...ngDevMode ? [{ debugName: "effectiveHeight" }] : /* istanbul ignore next */ []);
467
+ plotHeight = computed(() => plotHeightFor(this.effectiveHeight(), this.sparkline(), this.axisGap(), this.#wide()), ...ngDevMode ? [{ debugName: "plotHeight" }] : /* istanbul ignore next */ []);
468
+ tooltipStyle = (point) => tooltipPlacement(point.x / this.effectiveWidth());
469
+ tooltipClasses = computed(() => tooltipClassesFor(this.tooltip(), this.#wide()), ...ngDevMode ? [{ debugName: "tooltipClasses" }] : /* istanbul ignore next */ []);
470
+ geometry = computed(() => {
471
+ const plotHeight = this.plotHeight();
472
+ const padding = plotPaddingFor(plotHeight, this.sparkline());
473
+ return buildGeometry(this.points(), this.effectiveWidth(), plotHeight, {
474
+ x: 0,
475
+ top: padding,
476
+ bottom: padding
477
+ });
478
+ }, ...ngDevMode ? [{ debugName: "geometry" }] : /* istanbul ignore next */ []);
479
+ shown = computed(() => this.held() ?? this.geometry(), ...ngDevMode ? [{ debugName: "shown" }] : /* istanbul ignore next */ []);
480
+ startFade = computed(() => this.held() ? "opacity-0 duration-(--duration-exit)" : "duration-(--duration-fast)", ...ngDevMode ? [{ debugName: "startFade" }] : /* istanbul ignore next */ []);
481
+ ticks = computed(() => axisTicks(this.geometry().points), ...ngDevMode ? [{ debugName: "ticks" }] : /* istanbul ignore next */ []);
482
+ tickTexts = computed(() => tickLabels(this.ticks(), this.axisFormat(), this.axisTicks()), ...ngDevMode ? [{ debugName: "tickTexts" }] : /* istanbul ignore next */ []);
483
+ active = computed(() => {
484
+ const geometry = this.geometry();
485
+ const index = this.activeIndex();
486
+ return geometry && index !== null && !this.held() ? geometry.points[index] : null;
487
+ }, ...ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []);
488
+ startLabelInfo = computed(() => {
489
+ const text = this.startLabel();
490
+ const geometry = this.shown();
491
+ if (!text || !geometry) return null;
492
+ const label = `${text} ${this.valueFormat()(geometry.points[0].v)}`;
493
+ const size = {
494
+ width: label.length * .56 * 12 + 8,
495
+ height: 16
496
+ };
497
+ const placement = placeStartLabel(geometry.points, geometry.start.y, {
498
+ width: this.effectiveWidth(),
499
+ height: this.plotHeight()
500
+ }, size);
501
+ return {
502
+ label,
503
+ placement: {
504
+ ...placement,
505
+ y: startLabelBaseline(placement.y)
506
+ }
507
+ };
508
+ }, ...ngDevMode ? [{ debugName: "startLabelInfo" }] : /* istanbul ignore next */ []);
509
+ #renderRef = afterNextRender(() => {
510
+ this.#destroyRef.onDestroy(() => this.#renderRef.destroy());
511
+ observeSize(this.svgRef().nativeElement, this.#destroyRef, (size) => this.measuredSize.set(size));
512
+ });
513
+ constructor() {
514
+ effect(() => {
515
+ const points = this.points();
516
+ const geometry = this.geometry();
517
+ const reduced = this.#reducedMotion();
518
+ untracked(() => this.#motion.follow(points, geometry, {
519
+ reduced,
520
+ rangeKey: this.rangeKey(),
521
+ duration: () => durationFrom(getComputedStyle(this.#host.nativeElement).getPropertyValue("--duration-base"))
522
+ }));
523
+ });
524
+ this.#destroyRef.onDestroy(() => this.#motion.stop());
525
+ }
526
+ deltaFor(point) {
527
+ return point.v - this.points()[0].v;
528
+ }
529
+ deltaClasses(point) {
530
+ return `${this.tooltipClasses().delta} ${deltaTone(this.deltaFor(point))}`;
531
+ }
532
+ onPointerMove(event) {
533
+ const geometry = this.geometry();
534
+ if (this.sparkline() || !geometry || geometry.points.length === 0) return;
535
+ const rect = this.svgRef().nativeElement.getBoundingClientRect();
536
+ const ratio = rect.width === 0 ? 1 : this.effectiveWidth() / rect.width;
537
+ const x = (event.clientX - rect.left) * ratio;
538
+ this.activeIndex.set(nearestPointIndex(geometry.points, x));
539
+ }
540
+ onPointerLeave() {
541
+ this.activeIndex.set(null);
542
+ }
543
+ onKeydown(event) {
544
+ const geometry = this.geometry();
545
+ if (this.sparkline() || !geometry || geometry.points.length === 0) return;
546
+ const navigation = navigateIndex(event.key, this.activeIndex(), geometry.points.length - 1);
547
+ if (!navigation) return;
548
+ if (navigation.preventDefault) event.preventDefault();
549
+ this.activeIndex.set(navigation.index);
550
+ }
551
+ static ɵfac = i0.ɵɵngDeclareFactory({
552
+ minVersion: "12.0.0",
553
+ version: "22.2.0",
554
+ ngImport: i0,
555
+ type: UiLineChart,
556
+ deps: [],
557
+ target: i0.ɵɵFactoryTarget.Component
558
+ });
559
+ static ɵcmp = i0.ɵɵngDeclareComponent({
560
+ minVersion: "17.0.0",
561
+ version: "22.2.0",
562
+ type: UiLineChart,
563
+ isStandalone: true,
564
+ selector: "ui-line-chart",
565
+ inputs: {
566
+ points: {
567
+ classPropertyName: "points",
568
+ publicName: "points",
569
+ isSignal: true,
570
+ isRequired: true,
571
+ transformFunction: null
572
+ },
573
+ label: {
574
+ classPropertyName: "label",
575
+ publicName: "label",
576
+ isSignal: true,
577
+ isRequired: true,
578
+ transformFunction: null
579
+ },
580
+ valueFormat: {
581
+ classPropertyName: "valueFormat",
582
+ publicName: "valueFormat",
583
+ isSignal: true,
584
+ isRequired: false,
585
+ transformFunction: null
586
+ },
587
+ tooltipFormat: {
588
+ classPropertyName: "tooltipFormat",
589
+ publicName: "tooltipFormat",
590
+ isSignal: true,
591
+ isRequired: false,
592
+ transformFunction: null
593
+ },
594
+ deltaFormat: {
595
+ classPropertyName: "deltaFormat",
596
+ publicName: "deltaFormat",
597
+ isSignal: true,
598
+ isRequired: false,
599
+ transformFunction: null
600
+ },
601
+ deltaSuffix: {
602
+ classPropertyName: "deltaSuffix",
603
+ publicName: "deltaSuffix",
604
+ isSignal: true,
605
+ isRequired: false,
606
+ transformFunction: null
607
+ },
608
+ timeFormat: {
609
+ classPropertyName: "timeFormat",
610
+ publicName: "timeFormat",
611
+ isSignal: true,
612
+ isRequired: false,
613
+ transformFunction: null
614
+ },
615
+ axisFormat: {
616
+ classPropertyName: "axisFormat",
617
+ publicName: "axisFormat",
618
+ isSignal: true,
619
+ isRequired: false,
620
+ transformFunction: null
621
+ },
622
+ startLabel: {
623
+ classPropertyName: "startLabel",
624
+ publicName: "startLabel",
625
+ isSignal: true,
626
+ isRequired: false,
627
+ transformFunction: null
628
+ },
629
+ timeColumnLabel: {
630
+ classPropertyName: "timeColumnLabel",
631
+ publicName: "timeColumnLabel",
632
+ isSignal: true,
633
+ isRequired: false,
634
+ transformFunction: null
635
+ },
636
+ valueColumnLabel: {
637
+ classPropertyName: "valueColumnLabel",
638
+ publicName: "valueColumnLabel",
639
+ isSignal: true,
640
+ isRequired: false,
641
+ transformFunction: null
642
+ },
643
+ sparkline: {
644
+ classPropertyName: "sparkline",
645
+ publicName: "sparkline",
646
+ isSignal: true,
647
+ isRequired: false,
648
+ transformFunction: null
649
+ },
650
+ tooltip: {
651
+ classPropertyName: "tooltip",
652
+ publicName: "tooltip",
653
+ isSignal: true,
654
+ isRequired: false,
655
+ transformFunction: null
656
+ },
657
+ tooltipDelta: {
658
+ classPropertyName: "tooltipDelta",
659
+ publicName: "tooltipDelta",
660
+ isSignal: true,
661
+ isRequired: false,
662
+ transformFunction: null
663
+ },
664
+ axisGap: {
665
+ classPropertyName: "axisGap",
666
+ publicName: "axisGap",
667
+ isSignal: true,
668
+ isRequired: false,
669
+ transformFunction: null
670
+ },
671
+ axisTicks: {
672
+ classPropertyName: "axisTicks",
673
+ publicName: "axisTicks",
674
+ isSignal: true,
675
+ isRequired: false,
676
+ transformFunction: null
677
+ },
678
+ rangeKey: {
679
+ classPropertyName: "rangeKey",
680
+ publicName: "rangeKey",
681
+ isSignal: true,
682
+ isRequired: false,
683
+ transformFunction: null
684
+ }
685
+ },
686
+ host: { classAttribute: "block h-full" },
687
+ viewQueries: [
688
+ {
689
+ propertyName: "svgRef",
690
+ first: true,
691
+ predicate: ["svgRef"],
692
+ descendants: true,
693
+ isSignal: true
694
+ },
695
+ {
696
+ propertyName: "lineRef",
697
+ first: true,
698
+ predicate: ["lineRef"],
699
+ descendants: true,
700
+ isSignal: true
701
+ },
702
+ {
703
+ propertyName: "endRef",
704
+ first: true,
705
+ predicate: ["endRef"],
706
+ descendants: true,
707
+ isSignal: true
708
+ }
709
+ ],
710
+ ngImport: i0,
711
+ template: `
547
712
  <div class="relative h-full">
548
713
  <svg
549
714
  #svgRef
550
- class="block h-full w-full touch-pan-y overflow-visible"
715
+ class="block h-full w-full touch-none overflow-visible"
551
716
  role="img"
552
717
  [attr.aria-label]="label()"
553
718
  [attr.height]="effectiveHeight()"
@@ -555,6 +720,8 @@ class UiLineChart {
555
720
  [attr.viewBox]="'0 0 ' + effectiveWidth() + ' ' + effectiveHeight()"
556
721
  [attr.width]="effectiveWidth()"
557
722
  (keydown)="onKeydown($event)"
723
+ (pointercancel)="onPointerLeave()"
724
+ (pointerdown)="onPointerMove($event)"
558
725
  (pointerleave)="onPointerLeave()"
559
726
  (pointermove)="onPointerMove($event)"
560
727
  >
@@ -656,7 +823,9 @@ class UiLineChart {
656
823
  {{ tooltipFormat()?.(point) ?? valueFormat()(point.v) }}
657
824
  </p>
658
825
  @if (tooltipDelta()) {
659
- <p data-chart-delta [class]="deltaClasses(point)">{{ deltaFormat()(deltaFor(point)) }}</p>
826
+ <p data-chart-delta [class]="deltaClasses(point)">
827
+ {{ deltaFormat()(deltaFor(point)) }}{{ spacedDeltaSuffix() }}
828
+ </p>
660
829
  }
661
830
  <p class="text-caption text-(--subtle-foreground)" data-chart-date>{{ timeFormat()(point.t) }}</p>
662
831
  </div>
@@ -683,17 +852,24 @@ class UiLineChart {
683
852
  </div>
684
853
  }
685
854
  </div>
686
- `, isInline: true });
687
- }
688
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: UiLineChart, decorators: [{
689
- type: Component,
690
- args: [{
691
- selector: 'ui-line-chart',
692
- template: `
855
+ `,
856
+ isInline: true
857
+ });
858
+ };
859
+ i0.ɵɵngDeclareClassMetadata({
860
+ minVersion: "12.0.0",
861
+ version: "22.2.0",
862
+ ngImport: i0,
863
+ type: UiLineChart,
864
+ decorators: [{
865
+ type: Component,
866
+ args: [{
867
+ selector: "ui-line-chart",
868
+ template: `
693
869
  <div class="relative h-full">
694
870
  <svg
695
871
  #svgRef
696
- class="block h-full w-full touch-pan-y overflow-visible"
872
+ class="block h-full w-full touch-none overflow-visible"
697
873
  role="img"
698
874
  [attr.aria-label]="label()"
699
875
  [attr.height]="effectiveHeight()"
@@ -701,6 +877,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
701
877
  [attr.viewBox]="'0 0 ' + effectiveWidth() + ' ' + effectiveHeight()"
702
878
  [attr.width]="effectiveWidth()"
703
879
  (keydown)="onKeydown($event)"
880
+ (pointercancel)="onPointerLeave()"
881
+ (pointerdown)="onPointerMove($event)"
704
882
  (pointerleave)="onPointerLeave()"
705
883
  (pointermove)="onPointerMove($event)"
706
884
  >
@@ -802,7 +980,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
802
980
  {{ tooltipFormat()?.(point) ?? valueFormat()(point.v) }}
803
981
  </p>
804
982
  @if (tooltipDelta()) {
805
- <p data-chart-delta [class]="deltaClasses(point)">{{ deltaFormat()(deltaFor(point)) }}</p>
983
+ <p data-chart-delta [class]="deltaClasses(point)">
984
+ {{ deltaFormat()(deltaFor(point)) }}{{ spacedDeltaSuffix() }}
985
+ </p>
806
986
  }
807
987
  <p class="text-caption text-(--subtle-foreground)" data-chart-date>{{ timeFormat()(point.t) }}</p>
808
988
  </div>
@@ -830,15 +1010,161 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
830
1010
  }
831
1011
  </div>
832
1012
  `,
833
- host: {
834
- class: 'block h-full',
835
- },
836
- }]
837
- }], ctorParameters: () => [], propDecorators: { points: [{ type: i0.Input, args: [{ isSignal: true, alias: "points", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], tooltipFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipFormat", required: false }] }], deltaFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "deltaFormat", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], axisFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisFormat", required: false }] }], startLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "startLabel", required: false }] }], timeColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeColumnLabel", required: false }] }], valueColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueColumnLabel", required: false }] }], sparkline: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkline", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], tooltipDelta: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDelta", required: false }] }], axisGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisGap", required: false }] }], axisTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisTicks", required: false }] }], rangeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeKey", required: false }] }], svgRef: [{ type: i0.ViewChild, args: ['svgRef', { isSignal: true }] }], lineRef: [{ type: i0.ViewChild, args: ['lineRef', { isSignal: true }] }], endRef: [{ type: i0.ViewChild, args: ['endRef', { isSignal: true }] }] } });
838
-
839
- /**
840
- * Generated bundle index. Do not edit.
841
- */
842
-
1013
+ host: { class: "block h-full" }
1014
+ }]
1015
+ }],
1016
+ ctorParameters: () => [],
1017
+ propDecorators: {
1018
+ points: [{
1019
+ type: i0.Input,
1020
+ args: [{
1021
+ isSignal: true,
1022
+ alias: "points",
1023
+ required: true
1024
+ }]
1025
+ }],
1026
+ label: [{
1027
+ type: i0.Input,
1028
+ args: [{
1029
+ isSignal: true,
1030
+ alias: "label",
1031
+ required: true
1032
+ }]
1033
+ }],
1034
+ valueFormat: [{
1035
+ type: i0.Input,
1036
+ args: [{
1037
+ isSignal: true,
1038
+ alias: "valueFormat",
1039
+ required: false
1040
+ }]
1041
+ }],
1042
+ tooltipFormat: [{
1043
+ type: i0.Input,
1044
+ args: [{
1045
+ isSignal: true,
1046
+ alias: "tooltipFormat",
1047
+ required: false
1048
+ }]
1049
+ }],
1050
+ deltaFormat: [{
1051
+ type: i0.Input,
1052
+ args: [{
1053
+ isSignal: true,
1054
+ alias: "deltaFormat",
1055
+ required: false
1056
+ }]
1057
+ }],
1058
+ deltaSuffix: [{
1059
+ type: i0.Input,
1060
+ args: [{
1061
+ isSignal: true,
1062
+ alias: "deltaSuffix",
1063
+ required: false
1064
+ }]
1065
+ }],
1066
+ timeFormat: [{
1067
+ type: i0.Input,
1068
+ args: [{
1069
+ isSignal: true,
1070
+ alias: "timeFormat",
1071
+ required: false
1072
+ }]
1073
+ }],
1074
+ axisFormat: [{
1075
+ type: i0.Input,
1076
+ args: [{
1077
+ isSignal: true,
1078
+ alias: "axisFormat",
1079
+ required: false
1080
+ }]
1081
+ }],
1082
+ startLabel: [{
1083
+ type: i0.Input,
1084
+ args: [{
1085
+ isSignal: true,
1086
+ alias: "startLabel",
1087
+ required: false
1088
+ }]
1089
+ }],
1090
+ timeColumnLabel: [{
1091
+ type: i0.Input,
1092
+ args: [{
1093
+ isSignal: true,
1094
+ alias: "timeColumnLabel",
1095
+ required: false
1096
+ }]
1097
+ }],
1098
+ valueColumnLabel: [{
1099
+ type: i0.Input,
1100
+ args: [{
1101
+ isSignal: true,
1102
+ alias: "valueColumnLabel",
1103
+ required: false
1104
+ }]
1105
+ }],
1106
+ sparkline: [{
1107
+ type: i0.Input,
1108
+ args: [{
1109
+ isSignal: true,
1110
+ alias: "sparkline",
1111
+ required: false
1112
+ }]
1113
+ }],
1114
+ tooltip: [{
1115
+ type: i0.Input,
1116
+ args: [{
1117
+ isSignal: true,
1118
+ alias: "tooltip",
1119
+ required: false
1120
+ }]
1121
+ }],
1122
+ tooltipDelta: [{
1123
+ type: i0.Input,
1124
+ args: [{
1125
+ isSignal: true,
1126
+ alias: "tooltipDelta",
1127
+ required: false
1128
+ }]
1129
+ }],
1130
+ axisGap: [{
1131
+ type: i0.Input,
1132
+ args: [{
1133
+ isSignal: true,
1134
+ alias: "axisGap",
1135
+ required: false
1136
+ }]
1137
+ }],
1138
+ axisTicks: [{
1139
+ type: i0.Input,
1140
+ args: [{
1141
+ isSignal: true,
1142
+ alias: "axisTicks",
1143
+ required: false
1144
+ }]
1145
+ }],
1146
+ rangeKey: [{
1147
+ type: i0.Input,
1148
+ args: [{
1149
+ isSignal: true,
1150
+ alias: "rangeKey",
1151
+ required: false
1152
+ }]
1153
+ }],
1154
+ svgRef: [{
1155
+ type: i0.ViewChild,
1156
+ args: ["svgRef", { isSignal: true }]
1157
+ }],
1158
+ lineRef: [{
1159
+ type: i0.ViewChild,
1160
+ args: ["lineRef", { isSignal: true }]
1161
+ }],
1162
+ endRef: [{
1163
+ type: i0.ViewChild,
1164
+ args: ["endRef", { isSignal: true }]
1165
+ }]
1166
+ }
1167
+ });
843
1168
  export { UiLineChart };
844
- //# sourceMappingURL=joanroucoux-cairn-ui-line-chart.mjs.map
1169
+
1170
+ //# sourceMappingURL=joanroucoux-cairn-ui-line-chart.mjs.map