@omnitend/dashboard-for-laravel 0.28.0 → 0.30.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 (40) hide show
  1. package/dist/charts.d.ts +19 -0
  2. package/dist/charts.js +225 -0
  3. package/dist/charts.js.map +1 -0
  4. package/dist/charts.umd.cjs +2 -0
  5. package/dist/charts.umd.cjs.map +1 -0
  6. package/dist/components/base/DAlert.vue.d.ts +6 -1
  7. package/dist/components/base/DAutocomplete.vue.d.ts +21 -1
  8. package/dist/components/base/DFormCheckboxGroup.vue.d.ts +12 -0
  9. package/dist/components/extended/DXDashboardSidebar.vue.d.ts +2 -2
  10. package/dist/components/extended/DXField.vue.d.ts +4 -4
  11. package/dist/components/extended/DXForm.vue.d.ts +15 -4
  12. package/dist/components/extended/DXTable.vue.d.ts +39 -1
  13. package/dist/components/extended/DXTablePagination.vue.d.ts +2 -0
  14. package/dist/dashboard-for-laravel.js +8826 -8937
  15. package/dist/dashboard-for-laravel.js.map +1 -1
  16. package/dist/dashboard-for-laravel.umd.cjs +7 -7
  17. package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
  18. package/dist/index.d.ts +1 -3
  19. package/dist/style.css +1 -1
  20. package/dist/types/index.d.ts +1 -1
  21. package/docs/public/api-reference.json +84 -9
  22. package/docs/public/docs-map.md +1 -1
  23. package/docs/public/llms.txt +3 -2
  24. package/package.json +11 -5
  25. package/resources/css/theme.scss +21 -7
  26. package/resources/js/charts.ts +19 -0
  27. package/resources/js/components/base/DAlert.vue +19 -1
  28. package/resources/js/components/base/DAutocomplete.vue +96 -4
  29. package/resources/js/components/base/DFormCheckboxGroup.vue +22 -0
  30. package/resources/js/components/charts/DXBarChart.vue +0 -9
  31. package/resources/js/components/charts/DXDoughnutChart.vue +0 -9
  32. package/resources/js/components/charts/DXLineChart.vue +0 -9
  33. package/resources/js/components/extended/DXField.vue +12 -0
  34. package/resources/js/components/extended/DXForm.vue +25 -5
  35. package/resources/js/components/extended/DXTable.vue +139 -19
  36. package/resources/js/components/extended/DXTableEditorModal.vue +4 -0
  37. package/resources/js/components/extended/DXTablePagination.vue +5 -2
  38. package/resources/js/composables/useForm.ts +30 -3
  39. package/resources/js/index.ts +4 -3
  40. package/resources/js/types/index.ts +1 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Chart entry point — `@omnitend/dashboard-for-laravel/charts` (#142).
3
+ *
4
+ * The chart components are the ONLY ones that need `chart.js` / `vue-chartjs`,
5
+ * so they live behind this separate subpath entry. The main entry
6
+ * (`@omnitend/dashboard-for-laravel`) never references the chart libraries, so
7
+ * they are genuinely OPTIONAL peers: a consumer that renders no charts installs
8
+ * neither and their build no longer fails resolving them.
9
+ *
10
+ * import { DXBarChart } from '@omnitend/dashboard-for-laravel/charts';
11
+ *
12
+ * Chart containers are styled by `.dx-chart` in the main stylesheet
13
+ * (`@omnitend/dashboard-for-laravel/style.css`), which chart consumers already
14
+ * import for the theme and the `--dx-chart-*` palette variables — so this entry
15
+ * ships no CSS of its own.
16
+ */
17
+ export { default as DXBarChart } from "./components/charts/DXBarChart.vue";
18
+ export { default as DXLineChart } from "./components/charts/DXLineChart.vue";
19
+ export { default as DXDoughnutChart } from "./components/charts/DXDoughnutChart.vue";
package/dist/charts.js ADDED
@@ -0,0 +1,225 @@
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";
4
+ const D = (o) => !!o && typeof o == "object" && !Array.isArray(o);
5
+ function p(o, e) {
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;
9
+ return t;
10
+ }
11
+ let L = !1;
12
+ function y() {
13
+ L || (R.register(...X), L = !0);
14
+ }
15
+ const A = [
16
+ ["--dx-chart-1", "#2563eb"],
17
+ // blue
18
+ ["--dx-chart-2", "#65a30d"],
19
+ // lime
20
+ ["--dx-chart-3", "#7c3aed"],
21
+ // violet
22
+ ["--dx-chart-4", "#0d9488"],
23
+ // teal
24
+ ["--dx-chart-5", "#ea580c"],
25
+ // orange
26
+ ["--dx-chart-6", "#0891b2"],
27
+ // cyan
28
+ ["--dx-chart-7", "#d97706"],
29
+ // amber
30
+ ["--dx-chart-8", "#db2777"]
31
+ // pink
32
+ ];
33
+ function E() {
34
+ if (typeof document > "u" || typeof getComputedStyle > "u")
35
+ return A.map(([, e]) => e);
36
+ const o = getComputedStyle(document.documentElement);
37
+ return A.map(([e, t]) => o.getPropertyValue(e).trim() || t);
38
+ }
39
+ function F(o, e) {
40
+ return typeof document > "u" || typeof getComputedStyle > "u" ? e : getComputedStyle(document.documentElement).getPropertyValue(o).trim() || e;
41
+ }
42
+ function B(o, e) {
43
+ const t = o.replace(/^#/, "");
44
+ if (/^[0-9a-f]{6}$/i.test(t)) {
45
+ const n = parseInt(t.slice(0, 2), 16), s = parseInt(t.slice(2, 4), 16), l = parseInt(t.slice(4, 6), 16);
46
+ return `rgba(${n}, ${s}, ${l}, ${e})`;
47
+ }
48
+ const a = o.match(/^rgba?\(([^)]+)\)$/);
49
+ if (a) {
50
+ const [n, s, l] = a[1].split(",").map((r) => r.trim());
51
+ return `rgba(${n}, ${s}, ${l}, ${e})`;
52
+ }
53
+ return o;
54
+ }
55
+ function k(o, e, t, a) {
56
+ const n = new Intl.NumberFormat(a, {
57
+ maximumFractionDigits: 2
58
+ }).format(o);
59
+ return e === "currency" ? `${t}${n}` : e === "percent" ? `${n}%` : n;
60
+ }
61
+ function v(o) {
62
+ const {
63
+ valueFormat: e,
64
+ currencySymbol: t = "£",
65
+ locale: a,
66
+ showLegend: n = !1,
67
+ hasValueAxis: s = !0
68
+ } = o, l = B(F("--bs-border-color", "#dee2e6"), 0.6), r = F("--bs-secondary-color", "#6c757d"), u = {
69
+ responsive: !0,
70
+ maintainAspectRatio: !1,
71
+ plugins: {
72
+ legend: { display: n, labels: { color: r } },
73
+ tooltip: {
74
+ callbacks: {
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;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ };
84
+ return s && (u.scales = {
85
+ x: {
86
+ grid: { display: !1 },
87
+ border: { color: l },
88
+ ticks: { color: r }
89
+ },
90
+ y: {
91
+ beginAtZero: !0,
92
+ grid: { color: l, drawBorder: !1 },
93
+ border: { display: !1 },
94
+ ticks: {
95
+ color: r,
96
+ callback: (c) => k(Number(c), e, t, a)
97
+ }
98
+ }
99
+ }), u;
100
+ }
101
+ function _(o, e, t) {
102
+ const a = E(), n = o.length === 1;
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(
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);
109
+ });
110
+ }
111
+ const N = { class: "dx-chart" }, q = /* @__PURE__ */ m({
112
+ __name: "DXBarChart",
113
+ props: {
114
+ labels: {},
115
+ datasets: {},
116
+ options: {},
117
+ valueFormat: {},
118
+ currencySymbol: { default: "£" },
119
+ locale: {},
120
+ showLegend: { type: Boolean, default: void 0 }
121
+ },
122
+ setup(o) {
123
+ 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
143
+ }, null, 8, ["data", "options"])
144
+ ]));
145
+ }
146
+ }), I = { class: "dx-chart" }, z = /* @__PURE__ */ m({
147
+ __name: "DXLineChart",
148
+ props: {
149
+ labels: {},
150
+ datasets: {},
151
+ options: {},
152
+ valueFormat: {},
153
+ currencySymbol: { default: "£" },
154
+ locale: {},
155
+ showLegend: { type: Boolean, default: void 0 }
156
+ },
157
+ setup(o) {
158
+ 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
178
+ }, null, 8, ["data", "options"])
179
+ ]));
180
+ }
181
+ }), j = { class: "dx-chart" }, G = /* @__PURE__ */ m({
182
+ __name: "DXDoughnutChart",
183
+ props: {
184
+ labels: {},
185
+ data: {},
186
+ datasets: {},
187
+ options: {},
188
+ valueFormat: {},
189
+ currencySymbol: { default: "£" },
190
+ locale: {},
191
+ showLegend: { type: Boolean, default: void 0 }
192
+ },
193
+ setup(o) {
194
+ y();
195
+ const e = o, t = i(
196
+ () => e.datasets ?? [{ data: e.data ?? [] }]
197
+ ), a = i(() => ({
198
+ labels: e.labels,
199
+ datasets: _(t.value, "doughnut", e.labels.length)
200
+ })), n = i(
201
+ () => p(
202
+ v({
203
+ valueFormat: e.valueFormat,
204
+ currencySymbol: e.currencySymbol,
205
+ locale: e.locale,
206
+ showLegend: e.showLegend ?? !0,
207
+ hasValueAxis: !1
208
+ }),
209
+ e.options ?? {}
210
+ )
211
+ );
212
+ return (s, l) => (f(), b("div", j, [
213
+ h(g(P), {
214
+ data: a.value,
215
+ options: n.value
216
+ }, null, 8, ["data", "options"])
217
+ ]));
218
+ }
219
+ });
220
+ export {
221
+ q as DXBarChart,
222
+ G as DXDoughnutChart,
223
+ z as DXLineChart
224
+ };
225
+ //# sourceMappingURL=charts.js.map
@@ -0,0 +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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"}
@@ -0,0 +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"})}));
2
+ //# sourceMappingURL=charts.umd.cjs.map
@@ -0,0 +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,8 +1,13 @@
1
+ type __VLS_Props = {
2
+ modelValue?: boolean | number;
3
+ };
1
4
  declare var __VLS_7: string | number, __VLS_8: {};
