@omnitend/dashboard-for-laravel 0.33.1 → 0.34.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 (50) hide show
  1. package/dist/charts.js +82 -90
  2. package/dist/charts.js.map +1 -1
  3. package/dist/charts.umd.cjs +1 -1
  4. package/dist/charts.umd.cjs.map +1 -1
  5. package/dist/components/base/DButton.vue.d.ts +1 -1
  6. package/dist/components/charts/DXBarChart.vue.d.ts +3 -18
  7. package/dist/components/charts/DXLineChart.vue.d.ts +3 -18
  8. package/dist/components/charts/useThemedChart.d.ts +35 -0
  9. package/dist/components/extended/DXChoiceField.vue.d.ts +26 -0
  10. package/dist/components/extended/DXDashboardSidebar.vue.d.ts +1 -1
  11. package/dist/components/extended/DXField.vue.d.ts +10 -113
  12. package/dist/components/extended/DXFieldShell.vue.d.ts +73 -0
  13. package/dist/components/extended/DXNumericField.vue.d.ts +23 -0
  14. package/dist/components/extended/DXRepeater.vue.d.ts +4 -4
  15. package/dist/components/extended/DXRepeaterCards.vue.d.ts +45 -0
  16. package/dist/components/extended/DXTable.vue.d.ts +69 -2
  17. package/dist/components/extended/DXTableEditorModal.vue.d.ts +17 -2
  18. package/dist/components/extended/DXUserAvatar.vue.d.ts +1 -1
  19. package/dist/composables/useAsyncOptions.d.ts +20 -0
  20. package/dist/composables/useResourceEditor.d.ts +2 -1
  21. package/dist/dashboard-for-laravel.js +9982 -10052
  22. package/dist/dashboard-for-laravel.js.map +1 -1
  23. package/dist/dashboard-for-laravel.umd.cjs +7 -7
  24. package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
  25. package/dist/index.d.ts +3 -2
  26. package/dist/style.css +1 -1
  27. package/dist/types/index.d.ts +146 -0
  28. package/dist/utils/formSchema.d.ts +59 -0
  29. package/docs/public/api-reference.json +458 -30
  30. package/docs/public/docs-map.md +1 -1
  31. package/docs/public/llms.txt +5 -2
  32. package/package.json +1 -1
  33. package/resources/js/components/charts/DXBarChart.vue +4 -43
  34. package/resources/js/components/charts/DXLineChart.vue +4 -43
  35. package/resources/js/components/charts/useThemedChart.ts +69 -0
  36. package/resources/js/components/extended/DXChoiceField.vue +114 -0
  37. package/resources/js/components/extended/DXField.vue +328 -615
  38. package/resources/js/components/extended/DXFieldShell.vue +124 -0
  39. package/resources/js/components/extended/DXForm.vue +9 -5
  40. package/resources/js/components/extended/DXNumericField.vue +144 -0
  41. package/resources/js/components/extended/DXRepeater.vue +53 -175
  42. package/resources/js/components/extended/DXRepeaterCards.vue +133 -0
  43. package/resources/js/components/extended/DXTable.vue +202 -32
  44. package/resources/js/components/extended/DXTableEditorModal.vue +34 -2
  45. package/resources/js/composables/defineForm.ts +8 -44
  46. package/resources/js/composables/useAsyncOptions.ts +61 -0
  47. package/resources/js/composables/useResourceEditor.ts +24 -38
  48. package/resources/js/index.ts +33 -0
  49. package/resources/js/types/index.ts +231 -0
  50. package/resources/js/utils/formSchema.ts +126 -0
package/dist/charts.js CHANGED
@@ -1,18 +1,18 @@
1
- import { defineComponent as m, computed as i, openBlock as f, createElementBlock as b, createVNode as h, unref as g } from "vue";
2
- import { Bar as V, Line as O, Doughnut as P } from "vue-chartjs";
3
- import { Chart as R, registerables as X } from "chart.js";
1
+ import { computed as m, defineComponent as f, openBlock as p, createElementBlock as h, createVNode as g, unref as u } from "vue";
2
+ import { Bar as V, Line as P, Doughnut as R } from "vue-chartjs";
3
+ import { Chart as X, registerables as E } from "chart.js";
4
4
  const D = (o) => !!o && typeof o == "object" && !Array.isArray(o);
5
- function p(o, e) {
5
+ function b(o, e) {
6
6
  const t = { ...o };
7
- for (const [a, n] of Object.entries(e))
8
- D(n) && D(t[a]) ? t[a] = p(t[a], n) : t[a] = n;
7
+ for (const [r, n] of Object.entries(e))
8
+ D(n) && D(t[r]) ? t[r] = b(t[r], n) : t[r] = n;
9
9
  return t;
10
10
  }
11
- let L = !1;
11
+ let S = !1;
12
12
  function y() {
13
- L || (R.register(...X), L = !0);
13
+ S || (X.register(...E), S = !0);
14
14
  }
15
- const A = [
15
+ const w = [
16
16
  ["--dx-chart-1", "#2563eb"],
17
17
  // blue
18
18
  ["--dx-chart-2", "#65a30d"],
@@ -30,85 +30,105 @@ const A = [
30
30
  ["--dx-chart-8", "#db2777"]
31
31
  // pink
32
32
  ];
33
- function E() {
33
+ function N() {
34
34
  if (typeof document > "u" || typeof getComputedStyle > "u")
35
- return A.map(([, e]) => e);
35
+ return w.map(([, e]) => e);
36
36
  const o = getComputedStyle(document.documentElement);
37
- return A.map(([e, t]) => o.getPropertyValue(e).trim() || t);
37
+ return w.map(([e, t]) => o.getPropertyValue(e).trim() || t);
38
38
  }
39
- function F(o, e) {
39
+ function k(o, e) {
40
40
  return typeof document > "u" || typeof getComputedStyle > "u" ? e : getComputedStyle(document.documentElement).getPropertyValue(o).trim() || e;
41
41
  }
42
- function B(o, e) {
42
+ function L(o, e) {
43
43
  const t = o.replace(/^#/, "");
44
44
  if (/^[0-9a-f]{6}$/i.test(t)) {
45
45
  const n = parseInt(t.slice(0, 2), 16), s = parseInt(t.slice(2, 4), 16), l = parseInt(t.slice(4, 6), 16);
46
46
  return `rgba(${n}, ${s}, ${l}, ${e})`;
47
47
  }
48
- const a = o.match(/^rgba?\(([^)]+)\)$/);
49
- if (a) {
50
- const [n, s, l] = a[1].split(",").map((r) => r.trim());
48
+ const r = o.match(/^rgba?\(([^)]+)\)$/);
49
+ if (r) {
50
+ const [n, s, l] = r[1].split(",").map((a) => a.trim());
51
51
  return `rgba(${n}, ${s}, ${l}, ${e})`;
52
52
  }
53
53
  return o;
54
54
  }
55
- function k(o, e, t, a) {
56
- const n = new Intl.NumberFormat(a, {
55
+ function A(o, e, t, r) {
56
+ const n = new Intl.NumberFormat(r, {
57
57
  maximumFractionDigits: 2
58
58
  }).format(o);
59
59
  return e === "currency" ? `${t}${n}` : e === "percent" ? `${n}%` : n;
60
60
  }
61
- function v(o) {
61
+ function F(o) {
62
62
  const {
63
63
  valueFormat: e,
64
64
  currencySymbol: t = "£",
65
- locale: a,
65
+ locale: r,
66
66
  showLegend: n = !1,
67
67
  hasValueAxis: s = !0
68
- } = o, l = B(F("--bs-border-color", "#dee2e6"), 0.6), r = F("--bs-secondary-color", "#6c757d"), u = {
68
+ } = o, l = L(k("--bs-border-color", "#dee2e6"), 0.6), a = k("--bs-secondary-color", "#6c757d"), i = {
69
69
  responsive: !0,
70
70
  maintainAspectRatio: !1,
71
71
  plugins: {
72
- legend: { display: n, labels: { color: r } },
72
+ legend: { display: n, labels: { color: a } },
73
73
  tooltip: {
74
74
  callbacks: {
75
75
  label: (c) => {
76
- var S, w;
77
- const d = ((S = c.parsed) == null ? void 0 : S.y) ?? c.parsed ?? c.raw, C = typeof d == "number" ? d : Number(d), x = Number.isFinite(C) ? k(C, e, t, a) : String(d), $ = ((w = c.dataset) == null ? void 0 : w.label) ?? c.label ?? "";
78
- return $ ? `${$}: ${x}` : x;
76
+ var $, x;
77
+ const d = (($ = c.parsed) == null ? void 0 : $.y) ?? c.parsed ?? c.raw, v = typeof d == "number" ? d : Number(d), _ = Number.isFinite(v) ? A(v, e, t, r) : String(d), C = ((x = c.dataset) == null ? void 0 : x.label) ?? c.label ?? "";
78
+ return C ? `${C}: ${_}` : _;
79
79
  }
80
80
  }
81
81
  }
82
82
  }
83
83
  };
84
- return s && (u.scales = {
84
+ return s && (i.scales = {
85
85
  x: {
86
86
  grid: { display: !1 },
87
87
  border: { color: l },
88
- ticks: { color: r }
88
+ ticks: { color: a }
89
89
  },
90
90
  y: {
91
91
  beginAtZero: !0,
92
92
  grid: { color: l, drawBorder: !1 },
93
93
  border: { display: !1 },
94
94
  ticks: {
95
- color: r,
96
- callback: (c) => k(Number(c), e, t, a)
95
+ color: a,
96
+ callback: (c) => A(Number(c), e, t, r)
97
97
  }
98
98
  }
99
- }), u;
99
+ }), i;
100
100
  }
101
- function _(o, e, t) {
102
- const a = E(), n = o.length === 1;
101
+ function B(o, e, t) {
102
+ const r = N(), n = o.length === 1;
103
103
  return o.map((s, l) => {
104
- const r = { ...s }, u = a[l % a.length];
105
- return e === "doughnut" ? (r.backgroundColor === void 0 && (r.backgroundColor = Array.from(
104
+ const a = { ...s }, i = r[l % r.length];
105
+ return e === "doughnut" ? (a.backgroundColor === void 0 && (a.backgroundColor = Array.from(
106
106
  { length: t },
107
- (c, d) => a[d % a.length]
108
- )), r) : e === "line" ? (r.borderColor === void 0 && (r.borderColor = u), r.backgroundColor === void 0 && (r.backgroundColor = B(u, 0.15)), r.tension === void 0 && (r.tension = 0.3), r.fill === void 0 && (r.fill = !0), r.pointRadius === void 0 && (r.pointRadius = 2), r) : (r.backgroundColor === void 0 && (r.backgroundColor = n && t > 1 ? Array.from({ length: t }, () => u) : u), r.borderRadius === void 0 && (r.borderRadius = 4), r);
107
+ (c, d) => r[d % r.length]
108
+ )), a) : e === "line" ? (a.borderColor === void 0 && (a.borderColor = i), a.backgroundColor === void 0 && (a.backgroundColor = L(i, 0.15)), a.tension === void 0 && (a.tension = 0.3), a.fill === void 0 && (a.fill = !0), a.pointRadius === void 0 && (a.pointRadius = 2), a) : (a.backgroundColor === void 0 && (a.backgroundColor = n && t > 1 ? Array.from({ length: t }, () => i) : i), a.borderRadius === void 0 && (a.borderRadius = 4), a);
109
109
  });
110
110
  }
111
- const N = { class: "dx-chart" }, q = /* @__PURE__ */ m({
111
+ function O(o, e) {
112
+ const t = m(
113
+ () => ({
114
+ labels: e.labels,
115
+ datasets: B(e.datasets, o, e.labels.length)
116
+ })
117
+ ), r = m(
118
+ () => b(
119
+ F({
120
+ valueFormat: e.valueFormat,
121
+ currencySymbol: e.currencySymbol,
122
+ locale: e.locale,
123
+ showLegend: e.showLegend ?? e.datasets.length > 1,
124
+ hasValueAxis: !0
125
+ }),
126
+ e.options ?? {}
127
+ )
128
+ );
129
+ return { chartData: t, mergedOptions: r };
130
+ }
131
+ const I = { class: "dx-chart" }, z = /* @__PURE__ */ f({
112
132
  __name: "DXBarChart",
113
133
  props: {
114
134
  labels: {},
@@ -121,29 +141,15 @@ const N = { class: "dx-chart" }, q = /* @__PURE__ */ m({
121
141
  },
122
142
  setup(o) {
123
143
  y();
124
- const e = o, t = i(() => ({
125
- labels: e.labels,
126
- datasets: _(e.datasets, "bar", e.labels.length)
127
- })), a = i(
128
- () => p(
129
- v({
130
- valueFormat: e.valueFormat,
131
- currencySymbol: e.currencySymbol,
132
- locale: e.locale,
133
- showLegend: e.showLegend ?? e.datasets.length > 1,
134
- hasValueAxis: !0
135
- }),
136
- e.options ?? {}
137
- )
138
- );
139
- return (n, s) => (f(), b("div", N, [
140
- h(g(V), {
141
- data: t.value,
142
- options: a.value
144
+ const e = o, { chartData: t, mergedOptions: r } = O("bar", e);
145
+ return (n, s) => (p(), h("div", I, [
146
+ g(u(V), {
147
+ data: u(t),
148
+ options: u(r)
143
149
  }, null, 8, ["data", "options"])
144
150
  ]));
145
151
  }
146
- }), I = { class: "dx-chart" }, z = /* @__PURE__ */ m({
152
+ }), j = { class: "dx-chart" }, G = /* @__PURE__ */ f({
147
153
  __name: "DXLineChart",
148
154
  props: {
149
155
  labels: {},
@@ -156,29 +162,15 @@ const N = { class: "dx-chart" }, q = /* @__PURE__ */ m({
156
162
  },
157
163
  setup(o) {
158
164
  y();
159
- const e = o, t = i(() => ({
160
- labels: e.labels,
161
- datasets: _(e.datasets, "line", e.labels.length)
162
- })), a = i(
163
- () => p(
164
- v({
165
- valueFormat: e.valueFormat,
166
- currencySymbol: e.currencySymbol,
167
- locale: e.locale,
168
- showLegend: e.showLegend ?? e.datasets.length > 1,
169
- hasValueAxis: !0
170
- }),
171
- e.options ?? {}
172
- )
173
- );
174
- return (n, s) => (f(), b("div", I, [
175
- h(g(O), {
176
- data: t.value,
177
- options: a.value
165
+ const e = o, { chartData: t, mergedOptions: r } = O("line", e);
166
+ return (n, s) => (p(), h("div", j, [
167
+ g(u(P), {
168
+ data: u(t),
169
+ options: u(r)
178
170
  }, null, 8, ["data", "options"])
179
171
  ]));
180
172
  }
181
- }), j = { class: "dx-chart" }, G = /* @__PURE__ */ m({
173
+ }), T = { class: "dx-chart" }, H = /* @__PURE__ */ f({
182
174
  __name: "DXDoughnutChart",
183
175
  props: {
184
176
  labels: {},
@@ -192,14 +184,14 @@ const N = { class: "dx-chart" }, q = /* @__PURE__ */ m({
192
184
  },
193
185
  setup(o) {
194
186
  y();
195
- const e = o, t = i(
187
+ const e = o, t = m(
196
188
  () => e.datasets ?? [{ data: e.data ?? [] }]
197
- ), a = i(() => ({
189
+ ), r = m(() => ({
198
190
  labels: e.labels,
199
- datasets: _(t.value, "doughnut", e.labels.length)
200
- })), n = i(
201
- () => p(
202
- v({
191
+ datasets: B(t.value, "doughnut", e.labels.length)
192
+ })), n = m(
193
+ () => b(
194
+ F({
203
195
  valueFormat: e.valueFormat,
204
196
  currencySymbol: e.currencySymbol,
205
197
  locale: e.locale,
@@ -209,17 +201,17 @@ const N = { class: "dx-chart" }, q = /* @__PURE__ */ m({
209
201
  e.options ?? {}
210
202
  )
211
203
  );
212
- return (s, l) => (f(), b("div", j, [
213
- h(g(P), {
214
- data: a.value,
204
+ return (s, l) => (p(), h("div", T, [
205
+ g(u(R), {
206
+ data: r.value,
215
207
  options: n.value
216
208
  }, null, 8, ["data", "options"])
217
209
  ]));
218
210
  }
219
211
  });
220
212
  export {
221
- q as DXBarChart,
222
- G as DXDoughnutChart,
223
- z as DXLineChart
213
+ z as DXBarChart,
214
+ H as DXDoughnutChart,
215
+ G as DXLineChart
224
216
  };
225
217
  //# sourceMappingURL=charts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"charts.js","sources":["../resources/js/components/charts/chartTheme.ts","../resources/js/components/charts/DXBarChart.vue","../resources/js/components/charts/DXLineChart.vue","../resources/js/components/charts/DXDoughnutChart.vue"],"sourcesContent":["/**\n * Shared theming for the DX*Chart wrappers: the dedicated categorical chart\n * palette (--dx-chart-* from the live theme), dashboard-friendly default options (no gridlines /\n * legend clutter, responsive, formatted ticks), and a one-time Chart.js\n * registration. chart.js / vue-chartjs are OPTIONAL peer deps.\n */\nimport { Chart, registerables } from \"chart.js\";\n\nexport type ChartValueFormat = \"number\" | \"currency\" | \"percent\";\n\nconst isPlainObject = (value: unknown): value is Record<string, any> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\n/** Deep-merge consumer `options` over the themed defaults (arrays replace). */\nexport function mergeOptions(\n base: Record<string, any>,\n override: Record<string, any>,\n): Record<string, any> {\n const result: Record<string, any> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = mergeOptions(result[key], value);\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nlet registered = false;\n\n/**\n * Register Chart.js (idempotent). Uses the full `registerables` set so every\n * controller / scale / element / plugin (incl. Legend, Tooltip, Filler) is\n * available — chart.js is an external peer dep, so this adds no bundle weight.\n */\nexport function registerCharts(): void {\n if (registered) return;\n Chart.register(...registerables);\n registered = true;\n}\n\n// The dedicated data-viz palette (`$dx-chart-palette` in theme.scss) — eight\n// vivid hues in a fixed, CVD-validated order, published as --dx-chart-1..8.\n// Deliberately NOT the semantic --bs-* colours (#141): the base theme colours\n// are dark AA \"emphasis\" shades (too muted for series), and status colours\n// shouldn't impersonate \"series 2\". Fallbacks mirror the shipped theme so\n// charts still render without the CSS (SSR / tests / a consumer who didn't\n// import it). Sync with theme.scss's $dx-chart-palette is enforced by a test\n// that parses the Sass source (charts.test.ts), which is why this is exported.\nexport const PALETTE_VARS: Array<[string, string]> = [\n [\"--dx-chart-1\", \"#2563eb\"], // blue\n [\"--dx-chart-2\", \"#65a30d\"], // lime\n [\"--dx-chart-3\", \"#7c3aed\"], // violet\n [\"--dx-chart-4\", \"#0d9488\"], // teal\n [\"--dx-chart-5\", \"#ea580c\"], // orange\n [\"--dx-chart-6\", \"#0891b2\"], // cyan\n [\"--dx-chart-7\", \"#d97706\"], // amber\n [\"--dx-chart-8\", \"#db2777\"], // pink\n];\n\n/** Resolve the themed categorical palette from the document root. */\nexport function getPalette(): string[] {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return PALETTE_VARS.map(([, fallback]) => fallback);\n }\n const styles = getComputedStyle(document.documentElement);\n return PALETTE_VARS.map(([name, fallback]) => {\n const value = styles.getPropertyValue(name).trim();\n return value || fallback;\n });\n}\n\n/** Read a single theme colour (e.g. the body text colour) with a fallback. */\nfunction themeColor(name: string, fallback: string): string {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return fallback;\n }\n const value = getComputedStyle(document.documentElement)\n .getPropertyValue(name)\n .trim();\n return value || fallback;\n}\n\n/** Add an alpha channel to a hex or rgb() colour (best-effort). */\nexport function withAlpha(color: string, alpha: number): string {\n const hex = color.replace(/^#/, \"\");\n if (/^[0-9a-f]{6}$/i.test(hex)) {\n const r = parseInt(hex.slice(0, 2), 16);\n const g = parseInt(hex.slice(2, 4), 16);\n const b = parseInt(hex.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n const rgbMatch = color.match(/^rgba?\\(([^)]+)\\)$/);\n if (rgbMatch) {\n const [r, g, b] = rgbMatch[1].split(\",\").map((n) => n.trim());\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return color;\n}\n\n/** Format a numeric value per the chart's value format (for tick/tooltips). */\nexport function formatValue(\n value: number,\n format: ChartValueFormat | undefined,\n currencySymbol: string,\n locale?: string,\n): string {\n const num = new Intl.NumberFormat(locale, {\n maximumFractionDigits: 2,\n }).format(value);\n if (format === \"currency\") return `${currencySymbol}${num}`;\n if (format === \"percent\") return `${num}%`;\n return num;\n}\n\nexport interface BaseOptionArgs {\n valueFormat?: ChartValueFormat;\n currencySymbol?: string;\n locale?: string;\n /** Show the legend (default: false for a single dataset, true for many). */\n showLegend?: boolean;\n /** Whether the chart has value axes (bar/line) vs none (doughnut). */\n hasValueAxis?: boolean;\n}\n\n/** Dashboard default Chart.js options merged under any consumer `options`. */\nexport function baseOptions(args: BaseOptionArgs): Record<string, any> {\n const {\n valueFormat,\n currencySymbol = \"£\",\n locale,\n showLegend = false,\n hasValueAxis = true,\n } = args;\n\n const gridColor = withAlpha(themeColor(\"--bs-border-color\", \"#dee2e6\"), 0.6);\n const tickColor = themeColor(\"--bs-secondary-color\", \"#6c757d\");\n\n const options: Record<string, any> = {\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n legend: { display: showLegend, labels: { color: tickColor } },\n tooltip: {\n callbacks: {\n label: (ctx: any) => {\n const raw = ctx.parsed?.y ?? ctx.parsed ?? ctx.raw;\n const value = typeof raw === \"number\" ? raw : Number(raw);\n const formatted = Number.isFinite(value)\n ? formatValue(value, valueFormat, currencySymbol, locale)\n : String(raw);\n const label = ctx.dataset?.label ?? ctx.label ?? \"\";\n return label ? `${label}: ${formatted}` : formatted;\n },\n },\n },\n },\n };\n\n if (hasValueAxis) {\n options.scales = {\n x: {\n grid: { display: false },\n border: { color: gridColor },\n ticks: { color: tickColor },\n },\n y: {\n beginAtZero: true,\n grid: { color: gridColor, drawBorder: false },\n border: { display: false },\n ticks: {\n color: tickColor,\n callback: (value: number | string) =>\n formatValue(Number(value), valueFormat, currencySymbol, locale),\n },\n },\n };\n }\n\n return options;\n}\n\n/**\n * Apply the themed palette to any dataset that doesn't set its own colours.\n * Bar: one colour per dataset (or per bar for a single dataset). Line: a themed\n * stroke + translucent fill. Doughnut/pie: one colour per slice.\n */\nexport function applyPalette(\n datasets: any[],\n kind: \"bar\" | \"line\" | \"doughnut\",\n labelsCount: number,\n): any[] {\n const palette = getPalette();\n const single = datasets.length === 1;\n\n return datasets.map((dataset, index) => {\n const next = { ...dataset };\n const color = palette[index % palette.length];\n\n if (kind === \"doughnut\") {\n if (next.backgroundColor === undefined) {\n next.backgroundColor = Array.from(\n { length: labelsCount },\n (_, i) => palette[i % palette.length],\n );\n }\n return next;\n }\n\n if (kind === \"line\") {\n if (next.borderColor === undefined) next.borderColor = color;\n if (next.backgroundColor === undefined) {\n next.backgroundColor = withAlpha(color, 0.15);\n }\n if (next.tension === undefined) next.tension = 0.3;\n if (next.fill === undefined) next.fill = true;\n if (next.pointRadius === undefined) next.pointRadius = 2;\n return next;\n }\n\n // bar\n if (next.backgroundColor === undefined) {\n next.backgroundColor =\n single && labelsCount > 1\n ? Array.from({ length: labelsCount }, () => color)\n : color;\n }\n if (next.borderRadius === undefined) next.borderRadius = 4;\n return next;\n });\n}\n","<!--\n @component\n A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette (--dx-chart-1..8) applied and dashboard-friendly defaults (no x gridlines,\n formatted value ticks, responsive, legend hidden for a single series). Pass\n `labels` + `datasets`; `options` deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Bar :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Bar } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** X-axis category labels. */\n labels: string[];\n /** Chart.js datasets. Colours are themed when a dataset omits them. */\n datasets: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format value-axis ticks and tooltips. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: only when there is more than one dataset). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(props.datasets, \"bar\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? props.datasets.length > 1,\n hasValueAxis: true,\n }),\n props.options ?? {},\n ),\n);\n</script>\n","<!--\n @component\n A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette applied (--dx-chart-1..8 stroke + translucent fill, smoothed line)\n and dashboard-friendly defaults. Pass `labels` + `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Line :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Line } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** X-axis category labels. */\n labels: string[];\n /** Chart.js datasets. Colours are themed when a dataset omits them. */\n datasets: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format value-axis ticks and tooltips. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: only when there is more than one dataset). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(props.datasets, \"line\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? props.datasets.length > 1,\n hasValueAxis: true,\n }),\n props.options ?? {},\n ),\n);\n</script>\n","<!--\n @component\n A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one\n chart-palette colour (--dx-chart-1..8) per slice and dashboard-friendly defaults. Pass\n `labels` + either `data` (a number array) or full `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Doughnut :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Doughnut } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** Slice labels. */\n labels: string[];\n /** Convenience: a single number array (one value per label). */\n data?: number[];\n /** Full Chart.js datasets (alternative to `data`). */\n datasets?: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format tooltip values. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: true — slices need identifying). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? true` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst resolvedDatasets = computed(() =>\n props.datasets ?? [{ data: props.data ?? [] }],\n);\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(resolvedDatasets.value, \"doughnut\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? true,\n hasValueAxis: false,\n }),\n props.options ?? {},\n ),\n);\n</script>\n"],"names":["isPlainObject","value","mergeOptions","base","override","result","key","registered","registerCharts","Chart","registerables","PALETTE_VARS","getPalette","fallback","styles","name","themeColor","withAlpha","color","alpha","hex","r","g","b","rgbMatch","n","formatValue","format","currencySymbol","locale","num","baseOptions","args","valueFormat","showLegend","hasValueAxis","gridColor","tickColor","options","ctx","raw","_a","formatted","label","_b","applyPalette","datasets","kind","labelsCount","palette","single","dataset","index","next","_","i","props","__props","chartData","computed","mergedOptions","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","Bar","Line","resolvedDatasets","Doughnut"],"mappings":"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"}
1
+ {"version":3,"file":"charts.js","sources":["../resources/js/components/charts/chartTheme.ts","../resources/js/components/charts/useThemedChart.ts","../resources/js/components/charts/DXBarChart.vue","../resources/js/components/charts/DXLineChart.vue","../resources/js/components/charts/DXDoughnutChart.vue"],"sourcesContent":["/**\n * Shared theming for the DX*Chart wrappers: the dedicated categorical chart\n * palette (--dx-chart-* from the live theme), dashboard-friendly default options (no gridlines /\n * legend clutter, responsive, formatted ticks), and a one-time Chart.js\n * registration. chart.js / vue-chartjs are OPTIONAL peer deps.\n */\nimport { Chart, registerables } from \"chart.js\";\n\nexport type ChartValueFormat = \"number\" | \"currency\" | \"percent\";\n\nconst isPlainObject = (value: unknown): value is Record<string, any> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\n/** Deep-merge consumer `options` over the themed defaults (arrays replace). */\nexport function mergeOptions(\n base: Record<string, any>,\n override: Record<string, any>,\n): Record<string, any> {\n const result: Record<string, any> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = mergeOptions(result[key], value);\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nlet registered = false;\n\n/**\n * Register Chart.js (idempotent). Uses the full `registerables` set so every\n * controller / scale / element / plugin (incl. Legend, Tooltip, Filler) is\n * available — chart.js is an external peer dep, so this adds no bundle weight.\n */\nexport function registerCharts(): void {\n if (registered) return;\n Chart.register(...registerables);\n registered = true;\n}\n\n// The dedicated data-viz palette (`$dx-chart-palette` in theme.scss) — eight\n// vivid hues in a fixed, CVD-validated order, published as --dx-chart-1..8.\n// Deliberately NOT the semantic --bs-* colours (#141): the base theme colours\n// are dark AA \"emphasis\" shades (too muted for series), and status colours\n// shouldn't impersonate \"series 2\". Fallbacks mirror the shipped theme so\n// charts still render without the CSS (SSR / tests / a consumer who didn't\n// import it). Sync with theme.scss's $dx-chart-palette is enforced by a test\n// that parses the Sass source (charts.test.ts), which is why this is exported.\nexport const PALETTE_VARS: Array<[string, string]> = [\n [\"--dx-chart-1\", \"#2563eb\"], // blue\n [\"--dx-chart-2\", \"#65a30d\"], // lime\n [\"--dx-chart-3\", \"#7c3aed\"], // violet\n [\"--dx-chart-4\", \"#0d9488\"], // teal\n [\"--dx-chart-5\", \"#ea580c\"], // orange\n [\"--dx-chart-6\", \"#0891b2\"], // cyan\n [\"--dx-chart-7\", \"#d97706\"], // amber\n [\"--dx-chart-8\", \"#db2777\"], // pink\n];\n\n/** Resolve the themed categorical palette from the document root. */\nexport function getPalette(): string[] {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return PALETTE_VARS.map(([, fallback]) => fallback);\n }\n const styles = getComputedStyle(document.documentElement);\n return PALETTE_VARS.map(([name, fallback]) => {\n const value = styles.getPropertyValue(name).trim();\n return value || fallback;\n });\n}\n\n/** Read a single theme colour (e.g. the body text colour) with a fallback. */\nfunction themeColor(name: string, fallback: string): string {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return fallback;\n }\n const value = getComputedStyle(document.documentElement)\n .getPropertyValue(name)\n .trim();\n return value || fallback;\n}\n\n/** Add an alpha channel to a hex or rgb() colour (best-effort). */\nexport function withAlpha(color: string, alpha: number): string {\n const hex = color.replace(/^#/, \"\");\n if (/^[0-9a-f]{6}$/i.test(hex)) {\n const r = parseInt(hex.slice(0, 2), 16);\n const g = parseInt(hex.slice(2, 4), 16);\n const b = parseInt(hex.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n const rgbMatch = color.match(/^rgba?\\(([^)]+)\\)$/);\n if (rgbMatch) {\n const [r, g, b] = rgbMatch[1].split(\",\").map((n) => n.trim());\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return color;\n}\n\n/** Format a numeric value per the chart's value format (for tick/tooltips). */\nexport function formatValue(\n value: number,\n format: ChartValueFormat | undefined,\n currencySymbol: string,\n locale?: string,\n): string {\n const num = new Intl.NumberFormat(locale, {\n maximumFractionDigits: 2,\n }).format(value);\n if (format === \"currency\") return `${currencySymbol}${num}`;\n if (format === \"percent\") return `${num}%`;\n return num;\n}\n\nexport interface BaseOptionArgs {\n valueFormat?: ChartValueFormat;\n currencySymbol?: string;\n locale?: string;\n /** Show the legend (default: false for a single dataset, true for many). */\n showLegend?: boolean;\n /** Whether the chart has value axes (bar/line) vs none (doughnut). */\n hasValueAxis?: boolean;\n}\n\n/** Dashboard default Chart.js options merged under any consumer `options`. */\nexport function baseOptions(args: BaseOptionArgs): Record<string, any> {\n const {\n valueFormat,\n currencySymbol = \"£\",\n locale,\n showLegend = false,\n hasValueAxis = true,\n } = args;\n\n const gridColor = withAlpha(themeColor(\"--bs-border-color\", \"#dee2e6\"), 0.6);\n const tickColor = themeColor(\"--bs-secondary-color\", \"#6c757d\");\n\n const options: Record<string, any> = {\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n legend: { display: showLegend, labels: { color: tickColor } },\n tooltip: {\n callbacks: {\n label: (ctx: any) => {\n const raw = ctx.parsed?.y ?? ctx.parsed ?? ctx.raw;\n const value = typeof raw === \"number\" ? raw : Number(raw);\n const formatted = Number.isFinite(value)\n ? formatValue(value, valueFormat, currencySymbol, locale)\n : String(raw);\n const label = ctx.dataset?.label ?? ctx.label ?? \"\";\n return label ? `${label}: ${formatted}` : formatted;\n },\n },\n },\n },\n };\n\n if (hasValueAxis) {\n options.scales = {\n x: {\n grid: { display: false },\n border: { color: gridColor },\n ticks: { color: tickColor },\n },\n y: {\n beginAtZero: true,\n grid: { color: gridColor, drawBorder: false },\n border: { display: false },\n ticks: {\n color: tickColor,\n callback: (value: number | string) =>\n formatValue(Number(value), valueFormat, currencySymbol, locale),\n },\n },\n };\n }\n\n return options;\n}\n\n/**\n * Apply the themed palette to any dataset that doesn't set its own colours.\n * Bar: one colour per dataset (or per bar for a single dataset). Line: a themed\n * stroke + translucent fill. Doughnut/pie: one colour per slice.\n */\nexport function applyPalette(\n datasets: any[],\n kind: \"bar\" | \"line\" | \"doughnut\",\n labelsCount: number,\n): any[] {\n const palette = getPalette();\n const single = datasets.length === 1;\n\n return datasets.map((dataset, index) => {\n const next = { ...dataset };\n const color = palette[index % palette.length];\n\n if (kind === \"doughnut\") {\n if (next.backgroundColor === undefined) {\n next.backgroundColor = Array.from(\n { length: labelsCount },\n (_, i) => palette[i % palette.length],\n );\n }\n return next;\n }\n\n if (kind === \"line\") {\n if (next.borderColor === undefined) next.borderColor = color;\n if (next.backgroundColor === undefined) {\n next.backgroundColor = withAlpha(color, 0.15);\n }\n if (next.tension === undefined) next.tension = 0.3;\n if (next.fill === undefined) next.fill = true;\n if (next.pointRadius === undefined) next.pointRadius = 2;\n return next;\n }\n\n // bar\n if (next.backgroundColor === undefined) {\n next.backgroundColor =\n single && labelsCount > 1\n ? Array.from({ length: labelsCount }, () => color)\n : color;\n }\n if (next.borderRadius === undefined) next.borderRadius = 4;\n return next;\n });\n}\n","import { computed, type ComputedRef } from \"vue\";\nimport type { ChartData, ChartDataset, ChartOptions } from \"chart.js\";\nimport {\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\n/**\n * Shared props for the value-axis chart wrappers (`DXBarChart`, `DXLineChart`).\n * `DXBarChart` and `DXLineChart` differ ONLY in the chart.js component they\n * render and the palette kind — everything else (theming, option-merge,\n * legend-default) is identical, and lived duplicated in both SFCs (#135).\n */\nexport interface ThemedChartProps<TType extends \"bar\" | \"line\"> {\n /** X-axis category labels. */\n labels: string[];\n /** Chart.js datasets. Colours are themed when a dataset omits them. */\n datasets: ChartDataset<TType>[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: ChartOptions<TType>;\n /** Format value-axis ticks and tooltips. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: only when there is more than one dataset). */\n showLegend?: boolean;\n}\n\n/**\n * The themed-chart body shared by `DXBarChart` and `DXLineChart`: applies the\n * data-viz palette to un-coloured datasets and deep-merges the consumer's\n * `options` over the dashboard-friendly defaults. Pass the reactive `props`\n * object (property access stays reactive inside the computeds).\n */\nexport function useThemedChart<TType extends \"bar\" | \"line\">(\n type: TType,\n props: ThemedChartProps<TType>,\n): {\n chartData: ComputedRef<ChartData<TType>>;\n mergedOptions: ComputedRef<ChartOptions<TType>>;\n} {\n const chartData = computed(\n () =>\n ({\n labels: props.labels,\n datasets: applyPalette(props.datasets, type, props.labels.length),\n }) as ChartData<TType>,\n );\n\n const mergedOptions = computed(\n () =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? props.datasets.length > 1,\n hasValueAxis: true,\n }),\n props.options ?? {},\n ) as ChartOptions<TType>,\n );\n\n return { chartData, mergedOptions };\n}\n","<!--\n @component\n A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette (--dx-chart-1..8) applied and dashboard-friendly defaults (no x gridlines,\n formatted value ticks, responsive, legend hidden for a single series). Pass\n `labels` + `datasets`; `options` deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Bar :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Bar } from \"vue-chartjs\";\nimport { registerCharts } from \"./chartTheme\";\nimport { useThemedChart, type ThemedChartProps } from \"./useThemedChart\";\n\nregisterCharts();\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<ThemedChartProps<\"bar\">>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst { chartData, mergedOptions } = useThemedChart(\"bar\", props);\n</script>\n","<!--\n @component\n A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette applied (--dx-chart-1..8 stroke + translucent fill, smoothed line)\n and dashboard-friendly defaults. Pass `labels` + `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Line :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Line } from \"vue-chartjs\";\nimport { registerCharts } from \"./chartTheme\";\nimport { useThemedChart, type ThemedChartProps } from \"./useThemedChart\";\n\nregisterCharts();\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<ThemedChartProps<\"line\">>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst { chartData, mergedOptions } = useThemedChart(\"line\", props);\n</script>\n","<!--\n @component\n A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one\n chart-palette colour (--dx-chart-1..8) per slice and dashboard-friendly defaults. Pass\n `labels` + either `data` (a number array) or full `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Doughnut :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Doughnut } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** Slice labels. */\n labels: string[];\n /** Convenience: a single number array (one value per label). */\n data?: number[];\n /** Full Chart.js datasets (alternative to `data`). */\n datasets?: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format tooltip values. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: true — slices need identifying). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? true` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst resolvedDatasets = computed(() =>\n props.datasets ?? [{ data: props.data ?? [] }],\n);\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(resolvedDatasets.value, \"doughnut\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? true,\n hasValueAxis: false,\n }),\n props.options ?? {},\n ),\n);\n</script>\n"],"names":["isPlainObject","value","mergeOptions","base","override","result","key","registered","registerCharts","Chart","registerables","PALETTE_VARS","getPalette","fallback","styles","name","themeColor","withAlpha","color","alpha","hex","r","g","b","rgbMatch","n","formatValue","format","currencySymbol","locale","num","baseOptions","args","valueFormat","showLegend","hasValueAxis","gridColor","tickColor","options","ctx","raw","_a","formatted","label","_b","applyPalette","datasets","kind","labelsCount","palette","single","dataset","index","next","_","i","useThemedChart","type","props","chartData","computed","mergedOptions","__props","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","Bar","Line","resolvedDatasets","Doughnut"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(l,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("vue"),require("vue-chartjs"),require("chart.js")):typeof define=="function"&&define.amd?define(["exports","vue","vue-chartjs","chart.js"],o):(l=typeof globalThis<"u"?globalThis:l||self,o(l.DashboardForLaravelCharts={},l.Vue,l.VueChartjs,l.Chart))})(this,(function(l,o,f,y){"use strict";const C=r=>!!r&&typeof r=="object"&&!Array.isArray(r);function m(r,e){const t={...r};for(const[n,s]of Object.entries(e))C(s)&&C(t[n])?t[n]=m(t[n],s):t[n]=s;return t}let _=!1;function h(){_||(y.Chart.register(...y.registerables),_=!0)}const v=[["--dx-chart-1","#2563eb"],["--dx-chart-2","#65a30d"],["--dx-chart-3","#7c3aed"],["--dx-chart-4","#0d9488"],["--dx-chart-5","#ea580c"],["--dx-chart-6","#0891b2"],["--dx-chart-7","#d97706"],["--dx-chart-8","#db2777"]];function A(){if(typeof document>"u"||typeof getComputedStyle>"u")return v.map(([,e])=>e);const r=getComputedStyle(document.documentElement);return v.map(([e,t])=>r.getPropertyValue(e).trim()||t)}function $(r,e){return typeof document>"u"||typeof getComputedStyle>"u"?e:getComputedStyle(document.documentElement).getPropertyValue(r).trim()||e}function x(r,e){const t=r.replace(/^#/,"");if(/^[0-9a-f]{6}$/i.test(t)){const s=parseInt(t.slice(0,2),16),c=parseInt(t.slice(2,4),16),d=parseInt(t.slice(4,6),16);return`rgba(${s}, ${c}, ${d}, ${e})`}const n=r.match(/^rgba?\(([^)]+)\)$/);if(n){const[s,c,d]=n[1].split(",").map(a=>a.trim());return`rgba(${s}, ${c}, ${d}, ${e})`}return r}function S(r,e,t,n){const s=new Intl.NumberFormat(n,{maximumFractionDigits:2}).format(r);return e==="currency"?`${t}${s}`:e==="percent"?`${s}%`:s}function b(r){const{valueFormat:e,currencySymbol:t="£",locale:n,showLegend:s=!1,hasValueAxis:c=!0}=r,d=x($("--bs-border-color","#dee2e6"),.6),a=$("--bs-secondary-color","#6c757d"),u={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:s,labels:{color:a}},tooltip:{callbacks:{label:i=>{var L,F;const p=((L=i.parsed)==null?void 0:L.y)??i.parsed??i.raw,k=typeof p=="number"?p:Number(p),w=Number.isFinite(k)?S(k,e,t,n):String(p),D=((F=i.dataset)==null?void 0:F.label)??i.label??"";return D?`${D}: ${w}`:w}}}}};return c&&(u.scales={x:{grid:{display:!1},border:{color:d},ticks:{color:a}},y:{beginAtZero:!0,grid:{color:d,drawBorder:!1},border:{display:!1},ticks:{color:a,callback:i=>S(Number(i),e,t,n)}}}),u}function g(r,e,t){const n=A(),s=r.length===1;return r.map((c,d)=>{const a={...c},u=n[d%n.length];return e==="doughnut"?(a.backgroundColor===void 0&&(a.backgroundColor=Array.from({length:t},(i,p)=>n[p%n.length])),a):e==="line"?(a.borderColor===void 0&&(a.borderColor=u),a.backgroundColor===void 0&&(a.backgroundColor=x(u,.15)),a.tension===void 0&&(a.tension=.3),a.fill===void 0&&(a.fill=!0),a.pointRadius===void 0&&(a.pointRadius=2),a):(a.backgroundColor===void 0&&(a.backgroundColor=s&&t>1?Array.from({length:t},()=>u):u),a.borderRadius===void 0&&(a.borderRadius=4),a)})}const B={class:"dx-chart"},V=o.defineComponent({__name:"DXBarChart",props:{labels:{},datasets:{},options:{},valueFormat:{},currencySymbol:{default:"£"},locale:{},showLegend:{type:Boolean,default:void 0}},setup(r){h();const e=r,t=o.computed(()=>({labels:e.labels,datasets:g(e.datasets,"bar",e.labels.length)})),n=o.computed(()=>m(b({valueFormat:e.valueFormat,currencySymbol:e.currencySymbol,locale:e.locale,showLegend:e.showLegend??e.datasets.length>1,hasValueAxis:!0}),e.options??{}));return(s,c)=>(o.openBlock(),o.createElementBlock("div",B,[o.createVNode(o.unref(f.Bar),{data:t.value,options:n.value},null,8,["data","options"])]))}}),O={class:"dx-chart"},j=o.defineComponent({__name:"DXLineChart",props:{labels:{},datasets:{},options:{},valueFormat:{},currencySymbol:{default:"£"},locale:{},showLegend:{type:Boolean,default:void 0}},setup(r){h();const e=r,t=o.computed(()=>({labels:e.labels,datasets:g(e.datasets,"line",e.labels.length)})),n=o.computed(()=>m(b({valueFormat:e.valueFormat,currencySymbol:e.currencySymbol,locale:e.locale,showLegend:e.showLegend??e.datasets.length>1,hasValueAxis:!0}),e.options??{}));return(s,c)=>(o.openBlock(),o.createElementBlock("div",O,[o.createVNode(o.unref(f.Line),{data:t.value,options:n.value},null,8,["data","options"])]))}}),E={class:"dx-chart"},N=o.defineComponent({__name:"DXDoughnutChart",props:{labels:{},data:{},datasets:{},options:{},valueFormat:{},currencySymbol:{default:"£"},locale:{},showLegend:{type:Boolean,default:void 0}},setup(r){h();const e=r,t=o.computed(()=>e.datasets??[{data:e.data??[]}]),n=o.computed(()=>({labels:e.labels,datasets:g(t.value,"doughnut",e.labels.length)})),s=o.computed(()=>m(b({valueFormat:e.valueFormat,currencySymbol:e.currencySymbol,locale:e.locale,showLegend:e.showLegend??!0,hasValueAxis:!1}),e.options??{}));return(c,d)=>(o.openBlock(),o.createElementBlock("div",E,[o.createVNode(o.unref(f.Doughnut),{data:n.value,options:s.value},null,8,["data","options"])]))}});l.DXBarChart=V,l.DXDoughnutChart=N,l.DXLineChart=j,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(c,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("vue"),require("vue-chartjs"),require("chart.js")):typeof define=="function"&&define.amd?define(["exports","vue","vue-chartjs","chart.js"],o):(c=typeof globalThis<"u"?globalThis:c||self,o(c.DashboardForLaravelCharts={},c.Vue,c.VueChartjs,c.Chart))})(this,(function(c,o,m,b){"use strict";const g=r=>!!r&&typeof r=="object"&&!Array.isArray(r);function p(r,e){const t={...r};for(const[n,s]of Object.entries(e))g(s)&&g(t[n])?t[n]=p(t[n],s):t[n]=s;return t}let y=!1;function h(){y||(b.Chart.register(...b.registerables),y=!0)}const C=[["--dx-chart-1","#2563eb"],["--dx-chart-2","#65a30d"],["--dx-chart-3","#7c3aed"],["--dx-chart-4","#0d9488"],["--dx-chart-5","#ea580c"],["--dx-chart-6","#0891b2"],["--dx-chart-7","#d97706"],["--dx-chart-8","#db2777"]];function F(){if(typeof document>"u"||typeof getComputedStyle>"u")return C.map(([,e])=>e);const r=getComputedStyle(document.documentElement);return C.map(([e,t])=>r.getPropertyValue(e).trim()||t)}function _(r,e){return typeof document>"u"||typeof getComputedStyle>"u"?e:getComputedStyle(document.documentElement).getPropertyValue(r).trim()||e}function $(r,e){const t=r.replace(/^#/,"");if(/^[0-9a-f]{6}$/i.test(t)){const s=parseInt(t.slice(0,2),16),l=parseInt(t.slice(2,4),16),i=parseInt(t.slice(4,6),16);return`rgba(${s}, ${l}, ${i}, ${e})`}const n=r.match(/^rgba?\(([^)]+)\)$/);if(n){const[s,l,i]=n[1].split(",").map(a=>a.trim());return`rgba(${s}, ${l}, ${i}, ${e})`}return r}function x(r,e,t,n){const s=new Intl.NumberFormat(n,{maximumFractionDigits:2}).format(r);return e==="currency"?`${t}${s}`:e==="percent"?`${s}%`:s}function k(r){const{valueFormat:e,currencySymbol:t="£",locale:n,showLegend:s=!1,hasValueAxis:l=!0}=r,i=$(_("--bs-border-color","#dee2e6"),.6),a=_("--bs-secondary-color","#6c757d"),u={responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:s,labels:{color:a}},tooltip:{callbacks:{label:d=>{var v,A;const f=((v=d.parsed)==null?void 0:v.y)??d.parsed??d.raw,w=typeof f=="number"?f:Number(f),B=Number.isFinite(w)?x(w,e,t,n):String(f),L=((A=d.dataset)==null?void 0:A.label)??d.label??"";return L?`${L}: ${B}`:B}}}}};return l&&(u.scales={x:{grid:{display:!1},border:{color:i},ticks:{color:a}},y:{beginAtZero:!0,grid:{color:i,drawBorder:!1},border:{display:!1},ticks:{color:a,callback:d=>x(Number(d),e,t,n)}}}),u}function D(r,e,t){const n=F(),s=r.length===1;return r.map((l,i)=>{const a={...l},u=n[i%n.length];return e==="doughnut"?(a.backgroundColor===void 0&&(a.backgroundColor=Array.from({length:t},(d,f)=>n[f%n.length])),a):e==="line"?(a.borderColor===void 0&&(a.borderColor=u),a.backgroundColor===void 0&&(a.backgroundColor=$(u,.15)),a.tension===void 0&&(a.tension=.3),a.fill===void 0&&(a.fill=!0),a.pointRadius===void 0&&(a.pointRadius=2),a):(a.backgroundColor===void 0&&(a.backgroundColor=s&&t>1?Array.from({length:t},()=>u):u),a.borderRadius===void 0&&(a.borderRadius=4),a)})}function S(r,e){const t=o.computed(()=>({labels:e.labels,datasets:D(e.datasets,r,e.labels.length)})),n=o.computed(()=>p(k({valueFormat:e.valueFormat,currencySymbol:e.currencySymbol,locale:e.locale,showLegend:e.showLegend??e.datasets.length>1,hasValueAxis:!0}),e.options??{}));return{chartData:t,mergedOptions:n}}const V={class:"dx-chart"},O=o.defineComponent({__name:"DXBarChart",props:{labels:{},datasets:{},options:{},valueFormat:{},currencySymbol:{default:"£"},locale:{},showLegend:{type:Boolean,default:void 0}},setup(r){h();const e=r,{chartData:t,mergedOptions:n}=S("bar",e);return(s,l)=>(o.openBlock(),o.createElementBlock("div",V,[o.createVNode(o.unref(m.Bar),{data:o.unref(t),options:o.unref(n)},null,8,["data","options"])]))}}),j={class:"dx-chart"},E=o.defineComponent({__name:"DXLineChart",props:{labels:{},datasets:{},options:{},valueFormat:{},currencySymbol:{default:"£"},locale:{},showLegend:{type:Boolean,default:void 0}},setup(r){h();const e=r,{chartData:t,mergedOptions:n}=S("line",e);return(s,l)=>(o.openBlock(),o.createElementBlock("div",j,[o.createVNode(o.unref(m.Line),{data:o.unref(t),options:o.unref(n)},null,8,["data","options"])]))}}),N={class:"dx-chart"},P=o.defineComponent({__name:"DXDoughnutChart",props:{labels:{},data:{},datasets:{},options:{},valueFormat:{},currencySymbol:{default:"£"},locale:{},showLegend:{type:Boolean,default:void 0}},setup(r){h();const e=r,t=o.computed(()=>e.datasets??[{data:e.data??[]}]),n=o.computed(()=>({labels:e.labels,datasets:D(t.value,"doughnut",e.labels.length)})),s=o.computed(()=>p(k({valueFormat:e.valueFormat,currencySymbol:e.currencySymbol,locale:e.locale,showLegend:e.showLegend??!0,hasValueAxis:!1}),e.options??{}));return(l,i)=>(o.openBlock(),o.createElementBlock("div",N,[o.createVNode(o.unref(m.Doughnut),{data:n.value,options:s.value},null,8,["data","options"])]))}});c.DXBarChart=O,c.DXDoughnutChart=P,c.DXLineChart=E,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=charts.umd.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"charts.umd.cjs","sources":["../resources/js/components/charts/chartTheme.ts","../resources/js/components/charts/DXBarChart.vue","../resources/js/components/charts/DXLineChart.vue","../resources/js/components/charts/DXDoughnutChart.vue"],"sourcesContent":["/**\n * Shared theming for the DX*Chart wrappers: the dedicated categorical chart\n * palette (--dx-chart-* from the live theme), dashboard-friendly default options (no gridlines /\n * legend clutter, responsive, formatted ticks), and a one-time Chart.js\n * registration. chart.js / vue-chartjs are OPTIONAL peer deps.\n */\nimport { Chart, registerables } from \"chart.js\";\n\nexport type ChartValueFormat = \"number\" | \"currency\" | \"percent\";\n\nconst isPlainObject = (value: unknown): value is Record<string, any> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\n/** Deep-merge consumer `options` over the themed defaults (arrays replace). */\nexport function mergeOptions(\n base: Record<string, any>,\n override: Record<string, any>,\n): Record<string, any> {\n const result: Record<string, any> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = mergeOptions(result[key], value);\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nlet registered = false;\n\n/**\n * Register Chart.js (idempotent). Uses the full `registerables` set so every\n * controller / scale / element / plugin (incl. Legend, Tooltip, Filler) is\n * available — chart.js is an external peer dep, so this adds no bundle weight.\n */\nexport function registerCharts(): void {\n if (registered) return;\n Chart.register(...registerables);\n registered = true;\n}\n\n// The dedicated data-viz palette (`$dx-chart-palette` in theme.scss) — eight\n// vivid hues in a fixed, CVD-validated order, published as --dx-chart-1..8.\n// Deliberately NOT the semantic --bs-* colours (#141): the base theme colours\n// are dark AA \"emphasis\" shades (too muted for series), and status colours\n// shouldn't impersonate \"series 2\". Fallbacks mirror the shipped theme so\n// charts still render without the CSS (SSR / tests / a consumer who didn't\n// import it). Sync with theme.scss's $dx-chart-palette is enforced by a test\n// that parses the Sass source (charts.test.ts), which is why this is exported.\nexport const PALETTE_VARS: Array<[string, string]> = [\n [\"--dx-chart-1\", \"#2563eb\"], // blue\n [\"--dx-chart-2\", \"#65a30d\"], // lime\n [\"--dx-chart-3\", \"#7c3aed\"], // violet\n [\"--dx-chart-4\", \"#0d9488\"], // teal\n [\"--dx-chart-5\", \"#ea580c\"], // orange\n [\"--dx-chart-6\", \"#0891b2\"], // cyan\n [\"--dx-chart-7\", \"#d97706\"], // amber\n [\"--dx-chart-8\", \"#db2777\"], // pink\n];\n\n/** Resolve the themed categorical palette from the document root. */\nexport function getPalette(): string[] {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return PALETTE_VARS.map(([, fallback]) => fallback);\n }\n const styles = getComputedStyle(document.documentElement);\n return PALETTE_VARS.map(([name, fallback]) => {\n const value = styles.getPropertyValue(name).trim();\n return value || fallback;\n });\n}\n\n/** Read a single theme colour (e.g. the body text colour) with a fallback. */\nfunction themeColor(name: string, fallback: string): string {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return fallback;\n }\n const value = getComputedStyle(document.documentElement)\n .getPropertyValue(name)\n .trim();\n return value || fallback;\n}\n\n/** Add an alpha channel to a hex or rgb() colour (best-effort). */\nexport function withAlpha(color: string, alpha: number): string {\n const hex = color.replace(/^#/, \"\");\n if (/^[0-9a-f]{6}$/i.test(hex)) {\n const r = parseInt(hex.slice(0, 2), 16);\n const g = parseInt(hex.slice(2, 4), 16);\n const b = parseInt(hex.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n const rgbMatch = color.match(/^rgba?\\(([^)]+)\\)$/);\n if (rgbMatch) {\n const [r, g, b] = rgbMatch[1].split(\",\").map((n) => n.trim());\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return color;\n}\n\n/** Format a numeric value per the chart's value format (for tick/tooltips). */\nexport function formatValue(\n value: number,\n format: ChartValueFormat | undefined,\n currencySymbol: string,\n locale?: string,\n): string {\n const num = new Intl.NumberFormat(locale, {\n maximumFractionDigits: 2,\n }).format(value);\n if (format === \"currency\") return `${currencySymbol}${num}`;\n if (format === \"percent\") return `${num}%`;\n return num;\n}\n\nexport interface BaseOptionArgs {\n valueFormat?: ChartValueFormat;\n currencySymbol?: string;\n locale?: string;\n /** Show the legend (default: false for a single dataset, true for many). */\n showLegend?: boolean;\n /** Whether the chart has value axes (bar/line) vs none (doughnut). */\n hasValueAxis?: boolean;\n}\n\n/** Dashboard default Chart.js options merged under any consumer `options`. */\nexport function baseOptions(args: BaseOptionArgs): Record<string, any> {\n const {\n valueFormat,\n currencySymbol = \"£\",\n locale,\n showLegend = false,\n hasValueAxis = true,\n } = args;\n\n const gridColor = withAlpha(themeColor(\"--bs-border-color\", \"#dee2e6\"), 0.6);\n const tickColor = themeColor(\"--bs-secondary-color\", \"#6c757d\");\n\n const options: Record<string, any> = {\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n legend: { display: showLegend, labels: { color: tickColor } },\n tooltip: {\n callbacks: {\n label: (ctx: any) => {\n const raw = ctx.parsed?.y ?? ctx.parsed ?? ctx.raw;\n const value = typeof raw === \"number\" ? raw : Number(raw);\n const formatted = Number.isFinite(value)\n ? formatValue(value, valueFormat, currencySymbol, locale)\n : String(raw);\n const label = ctx.dataset?.label ?? ctx.label ?? \"\";\n return label ? `${label}: ${formatted}` : formatted;\n },\n },\n },\n },\n };\n\n if (hasValueAxis) {\n options.scales = {\n x: {\n grid: { display: false },\n border: { color: gridColor },\n ticks: { color: tickColor },\n },\n y: {\n beginAtZero: true,\n grid: { color: gridColor, drawBorder: false },\n border: { display: false },\n ticks: {\n color: tickColor,\n callback: (value: number | string) =>\n formatValue(Number(value), valueFormat, currencySymbol, locale),\n },\n },\n };\n }\n\n return options;\n}\n\n/**\n * Apply the themed palette to any dataset that doesn't set its own colours.\n * Bar: one colour per dataset (or per bar for a single dataset). Line: a themed\n * stroke + translucent fill. Doughnut/pie: one colour per slice.\n */\nexport function applyPalette(\n datasets: any[],\n kind: \"bar\" | \"line\" | \"doughnut\",\n labelsCount: number,\n): any[] {\n const palette = getPalette();\n const single = datasets.length === 1;\n\n return datasets.map((dataset, index) => {\n const next = { ...dataset };\n const color = palette[index % palette.length];\n\n if (kind === \"doughnut\") {\n if (next.backgroundColor === undefined) {\n next.backgroundColor = Array.from(\n { length: labelsCount },\n (_, i) => palette[i % palette.length],\n );\n }\n return next;\n }\n\n if (kind === \"line\") {\n if (next.borderColor === undefined) next.borderColor = color;\n if (next.backgroundColor === undefined) {\n next.backgroundColor = withAlpha(color, 0.15);\n }\n if (next.tension === undefined) next.tension = 0.3;\n if (next.fill === undefined) next.fill = true;\n if (next.pointRadius === undefined) next.pointRadius = 2;\n return next;\n }\n\n // bar\n if (next.backgroundColor === undefined) {\n next.backgroundColor =\n single && labelsCount > 1\n ? Array.from({ length: labelsCount }, () => color)\n : color;\n }\n if (next.borderRadius === undefined) next.borderRadius = 4;\n return next;\n });\n}\n","<!--\n @component\n A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette (--dx-chart-1..8) applied and dashboard-friendly defaults (no x gridlines,\n formatted value ticks, responsive, legend hidden for a single series). Pass\n `labels` + `datasets`; `options` deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Bar :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Bar } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** X-axis category labels. */\n labels: string[];\n /** Chart.js datasets. Colours are themed when a dataset omits them. */\n datasets: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format value-axis ticks and tooltips. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: only when there is more than one dataset). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(props.datasets, \"bar\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? props.datasets.length > 1,\n hasValueAxis: true,\n }),\n props.options ?? {},\n ),\n);\n</script>\n","<!--\n @component\n A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette applied (--dx-chart-1..8 stroke + translucent fill, smoothed line)\n and dashboard-friendly defaults. Pass `labels` + `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Line :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Line } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** X-axis category labels. */\n labels: string[];\n /** Chart.js datasets. Colours are themed when a dataset omits them. */\n datasets: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format value-axis ticks and tooltips. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: only when there is more than one dataset). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(props.datasets, \"line\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? props.datasets.length > 1,\n hasValueAxis: true,\n }),\n props.options ?? {},\n ),\n);\n</script>\n","<!--\n @component\n A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one\n chart-palette colour (--dx-chart-1..8) per slice and dashboard-friendly defaults. Pass\n `labels` + either `data` (a number array) or full `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Doughnut :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Doughnut } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** Slice labels. */\n labels: string[];\n /** Convenience: a single number array (one value per label). */\n data?: number[];\n /** Full Chart.js datasets (alternative to `data`). */\n datasets?: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format tooltip values. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: true — slices need identifying). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? true` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst resolvedDatasets = computed(() =>\n props.datasets ?? [{ data: props.data ?? [] }],\n);\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(resolvedDatasets.value, \"doughnut\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? true,\n hasValueAxis: false,\n }),\n props.options ?? {},\n ),\n);\n</script>\n"],"names":["isPlainObject","value","mergeOptions","base","override","result","key","registered","registerCharts","Chart","registerables","PALETTE_VARS","getPalette","fallback","styles","name","themeColor","withAlpha","color","alpha","hex","r","g","b","rgbMatch","n","formatValue","format","currencySymbol","locale","num","baseOptions","args","valueFormat","showLegend","hasValueAxis","gridColor","tickColor","options","ctx","raw","_a","formatted","label","_b","applyPalette","datasets","kind","labelsCount","palette","single","dataset","index","next","_","i","props","__props","chartData","computed","mergedOptions","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","Bar","Line","resolvedDatasets","Doughnut"],"mappings":"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"}
1
+ {"version":3,"file":"charts.umd.cjs","sources":["../resources/js/components/charts/chartTheme.ts","../resources/js/components/charts/useThemedChart.ts","../resources/js/components/charts/DXBarChart.vue","../resources/js/components/charts/DXLineChart.vue","../resources/js/components/charts/DXDoughnutChart.vue"],"sourcesContent":["/**\n * Shared theming for the DX*Chart wrappers: the dedicated categorical chart\n * palette (--dx-chart-* from the live theme), dashboard-friendly default options (no gridlines /\n * legend clutter, responsive, formatted ticks), and a one-time Chart.js\n * registration. chart.js / vue-chartjs are OPTIONAL peer deps.\n */\nimport { Chart, registerables } from \"chart.js\";\n\nexport type ChartValueFormat = \"number\" | \"currency\" | \"percent\";\n\nconst isPlainObject = (value: unknown): value is Record<string, any> =>\n !!value && typeof value === \"object\" && !Array.isArray(value);\n\n/** Deep-merge consumer `options` over the themed defaults (arrays replace). */\nexport function mergeOptions(\n base: Record<string, any>,\n override: Record<string, any>,\n): Record<string, any> {\n const result: Record<string, any> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = mergeOptions(result[key], value);\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nlet registered = false;\n\n/**\n * Register Chart.js (idempotent). Uses the full `registerables` set so every\n * controller / scale / element / plugin (incl. Legend, Tooltip, Filler) is\n * available — chart.js is an external peer dep, so this adds no bundle weight.\n */\nexport function registerCharts(): void {\n if (registered) return;\n Chart.register(...registerables);\n registered = true;\n}\n\n// The dedicated data-viz palette (`$dx-chart-palette` in theme.scss) — eight\n// vivid hues in a fixed, CVD-validated order, published as --dx-chart-1..8.\n// Deliberately NOT the semantic --bs-* colours (#141): the base theme colours\n// are dark AA \"emphasis\" shades (too muted for series), and status colours\n// shouldn't impersonate \"series 2\". Fallbacks mirror the shipped theme so\n// charts still render without the CSS (SSR / tests / a consumer who didn't\n// import it). Sync with theme.scss's $dx-chart-palette is enforced by a test\n// that parses the Sass source (charts.test.ts), which is why this is exported.\nexport const PALETTE_VARS: Array<[string, string]> = [\n [\"--dx-chart-1\", \"#2563eb\"], // blue\n [\"--dx-chart-2\", \"#65a30d\"], // lime\n [\"--dx-chart-3\", \"#7c3aed\"], // violet\n [\"--dx-chart-4\", \"#0d9488\"], // teal\n [\"--dx-chart-5\", \"#ea580c\"], // orange\n [\"--dx-chart-6\", \"#0891b2\"], // cyan\n [\"--dx-chart-7\", \"#d97706\"], // amber\n [\"--dx-chart-8\", \"#db2777\"], // pink\n];\n\n/** Resolve the themed categorical palette from the document root. */\nexport function getPalette(): string[] {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return PALETTE_VARS.map(([, fallback]) => fallback);\n }\n const styles = getComputedStyle(document.documentElement);\n return PALETTE_VARS.map(([name, fallback]) => {\n const value = styles.getPropertyValue(name).trim();\n return value || fallback;\n });\n}\n\n/** Read a single theme colour (e.g. the body text colour) with a fallback. */\nfunction themeColor(name: string, fallback: string): string {\n if (typeof document === \"undefined\" || typeof getComputedStyle === \"undefined\") {\n return fallback;\n }\n const value = getComputedStyle(document.documentElement)\n .getPropertyValue(name)\n .trim();\n return value || fallback;\n}\n\n/** Add an alpha channel to a hex or rgb() colour (best-effort). */\nexport function withAlpha(color: string, alpha: number): string {\n const hex = color.replace(/^#/, \"\");\n if (/^[0-9a-f]{6}$/i.test(hex)) {\n const r = parseInt(hex.slice(0, 2), 16);\n const g = parseInt(hex.slice(2, 4), 16);\n const b = parseInt(hex.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n const rgbMatch = color.match(/^rgba?\\(([^)]+)\\)$/);\n if (rgbMatch) {\n const [r, g, b] = rgbMatch[1].split(\",\").map((n) => n.trim());\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n return color;\n}\n\n/** Format a numeric value per the chart's value format (for tick/tooltips). */\nexport function formatValue(\n value: number,\n format: ChartValueFormat | undefined,\n currencySymbol: string,\n locale?: string,\n): string {\n const num = new Intl.NumberFormat(locale, {\n maximumFractionDigits: 2,\n }).format(value);\n if (format === \"currency\") return `${currencySymbol}${num}`;\n if (format === \"percent\") return `${num}%`;\n return num;\n}\n\nexport interface BaseOptionArgs {\n valueFormat?: ChartValueFormat;\n currencySymbol?: string;\n locale?: string;\n /** Show the legend (default: false for a single dataset, true for many). */\n showLegend?: boolean;\n /** Whether the chart has value axes (bar/line) vs none (doughnut). */\n hasValueAxis?: boolean;\n}\n\n/** Dashboard default Chart.js options merged under any consumer `options`. */\nexport function baseOptions(args: BaseOptionArgs): Record<string, any> {\n const {\n valueFormat,\n currencySymbol = \"£\",\n locale,\n showLegend = false,\n hasValueAxis = true,\n } = args;\n\n const gridColor = withAlpha(themeColor(\"--bs-border-color\", \"#dee2e6\"), 0.6);\n const tickColor = themeColor(\"--bs-secondary-color\", \"#6c757d\");\n\n const options: Record<string, any> = {\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n legend: { display: showLegend, labels: { color: tickColor } },\n tooltip: {\n callbacks: {\n label: (ctx: any) => {\n const raw = ctx.parsed?.y ?? ctx.parsed ?? ctx.raw;\n const value = typeof raw === \"number\" ? raw : Number(raw);\n const formatted = Number.isFinite(value)\n ? formatValue(value, valueFormat, currencySymbol, locale)\n : String(raw);\n const label = ctx.dataset?.label ?? ctx.label ?? \"\";\n return label ? `${label}: ${formatted}` : formatted;\n },\n },\n },\n },\n };\n\n if (hasValueAxis) {\n options.scales = {\n x: {\n grid: { display: false },\n border: { color: gridColor },\n ticks: { color: tickColor },\n },\n y: {\n beginAtZero: true,\n grid: { color: gridColor, drawBorder: false },\n border: { display: false },\n ticks: {\n color: tickColor,\n callback: (value: number | string) =>\n formatValue(Number(value), valueFormat, currencySymbol, locale),\n },\n },\n };\n }\n\n return options;\n}\n\n/**\n * Apply the themed palette to any dataset that doesn't set its own colours.\n * Bar: one colour per dataset (or per bar for a single dataset). Line: a themed\n * stroke + translucent fill. Doughnut/pie: one colour per slice.\n */\nexport function applyPalette(\n datasets: any[],\n kind: \"bar\" | \"line\" | \"doughnut\",\n labelsCount: number,\n): any[] {\n const palette = getPalette();\n const single = datasets.length === 1;\n\n return datasets.map((dataset, index) => {\n const next = { ...dataset };\n const color = palette[index % palette.length];\n\n if (kind === \"doughnut\") {\n if (next.backgroundColor === undefined) {\n next.backgroundColor = Array.from(\n { length: labelsCount },\n (_, i) => palette[i % palette.length],\n );\n }\n return next;\n }\n\n if (kind === \"line\") {\n if (next.borderColor === undefined) next.borderColor = color;\n if (next.backgroundColor === undefined) {\n next.backgroundColor = withAlpha(color, 0.15);\n }\n if (next.tension === undefined) next.tension = 0.3;\n if (next.fill === undefined) next.fill = true;\n if (next.pointRadius === undefined) next.pointRadius = 2;\n return next;\n }\n\n // bar\n if (next.backgroundColor === undefined) {\n next.backgroundColor =\n single && labelsCount > 1\n ? Array.from({ length: labelsCount }, () => color)\n : color;\n }\n if (next.borderRadius === undefined) next.borderRadius = 4;\n return next;\n });\n}\n","import { computed, type ComputedRef } from \"vue\";\nimport type { ChartData, ChartDataset, ChartOptions } from \"chart.js\";\nimport {\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\n/**\n * Shared props for the value-axis chart wrappers (`DXBarChart`, `DXLineChart`).\n * `DXBarChart` and `DXLineChart` differ ONLY in the chart.js component they\n * render and the palette kind — everything else (theming, option-merge,\n * legend-default) is identical, and lived duplicated in both SFCs (#135).\n */\nexport interface ThemedChartProps<TType extends \"bar\" | \"line\"> {\n /** X-axis category labels. */\n labels: string[];\n /** Chart.js datasets. Colours are themed when a dataset omits them. */\n datasets: ChartDataset<TType>[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: ChartOptions<TType>;\n /** Format value-axis ticks and tooltips. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: only when there is more than one dataset). */\n showLegend?: boolean;\n}\n\n/**\n * The themed-chart body shared by `DXBarChart` and `DXLineChart`: applies the\n * data-viz palette to un-coloured datasets and deep-merges the consumer's\n * `options` over the dashboard-friendly defaults. Pass the reactive `props`\n * object (property access stays reactive inside the computeds).\n */\nexport function useThemedChart<TType extends \"bar\" | \"line\">(\n type: TType,\n props: ThemedChartProps<TType>,\n): {\n chartData: ComputedRef<ChartData<TType>>;\n mergedOptions: ComputedRef<ChartOptions<TType>>;\n} {\n const chartData = computed(\n () =>\n ({\n labels: props.labels,\n datasets: applyPalette(props.datasets, type, props.labels.length),\n }) as ChartData<TType>,\n );\n\n const mergedOptions = computed(\n () =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? props.datasets.length > 1,\n hasValueAxis: true,\n }),\n props.options ?? {},\n ) as ChartOptions<TType>,\n );\n\n return { chartData, mergedOptions };\n}\n","<!--\n @component\n A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette (--dx-chart-1..8) applied and dashboard-friendly defaults (no x gridlines,\n formatted value ticks, responsive, legend hidden for a single series). Pass\n `labels` + `datasets`; `options` deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Bar :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Bar } from \"vue-chartjs\";\nimport { registerCharts } from \"./chartTheme\";\nimport { useThemedChart, type ThemedChartProps } from \"./useThemedChart\";\n\nregisterCharts();\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<ThemedChartProps<\"bar\">>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst { chartData, mergedOptions } = useThemedChart(\"bar\", props);\n</script>\n","<!--\n @component\n A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the\n theme's dedicated chart palette applied (--dx-chart-1..8 stroke + translucent fill, smoothed line)\n and dashboard-friendly defaults. Pass `labels` + `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Line :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Line } from \"vue-chartjs\";\nimport { registerCharts } from \"./chartTheme\";\nimport { useThemedChart, type ThemedChartProps } from \"./useThemedChart\";\n\nregisterCharts();\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? datasets.length > 1` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<ThemedChartProps<\"line\">>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst { chartData, mergedOptions } = useThemedChart(\"line\", props);\n</script>\n","<!--\n @component\n A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one\n chart-palette colour (--dx-chart-1..8) per slice and dashboard-friendly defaults. Pass\n `labels` + either `data` (a number array) or full `datasets`; `options`\n deep-merges over the defaults.\n\n Requires the optional peer deps `chart.js` and `vue-chartjs`.\n-->\n<template>\n <div class=\"dx-chart\">\n <Doughnut :data=\"chartData\" :options=\"mergedOptions\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Doughnut } from \"vue-chartjs\";\nimport {\n registerCharts,\n baseOptions,\n applyPalette,\n mergeOptions,\n type ChartValueFormat,\n} from \"./chartTheme\";\n\nregisterCharts();\n\ninterface Props {\n /** Slice labels. */\n labels: string[];\n /** Convenience: a single number array (one value per label). */\n data?: number[];\n /** Full Chart.js datasets (alternative to `data`). */\n datasets?: any[];\n /** Chart.js options; deep-merged over the themed defaults. */\n options?: Record<string, any>;\n /** Format tooltip values. */\n valueFormat?: ChartValueFormat;\n /** Currency symbol for valueFormat: \"currency\" (default \"£\"). */\n currencySymbol?: string;\n /** Locale for number formatting. */\n locale?: string;\n /** Show the legend (default: true — slices need identifying). */\n showLegend?: boolean;\n}\n\n// `showLegend` defaults to `undefined` (not Vue's Boolean-cast `false`) so the\n// `?? true` default can actually fire when it isn't passed.\nconst props = withDefaults(defineProps<Props>(), {\n currencySymbol: \"£\",\n showLegend: undefined,\n});\n\nconst resolvedDatasets = computed(() =>\n props.datasets ?? [{ data: props.data ?? [] }],\n);\n\nconst chartData = computed(() => ({\n labels: props.labels,\n datasets: applyPalette(resolvedDatasets.value, \"doughnut\", props.labels.length),\n}));\n\nconst mergedOptions = computed(() =>\n mergeOptions(\n baseOptions({\n valueFormat: props.valueFormat,\n currencySymbol: props.currencySymbol,\n locale: props.locale,\n showLegend: props.showLegend ?? true,\n hasValueAxis: false,\n }),\n props.options ?? {},\n ),\n);\n</script>\n"],"names":["isPlainObject","value","mergeOptions","base","override","result","key","registered","registerCharts","Chart","registerables","PALETTE_VARS","getPalette","fallback","styles","name","themeColor","withAlpha","color","alpha","hex","r","g","b","rgbMatch","n","formatValue","format","currencySymbol","locale","num","baseOptions","args","valueFormat","showLegend","hasValueAxis","gridColor","tickColor","options","ctx","raw","_a","formatted","label","_b","applyPalette","datasets","kind","labelsCount","palette","single","dataset","index","next","_","i","useThemedChart","type","props","chartData","computed","mergedOptions","__props","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","Bar","Line","resolvedDatasets","Doughnut"],"mappings":"4WAUA,MAAMA,EAAiBC,GACnB,CAAC,CAACA,GAAS,OAAOA,GAAU,UAAY,CAAC,MAAM,QAAQA,CAAK,EAGzD,SAASC,EACZC,EACAC,EACmB,CACnB,MAAMC,EAA8B,CAAE,GAAGF,CAAA,EACzC,SAAW,CAACG,EAAKL,CAAK,IAAK,OAAO,QAAQG,CAAQ,EAC1CJ,EAAcC,CAAK,GAAKD,EAAcK,EAAOC,CAAG,CAAC,EACjDD,EAAOC,CAAG,EAAIJ,EAAaG,EAAOC,CAAG,EAAGL,CAAK,EAE7CI,EAAOC,CAAG,EAAIL,EAGtB,OAAOI,CACX,CAEA,IAAIE,EAAa,GAOV,SAASC,GAAuB,CAC/BD,IACJE,QAAM,SAAS,GAAGC,eAAa,EAC/BH,EAAa,GACjB,CAUO,MAAMI,EAAwC,CACjD,CAAC,eAAgB,SAAS,EAC1B,CAAC,eAAgB,SAAS,EAC1B,CAAC,eAAgB,SAAS,EAC1B,CAAC,eAAgB,SAAS,EAC1B,CAAC,eAAgB,SAAS,EAC1B,CAAC,eAAgB,SAAS,EAC1B,CAAC,eAAgB,SAAS,EAC1B,CAAC,eAAgB,SAAS,CAC9B,EAGO,SAASC,GAAuB,CACnC,GAAI,OAAO,SAAa,KAAe,OAAO,iBAAqB,IAC/D,OAAOD,EAAa,IAAI,CAAC,CAAA,CAAGE,CAAQ,IAAMA,CAAQ,EAEtD,MAAMC,EAAS,iBAAiB,SAAS,eAAe,EACxD,OAAOH,EAAa,IAAI,CAAC,CAACI,EAAMF,CAAQ,IACtBC,EAAO,iBAAiBC,CAAI,EAAE,KAAA,GAC5BF,CACnB,CACL,CAGA,SAASG,EAAWD,EAAcF,EAA0B,CACxD,OAAI,OAAO,SAAa,KAAe,OAAO,iBAAqB,IACxDA,EAEG,iBAAiB,SAAS,eAAe,EAClD,iBAAiBE,CAAI,EACrB,KAAA,GACWF,CACpB,CAGO,SAASI,EAAUC,EAAeC,EAAuB,CAC5D,MAAMC,EAAMF,EAAM,QAAQ,KAAM,EAAE,EAClC,GAAI,iBAAiB,KAAKE,CAAG,EAAG,CAC5B,MAAMC,EAAI,SAASD,EAAI,MAAM,EAAG,CAAC,EAAG,EAAE,EAChCE,EAAI,SAASF,EAAI,MAAM,EAAG,CAAC,EAAG,EAAE,EAChCG,EAAI,SAASH,EAAI,MAAM,EAAG,CAAC,EAAG,EAAE,EACtC,MAAO,QAAQC,CAAC,KAAKC,CAAC,KAAKC,CAAC,KAAKJ,CAAK,GAC1C,CACA,MAAMK,EAAWN,EAAM,MAAM,oBAAoB,EACjD,GAAIM,EAAU,CACV,KAAM,CAACH,EAAGC,EAAGC,CAAC,EAAIC,EAAS,CAAC,EAAE,MAAM,GAAG,EAAE,IAAKC,GAAMA,EAAE,MAAM,EAC5D,MAAO,QAAQJ,CAAC,KAAKC,CAAC,KAAKC,CAAC,KAAKJ,CAAK,GAC1C,CACA,OAAOD,CACX,CAGO,SAASQ,EACZzB,EACA0B,EACAC,EACAC,EACM,CACN,MAAMC,EAAM,IAAI,KAAK,aAAaD,EAAQ,CACtC,sBAAuB,CAAA,CAC1B,EAAE,OAAO5B,CAAK,EACf,OAAI0B,IAAW,WAAmB,GAAGC,CAAc,GAAGE,CAAG,GACrDH,IAAW,UAAkB,GAAGG,CAAG,IAChCA,CACX,CAaO,SAASC,EAAYC,EAA2C,CACnE,KAAM,CACF,YAAAC,EACA,eAAAL,EAAiB,IACjB,OAAAC,EACA,WAAAK,EAAa,GACb,aAAAC,EAAe,EAAA,EACfH,EAEEI,EAAYnB,EAAUD,EAAW,oBAAqB,SAAS,EAAG,EAAG,EACrEqB,EAAYrB,EAAW,uBAAwB,SAAS,EAExDsB,EAA+B,CACjC,WAAY,GACZ,oBAAqB,GACrB,QAAS,CACL,OAAQ,CAAE,QAASJ,EAAY,OAAQ,CAAE,MAAOG,EAAU,EAC1D,QAAS,CACL,UAAW,CACP,MAAQE,GAAa,SACjB,MAAMC,IAAMC,EAAAF,EAAI,SAAJ,YAAAE,EAAY,IAAKF,EAAI,QAAUA,EAAI,IACzCtC,EAAQ,OAAOuC,GAAQ,SAAWA,EAAM,OAAOA,CAAG,EAClDE,EAAY,OAAO,SAASzC,CAAK,EACjCyB,EAAYzB,EAAOgC,EAAaL,EAAgBC,CAAM,EACtD,OAAOW,CAAG,EACVG,IAAQC,EAAAL,EAAI,UAAJ,YAAAK,EAAa,QAASL,EAAI,OAAS,GACjD,OAAOI,EAAQ,GAAGA,CAAK,KAAKD,CAAS,GAAKA,CAC9C,CAAA,CACJ,CACJ,CACJ,EAGJ,OAAIP,IACAG,EAAQ,OAAS,CACb,EAAG,CACC,KAAM,CAAE,QAAS,EAAA,EACjB,OAAQ,CAAE,MAAOF,CAAA,EACjB,MAAO,CAAE,MAAOC,CAAA,CAAU,EAE9B,EAAG,CACC,YAAa,GACb,KAAM,CAAE,MAAOD,EAAW,WAAY,EAAA,EACtC,OAAQ,CAAE,QAAS,EAAA,EACnB,MAAO,CACH,MAAOC,EACP,SAAWpC,GACPyB,EAAY,OAAOzB,CAAK,EAAGgC,EAAaL,EAAgBC,CAAM,CAAA,CACtE,CACJ,GAIDS,CACX,CAOO,SAASO,EACZC,EACAC,EACAC,EACK,CACL,MAAMC,EAAUrC,EAAA,EACVsC,EAASJ,EAAS,SAAW,EAEnC,OAAOA,EAAS,IAAI,CAACK,EAASC,IAAU,CACpC,MAAMC,EAAO,CAAE,GAAGF,CAAA,EACZjC,EAAQ+B,EAAQG,EAAQH,EAAQ,MAAM,EAE5C,OAAIF,IAAS,YACLM,EAAK,kBAAoB,SACzBA,EAAK,gBAAkB,MAAM,KACzB,CAAE,OAAQL,CAAA,EACV,CAACM,EAAGC,IAAMN,EAAQM,EAAIN,EAAQ,MAAM,CAAA,GAGrCI,GAGPN,IAAS,QACLM,EAAK,cAAgB,SAAWA,EAAK,YAAcnC,GACnDmC,EAAK,kBAAoB,SACzBA,EAAK,gBAAkBpC,EAAUC,EAAO,GAAI,GAE5CmC,EAAK,UAAY,SAAWA,EAAK,QAAU,IAC3CA,EAAK,OAAS,SAAWA,EAAK,KAAO,IACrCA,EAAK,cAAgB,SAAWA,EAAK,YAAc,GAChDA,IAIPA,EAAK,kBAAoB,SACzBA,EAAK,gBACDH,GAAUF,EAAc,EAClB,MAAM,KAAK,CAAE,OAAQA,CAAA,EAAe,IAAM9B,CAAK,EAC/CA,GAEVmC,EAAK,eAAiB,SAAWA,EAAK,aAAe,GAClDA,EACX,CAAC,CACL,CCjMO,SAASG,EACZC,EACAC,EAIF,CACE,MAAMC,EAAYC,EAAAA,SACd,KACK,CACG,OAAQF,EAAM,OACd,SAAUb,EAAaa,EAAM,SAAUD,EAAMC,EAAM,OAAO,MAAM,CAAA,EACpE,EAGFG,EAAgBD,EAAAA,SAClB,IACI1D,EACI6B,EAAY,CACR,YAAa2B,EAAM,YACnB,eAAgBA,EAAM,eACtB,OAAQA,EAAM,OACd,WAAYA,EAAM,YAAcA,EAAM,SAAS,OAAS,EACxD,aAAc,EAAA,CACjB,EACDA,EAAM,SAAW,CAAA,CAAC,CACtB,EAGR,MAAO,CAAE,UAAAC,EAAW,cAAAE,CAAA,CACxB,sNChDArD,EAAA,EAIA,MAAMkD,EAAQI,EAKR,CAAE,UAAAH,EAAW,cAAAE,CAAA,EAAkBL,EAAe,MAAOE,CAAK,gBAnB9DK,YAAA,EAAAC,qBAEM,MAFNC,EAEM,CADJC,cAAkDC,EAAAA,MAAAC,EAAAA,GAAA,EAAA,CAA5C,KAAMD,EAAAA,MAAAR,CAAA,EAAY,QAASQ,EAAAA,MAAAN,CAAA,CAAA,mPCSrCrD,EAAA,EAIA,MAAMkD,EAAQI,EAKR,CAAE,UAAAH,EAAW,cAAAE,CAAA,EAAkBL,EAAe,OAAQE,CAAK,gBAnB/DK,YAAA,EAAAC,qBAEM,MAFNC,EAEM,CADJC,cAAmDC,EAAAA,MAAAE,EAAAA,IAAA,EAAA,CAA5C,KAAMF,EAAAA,MAAAR,CAAA,EAAY,QAASQ,EAAAA,MAAAN,CAAA,CAAA,+PCetCrD,EAAA,EAuBA,MAAMkD,EAAQI,EAKRQ,EAAmBV,EAAAA,SAAS,IAChCF,EAAM,UAAY,CAAC,CAAE,KAAMA,EAAM,MAAQ,EAAC,CAAG,CAAA,EAGzCC,EAAYC,EAAAA,SAAS,KAAO,CAChC,OAAQF,EAAM,OACd,SAAUb,EAAayB,EAAiB,MAAO,WAAYZ,EAAM,OAAO,MAAM,CAAA,EAC9E,EAEIG,EAAgBD,EAAAA,SAAS,IAC7B1D,EACE6B,EAAY,CACV,YAAa2B,EAAM,YACnB,eAAgBA,EAAM,eACtB,OAAQA,EAAM,OACd,WAAYA,EAAM,YAAc,GAChC,aAAc,EAAA,CACf,EACDA,EAAM,SAAW,CAAA,CAAC,CACpB,gBA/DAK,YAAA,EAAAC,qBAEM,MAFNC,EAEM,CADJC,cAAuDC,EAAAA,MAAAI,EAAAA,QAAA,EAAA,CAA5C,KAAMZ,EAAA,MAAY,QAASE,EAAA,KAAA"}
@@ -67,9 +67,9 @@ type __VLS_Slots = {} & {
67
67
  default?: (props: typeof __VLS_6) => any;
68
68
  };
69
69
  declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
70
+ loading: boolean;
70
71
  variant: ButtonVariant | LinkVariant | null;
71
72
  disabled: boolean;
72
- loading: boolean;
73
73
  block: boolean;
74
74
  spinnerDelay: number;
75
75
  minSpinnerTime: number;
@@ -1,21 +1,6 @@
1
- import { type ChartValueFormat } from "./chartTheme";
2
- interface Props {
3
- /** X-axis category labels. */
4
- labels: string[];
5
- /** Chart.js datasets. Colours are themed when a dataset omits them. */
6
- datasets: any[];
7
- /** Chart.js options; deep-merged over the themed defaults. */
8
- options?: Record<string, any>;
9
- /** Format value-axis ticks and tooltips. */
10
- valueFormat?: ChartValueFormat;
11
- /** Currency symbol for valueFormat: "currency" (default "£"). */
12
- currencySymbol?: string;
13
- /** Locale for number formatting. */
14
- locale?: string;
15
- /** Show the legend (default: only when there is more than one dataset). */
16
- showLegend?: boolean;
17
- }
18
- declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
1
+ import { type ThemedChartProps } from "./useThemedChart";
2
+ type __VLS_Props = ThemedChartProps<"bar">;
3
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
19
4
  currencySymbol: string;
20
5
  showLegend: boolean;
21
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,21 +1,6 @@
1
- import { type ChartValueFormat } from "./chartTheme";
2
- interface Props {
3
- /** X-axis category labels. */
4
- labels: string[];
5
- /** Chart.js datasets. Colours are themed when a dataset omits them. */
6
- datasets: any[];
7
- /** Chart.js options; deep-merged over the themed defaults. */
8
- options?: Record<string, any>;
9
- /** Format value-axis ticks and tooltips. */
10
- valueFormat?: ChartValueFormat;
11
- /** Currency symbol for valueFormat: "currency" (default "£"). */
12
- currencySymbol?: string;
13
- /** Locale for number formatting. */
14
- locale?: string;
15
- /** Show the legend (default: only when there is more than one dataset). */
16
- showLegend?: boolean;
17
- }
18
- declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
1
+ import { type ThemedChartProps } from "./useThemedChart";
2
+ type __VLS_Props = ThemedChartProps<"line">;
3
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
19
4
  currencySymbol: string;
20
5
  showLegend: boolean;
21
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;