2
5
  type __VLS_Slots = {} & {
3
6
  [K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
4
7
  };
5
- declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
8
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
9
+ modelValue: boolean | number;
10
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
11
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
7
12
  export default _default;
8
13
  type __VLS_WithSlots<T, S> = T & {
@@ -1,8 +1,28 @@
1
+ import type { SelectOption } from "bootstrap-vue-next";
2
+ type AutocompleteOptions = readonly (object | string | number | boolean)[];
3
+ type FilterFunction = (options: SelectOption[], search: string) => SelectOption[];
4
+ interface Props {
5
+ /** Options passed straight through to BAutocomplete (see its `options` prop). */
6
+ options?: AutocompleteOptions;
7
+ /**
8
+ * A custom filter. Composed with `null-option` rather than replaced: when a
9
+ * null option is present it stays pinned and this runs on the rest.
10
+ */
11
+ filterFunction?: FilterFunction;
12
+ /**
13
+ * Prepend a pinned, never-filtered, selectable "no selection" row whose value
14
+ * is `null` (#138). `true` → default label "None"; a string → that label.
15
+ * Selecting it sets the model to `null` (bvn reads that as no selection, so
16
+ * the ✕ hides and the placeholder shows). Off by default — unchanged
17
+ * behaviour. DXTable does NOT use this; it builds its own "All" option.
18
+ */
19
+ nullOption?: boolean | string;
20
+ }
1
21
  declare var __VLS_6: string | number, __VLS_7: any;
2
22
  type __VLS_Slots = {} & {
3
23
  [K in NonNullable<typeof __VLS_6>]?: (props: typeof __VLS_7) => any;
4
24
  };
5
- declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
25
+ declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
26
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
7
27
  export default _default;
8
28
  type __VLS_WithSlots<T, S> = T & {
@@ -0,0 +1,12 @@
1
+ declare var __VLS_7: string | number, __VLS_8: any;
2
+ type __VLS_Slots = {} & {
3
+ [K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
4
+ };
5
+ declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
7
+ export default _default;
8
+ type __VLS_WithSlots<T, S> = T & {
9
+ new (): {
10
+ $slots: S;
11
+ };
12
+ };
@@ -46,9 +46,9 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}
46
46
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
47
47
  onToggle?: (() => any) | undefined;
48
48
  }>, {
49
- collapsed: boolean;
50
- hidden: boolean;
51
49
  title: string;
50
+ hidden: boolean;
51
+ collapsed: boolean;
52
52
  collapsibleGroups: boolean;
53
53
  autoCollapseInactiveGroups: boolean;
54
54
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -112,10 +112,10 @@ declare var __VLS_1: {
112
112
  model: any;
113
113
  value: any;
114
114
  update: typeof setValue;
115
- }, __VLS_198: {
115
+ }, __VLS_201: {
116
116
  field: FieldDefinition;
117
117
  model: any;
118
- }, __VLS_203: {
118
+ }, __VLS_206: {
119
119
  field: FieldDefinition;
120
120
  model: any;
121
121
  };
@@ -160,9 +160,9 @@ type __VLS_Slots = {} & {
160
160
  } & {
161
161
  value?: (props: typeof __VLS_133) => any;
162
162
  } & {
163
- info?: (props: typeof __VLS_198) => any;
163
+ info?: (props: typeof __VLS_201) => any;
164
164
  } & {
165
- hint?: (props: typeof __VLS_203) => any;
165
+ hint?: (props: typeof __VLS_206) => any;
166
166
  };
167
167
  declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
168
168
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
@@ -28,11 +28,21 @@ interface Props {
28
28
  autoErrorTab?: boolean;
29
29
  /**
30
30
  * Wrap the form in a card for a visual boundary (mirrors DXTable's
31
- * `card` prop). Tabbed forms render the tab nav as a BS5
32
- * card-header-tabs instead of double-wrapping. Off by default since
33
- * DXForm is commonly embedded in a page card or modal already.
31
+ * `card` prop). Off by default since DXForm is commonly embedded in a
32
+ * page card or modal already. Tabbed forms are wrapped by default
33
+ * regardless — see `cardTabs`; setting `card` also forces the tabbed
34
+ * card on even when `cardTabs` is disabled.
34
35
  */
35
36
  card?: boolean;
37
+ /**
38
+ * Wrap a TABBED form's content in a card panel so the active tab reads
39
+ * as a finished panel connected to the tab strip (the standard
40
+ * Bootstrap card-with-tabs pattern) rather than floating on the bare
41
+ * page background (#159). On by default. Set `false` for bare tabs
42
+ * (e.g. inside a modal that already provides a boundary). Ignored for
43
+ * flat (non-tabbed) forms — use `card` for those.
44
+ */
45
+ cardTabs?: boolean;
36
46
  /**
37
47
  * Form-wide field layout: "vertical" (default, label above input) or
38
48
  * "horizontal" (label left, input right). Overridable per-field via
@@ -63,12 +73,13 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps,
63
73
  onSubmit?: (() => any) | undefined;
64
74
  "onUpdate:activeTab"?: ((value: number) => any) | undefined;
65
75
  }>, {
66
- card: boolean;
67
76
  layout: "vertical" | "horizontal";
77
+ card: boolean;
68
78
  submitText: string;
69
79
  submitLoadingText: string;
70
80
  showSubmit: boolean;
71
81
  autoErrorTab: boolean;
82
+ cardTabs: boolean;
72
83
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
73
84
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
74
85
  export default _default;
@@ -36,6 +36,19 @@ export interface TableField {
36
36
  filter?: FilterType;
37
37
  filterOptions?: FilterOption[];
38
38
  filterPlaceholder?: string;
39
+ /**
40
+ * Fixed width for this column, applied via a `<colgroup>` `<col>`. A number
41
+ * is treated as pixels (`200` → `200px`); a string is used verbatim
42
+ * (`"20%"`, `"12rem"`). Pair with the table-level `fixedLayout` prop so the
43
+ * width is authoritative and the column stops resizing to fit its content —
44
+ * without `fixedLayout` a wider cell can still stretch the column (#156).
45
+ */
46
+ width?: string | number;
47
+ /**
48
+ * Minimum width for this column, applied via the same `<col>` as `width`.
49
+ * A number is pixels; a string is used verbatim.
50
+ */
51
+ minWidth?: string | number;
39
52
  [key: string]: any;
40
53
  }
41
54
  export interface PaginationData {
@@ -109,11 +122,27 @@ export interface Props<TItem = any> {
109
122
  showPagination?: boolean;
110
123
  /** Show per-page selector */
111
124
  showPerPageSelector?: boolean;
125
+ /**
126
+ * Show the footer's item-count caption ("N items." / "X to Y out of Z
127
+ * items."). Defaults to `true`. Set `false` to suppress just the caption,
128
+ * independent of the pager — a page ported from a plain `<b-table>` that
129
+ * hides the pager with `:show-pagination="false"` would otherwise gain a
130
+ * caption it never had (#127).
131
+ */
132
+ showCount?: boolean;
112
133
  /** Per-page options for selector */
113
134
  perPageOptions?: number[];
114
135
  /** Current page (for provider mode) */
115
136
  currentPage?: number;
116
- /** Items per page (for provider mode, v-model support) */
137
+ /**
138
+ * Items per page. With a `v-model:per-page` it is controlled state; a bare
139
+ * `:per-page="20"` is the INITIAL page size (see the initial-value vs
140
+ * controlled note above). When passed explicitly it takes PRECEDENCE over any
141
+ * per-page preference persisted in localStorage — `:per-page="20"` always
142
+ * starts at 20, never a size the user once picked on another table (#124).
143
+ * Omit it to let a stored preference (URL-keyed tables only) or the default
144
+ * apply.
145
+ */
117
146
  perPage?: number;
118
147
  /** Striped (banded) rows. Off by default — banding adds little on short
119
148
  * tables and can read as noise; opt in with `:striped="true"`. */
@@ -217,6 +246,15 @@ export interface Props<TItem = any> {
217
246
  rowClickable?: (item: TItem, index: number) => boolean;
218
247
  /** Enable client-side filtering, sorting, and pagination on items array */
219
248
  clientSide?: boolean;
249
+ /**
250
+ * Apply `table-layout: fixed` to the underlying table. Off by default, so
251
+ * existing tables keep their content-sized columns. When on, column widths
252
+ * stop depending on cell content, so they no longer reshuffle when a filter
253
+ * narrows the rows — the table stays put as you filter (#156). Combine with
254
+ * per-field `width`/`minWidth` to control the proportions; columns with no
255
+ * declared width share the remaining space equally.
256
+ */
257
+ fixedLayout?: boolean;
220
258
  }
221
259
  declare const _default: <T = any>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
222
260
  props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
@@ -10,6 +10,8 @@ interface Props {
10
10
  showPagination: boolean;
11
11
  /** Whether to render the per-page selector. */
12
12
  showPerPageSelector: boolean;
13
+ /** Whether to render the item-count caption (the "X out of Z items" line). */
14
+ showCount: boolean;
13
15
  /** Singular item noun for the info line (e.g. "product"). */
14
16
  singularItemName: string;
15
17
  /** Plural item noun for the info line (e.g. "products"). */