@omnitend/dashboard-for-laravel 0.41.2 → 0.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/assets/MavenPro-Bold-35b16f06.woff2 +0 -0
- package/dist/assets/MavenPro-Medium-febc8674.woff2 +0 -0
- package/dist/assets/MavenPro-Regular-aee3df9e.woff2 +0 -0
- package/dist/assets/MavenPro-SemiBold-c555dbfa.woff2 +0 -0
- package/dist/assets/Poppins-Medium-5800fa57.woff2 +0 -0
- package/dist/assets/Poppins-SemiBold-94a36267.woff2 +0 -0
- package/dist/charts.js +148 -130
- package/dist/charts.js.map +1 -1
- package/dist/charts.umd.cjs +1 -1
- package/dist/charts.umd.cjs.map +1 -1
- package/dist/components/base/DAutocomplete.vue.d.ts +2 -2
- package/dist/components/charts/chartTheme.d.ts +14 -2
- package/dist/components/extended/DXField.vue.d.ts +16 -5
- package/dist/components/extended/DXForm.vue.d.ts +25 -9
- package/dist/components/extended/DXNumberStepper.vue.d.ts +33 -0
- package/dist/components/extended/DXSaveButton.vue.d.ts +38 -0
- package/dist/components/extended/DXTable.vue.d.ts +6 -2
- package/dist/composables/useForm.d.ts +7 -0
- package/dist/dashboard-for-laravel.js +9317 -9052
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +7 -7
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/style.css +2 -2
- package/dist/types/index.d.ts +20 -3
- package/docs/public/api-reference.json +165 -14
- package/docs/public/docs-map.md +4 -2
- package/docs/public/llms.txt +4 -2
- package/package.json +1 -1
- package/resources/css/theme.scss +558 -89
- package/resources/fonts/maven-pro/MavenPro-Bold.woff2 +0 -0
- package/resources/fonts/maven-pro/MavenPro-Medium.woff2 +0 -0
- package/resources/fonts/maven-pro/MavenPro-Regular.woff2 +0 -0
- package/resources/fonts/maven-pro/MavenPro-SemiBold.woff2 +0 -0
- package/resources/fonts/maven-pro/OFL.txt +93 -0
- package/resources/fonts/poppins/OFL.txt +93 -0
- package/resources/fonts/poppins/Poppins-Medium.woff2 +0 -0
- package/resources/fonts/poppins/Poppins-SemiBold.woff2 +0 -0
- package/resources/js/components/base/DAutocomplete.vue +73 -2
- package/resources/js/components/charts/DXBarChart.vue +2 -1
- package/resources/js/components/charts/DXDoughnutChart.vue +2 -1
- package/resources/js/components/charts/DXLineChart.vue +2 -1
- package/resources/js/components/charts/chartTheme.ts +68 -21
- package/resources/js/components/extended/DXDashboard.vue +4 -2
- package/resources/js/components/extended/DXDashboardSidebar.vue +52 -20
- package/resources/js/components/extended/DXField.vue +59 -28
- package/resources/js/components/extended/DXForm.vue +122 -21
- package/resources/js/components/extended/DXNumberStepper.vue +243 -0
- package/resources/js/components/extended/DXSaveButton.vue +82 -0
- package/resources/js/components/extended/DXTable.vue +44 -15
- package/resources/js/composables/useForm.ts +11 -0
- package/resources/js/composables/useToast.ts +1 -1
- package/resources/js/index.ts +2 -0
- package/resources/js/types/index.ts +21 -3
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2011 The Maven Pro Project Authors (http://www.vissol.co.uk/mavenpro/), with Reserved Font Name "Maven Pro".
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
Binary file
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* searchable single-select can always reach "none" in-list (#138).
|
|
10
10
|
*/
|
|
11
11
|
<script setup lang="ts">
|
|
12
|
-
import { computed, useAttrs } from "vue";
|
|
12
|
+
import { computed, ref, useAttrs, watch } from "vue";
|
|
13
13
|
import { BAutocomplete } from "bootstrap-vue-next";
|
|
14
14
|
import type { SelectOption } from "bootstrap-vue-next";
|
|
15
15
|
|
|
@@ -127,6 +127,75 @@ const hasValue = computed(() => {
|
|
|
127
127
|
const noClearButton = computed(
|
|
128
128
|
() => attr("noClearButton", "no-clear-button") === true || !hasValue.value,
|
|
129
129
|
);
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* Late options (0.42.1). bvn's BAutocomplete writes the input's text from the
|
|
133
|
+
* model value through reka-ui's ComboboxInput, which does so on mount and
|
|
134
|
+
* again only when the MODEL changes. A value set before its options load (a
|
|
135
|
+
* DXTable select filter seeded from the URL while the option list is still
|
|
136
|
+
* being fetched) therefore kept showing the raw value ("78") after its label
|
|
137
|
+
* ("Alcopop") arrived. bvn exposes no "resync" method, but the input's text
|
|
138
|
+
* is its `search` v-model, so this wrapper owns that model and, whenever the
|
|
139
|
+
* options change, puts the matching option's label in it.
|
|
140
|
+
*
|
|
141
|
+
* It never overwrites a search the user is typing: while the input has
|
|
142
|
+
* focus, it relabels only text that is still exactly the raw value. Multiple
|
|
143
|
+
* mode needs nothing (bvn derives the chips from the options on every
|
|
144
|
+
* render). A consumer that binds its own `search` owns the text, so it is
|
|
145
|
+
* left alone. A `:key` remount would also work but drops focus and the open
|
|
146
|
+
* menu, so the `search` model is the cleaner hook.
|
|
147
|
+
*/
|
|
148
|
+
const rootElement = ref<HTMLElement | null>(null);
|
|
149
|
+
const searchText = ref("");
|
|
150
|
+
const consumerOwnsSearch = computed(() => attr("search", "search") !== undefined);
|
|
151
|
+
const boundSearch = computed<string>(() =>
|
|
152
|
+
consumerOwnsSearch.value ? String(attr("search", "search")) : searchText.value,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const optionValueAndText = (option: unknown): { value: unknown; text: unknown } => {
|
|
156
|
+
if (option === null || typeof option !== "object") return { value: option, text: option };
|
|
157
|
+
const valueField = (attr("valueField", "value-field") as string | undefined) ?? "value";
|
|
158
|
+
const textField = (attr("textField", "text-field") as string | undefined) ?? "text";
|
|
159
|
+
const record = option as Record<string, unknown>;
|
|
160
|
+
return { value: record[valueField], text: record[textField] };
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// The same equality bvn uses to find the selected option (strict, or by key).
|
|
164
|
+
const sameValue = (optionValue: unknown, modelValue: unknown) => {
|
|
165
|
+
const by = attr("by", "by");
|
|
166
|
+
if (typeof by === "string") {
|
|
167
|
+
return (
|
|
168
|
+
(optionValue as Record<string, unknown> | null)?.[by] ===
|
|
169
|
+
(modelValue as Record<string, unknown> | null)?.[by]
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return optionValue === modelValue;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// A bare `multiple` in a template arrives as "" (it is not a declared prop).
|
|
176
|
+
const isMultiple = computed(() => {
|
|
177
|
+
const multiple = attr("multiple", "multiple");
|
|
178
|
+
return multiple !== undefined && multiple !== null && multiple !== false;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const relabelFromOptions = () => {
|
|
182
|
+
if (isMultiple.value || consumerOwnsSearch.value) return;
|
|
183
|
+
const modelValue = attr("modelValue", "model-value");
|
|
184
|
+
if (modelValue === null || modelValue === undefined || modelValue === "") return;
|
|
185
|
+
const match = computedOptions.value
|
|
186
|
+
.map(optionValueAndText)
|
|
187
|
+
.find((option) => sameValue(option.value, modelValue));
|
|
188
|
+
if (!match) return;
|
|
189
|
+
const label = String(match.text ?? "");
|
|
190
|
+
if (searchText.value === label) return;
|
|
191
|
+
const input = rootElement.value?.querySelector("input");
|
|
192
|
+
const userIsTyping =
|
|
193
|
+
input !== null && input !== undefined && document.activeElement === input;
|
|
194
|
+
if (userIsTyping && searchText.value !== String(modelValue)) return;
|
|
195
|
+
searchText.value = label;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
watch(computedOptions, relabelFromOptions);
|
|
130
199
|
</script>
|
|
131
200
|
|
|
132
201
|
<template>
|
|
@@ -138,12 +207,14 @@ const noClearButton = computed(
|
|
|
138
207
|
root, so `[data-v-x] .input-group` matches nothing in a consumer build
|
|
139
208
|
(#53). A plain element root makes it deterministic.
|
|
140
209
|
-->
|
|
141
|
-
<div class="d-autocomplete">
|
|
210
|
+
<div ref="rootElement" class="d-autocomplete">
|
|
142
211
|
<BAutocomplete
|
|
143
212
|
v-bind="$attrs"
|
|
213
|
+
:search="boundSearch"
|
|
144
214
|
:options="computedOptions"
|
|
145
215
|
:filter-function="effectiveFilterFunction"
|
|
146
216
|
:no-clear-button="noClearButton"
|
|
217
|
+
@update:search="searchText = $event"
|
|
147
218
|
>
|
|
148
219
|
<!-- Dynamically pass through all named slots with their props -->
|
|
149
220
|
<template v-for="(_, name) in $slots" :key="name" #[name]="slotProps">
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component
|
|
3
3
|
A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the
|
|
4
|
-
theme's dedicated chart palette (--dx-chart-1..8
|
|
4
|
+
theme's dedicated chart palette (--dx-chart-1..8 fills, a 1px --dx-chart-edge outline) applied
|
|
5
|
+
and dashboard-friendly defaults (no x gridlines,
|
|
5
6
|
formatted value ticks, responsive, legend hidden for a single series). Pass
|
|
6
7
|
`labels` + `datasets`; `options` deep-merges over the defaults.
|
|
7
8
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component
|
|
3
3
|
A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one
|
|
4
|
-
chart-palette colour (--dx-chart-1..8) per slice
|
|
4
|
+
chart-palette colour (--dx-chart-1..8) per slice, a 1px --dx-chart-edge outline, and
|
|
5
|
+
dashboard-friendly defaults. Pass
|
|
5
6
|
`labels` + either `data` (a number array) or full `datasets`; `options`
|
|
6
7
|
deep-merges over the defaults.
|
|
7
8
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component
|
|
3
3
|
A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the
|
|
4
|
-
theme's dedicated chart palette applied (--dx-chart-1..8 stroke
|
|
4
|
+
theme's dedicated chart palette applied (--dx-chart-line-1..8 stroke and point outline,
|
|
5
|
+
--dx-chart-1..8 point centre and translucent area fill, smoothed line)
|
|
5
6
|
and dashboard-friendly defaults. Pass `labels` + `datasets`; `options`
|
|
6
7
|
deep-merges over the defaults.
|
|
7
8
|
|
|
@@ -42,24 +42,42 @@ export function registerCharts(): void {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// The dedicated data-viz palette (`$dx-chart-palette` in theme.scss) — eight
|
|
45
|
-
// vivid
|
|
46
|
-
// Deliberately NOT the semantic --bs-* colours
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
// that parses the Sass source (charts.test.ts), which is why this is exported.
|
|
45
|
+
// light, vivid fills in a fixed, CVD-validated order, published as
|
|
46
|
+
// --dx-chart-1..8. Deliberately NOT read from the semantic --bs-* colours
|
|
47
|
+
// (#141). Fallbacks mirror the shipped theme so charts still render without
|
|
48
|
+
// the CSS (SSR / tests / a consumer who didn't import it). Sync with
|
|
49
|
+
// theme.scss is enforced by a test that parses the Sass source
|
|
50
|
+
// (charts.test.ts), which is why these lists are exported.
|
|
52
51
|
export const PALETTE_VARS: Array<[string, string]> = [
|
|
53
|
-
["--dx-chart-1", "#
|
|
54
|
-
["--dx-chart-2", "#
|
|
55
|
-
["--dx-chart-3", "#
|
|
56
|
-
["--dx-chart-4", "#
|
|
57
|
-
["--dx-chart-5", "#
|
|
58
|
-
["--dx-chart-6", "#
|
|
59
|
-
["--dx-chart-7", "#
|
|
60
|
-
["--dx-chart-8", "#
|
|
52
|
+
["--dx-chart-1", "#7fd7fd"], // sky
|
|
53
|
+
["--dx-chart-2", "#7bf25a"], // lime
|
|
54
|
+
["--dx-chart-3", "#e46ab9"], // magenta
|
|
55
|
+
["--dx-chart-4", "#d6e86a"], // lime-yellow
|
|
56
|
+
["--dx-chart-5", "#b9a3f0"], // lavender
|
|
57
|
+
["--dx-chart-6", "#efd574"], // butter
|
|
58
|
+
["--dx-chart-7", "#9fb4ff"], // periwinkle
|
|
59
|
+
["--dx-chart-8", "#f7a072"], // peach
|
|
61
60
|
];
|
|
62
61
|
|
|
62
|
+
// The line/point shade for each slot (`$dx-chart-line-palette`): the fill
|
|
63
|
+
// darkened to 3.5:1 on white, so a line stroke or point outline clears WCAG
|
|
64
|
+
// 1.4.11 non-text contrast where the light fill alone would not. Same order.
|
|
65
|
+
export const LINE_PALETTE_VARS: Array<[string, string]> = [
|
|
66
|
+
["--dx-chart-line-1", "#5590aa"],
|
|
67
|
+
["--dx-chart-line-2", "#4d9839"],
|
|
68
|
+
["--dx-chart-line-3", "#d161a9"],
|
|
69
|
+
["--dx-chart-line-4", "#848f41"],
|
|
70
|
+
["--dx-chart-line-5", "#907fbb"],
|
|
71
|
+
["--dx-chart-line-6", "#99884a"],
|
|
72
|
+
["--dx-chart-line-7", "#7686be"],
|
|
73
|
+
["--dx-chart-line-8", "#ba7956"],
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// The 1px outline on bars and doughnut segments (`$dx-chart-edge`): it carries
|
|
77
|
+
// the non-text contrast for the light fills. Dark mode remaps it to the dark
|
|
78
|
+
// body colour.
|
|
79
|
+
export const EDGE_VAR: [string, string] = ["--dx-chart-edge", "#121419"];
|
|
80
|
+
|
|
63
81
|
/**
|
|
64
82
|
* The element whose computed style the theme variables are read from.
|
|
65
83
|
*
|
|
@@ -86,12 +104,29 @@ function themeScope(scope?: Element | null): Element | null {
|
|
|
86
104
|
* backward compatible.
|
|
87
105
|
*/
|
|
88
106
|
export function getPalette(scope?: Element | null): string[] {
|
|
107
|
+
return resolveVars(PALETTE_VARS, scope);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Resolve the themed line/point shades (`--dx-chart-line-1..8`), slot for slot
|
|
112
|
+
* with getPalette(). Same scope rules as getPalette().
|
|
113
|
+
*/
|
|
114
|
+
export function getLinePalette(scope?: Element | null): string[] {
|
|
115
|
+
return resolveVars(LINE_PALETTE_VARS, scope);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Resolve the bar/segment edge colour (`--dx-chart-edge`). */
|
|
119
|
+
export function getEdgeColor(scope?: Element | null): string {
|
|
120
|
+
return resolveVars([EDGE_VAR], scope)[0];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveVars(vars: Array<[string, string]>, scope?: Element | null): string[] {
|
|
89
124
|
const host = themeScope(scope);
|
|
90
125
|
if (host === null) {
|
|
91
|
-
return
|
|
126
|
+
return vars.map(([, fallback]) => fallback);
|
|
92
127
|
}
|
|
93
128
|
const styles = getComputedStyle(host);
|
|
94
|
-
return
|
|
129
|
+
return vars.map(([name, fallback]) => {
|
|
95
130
|
const value = styles.getPropertyValue(name).trim();
|
|
96
131
|
return value || fallback;
|
|
97
132
|
});
|
|
@@ -273,8 +308,11 @@ export function baseOptions(args: BaseOptionArgs): Record<string, any> {
|
|
|
273
308
|
|
|
274
309
|
/**
|
|
275
310
|
* Apply the themed palette to any dataset that doesn't set its own colours.
|
|
276
|
-
*
|
|
277
|
-
*
|
|
311
|
+
* Each property is filled in only when the caller left it undefined.
|
|
312
|
+
* Bar: one fill per dataset (or per bar for a single dataset), with a 1px
|
|
313
|
+
* edge outline. Line: the slot's darker line shade for the stroke and point
|
|
314
|
+
* outlines, the fill for point centres, and a 35% fill wash under the line.
|
|
315
|
+
* Doughnut/pie: one fill per slice, with a 1px edge outline.
|
|
278
316
|
*
|
|
279
317
|
* `scope` is the chart's container element; the palette is resolved from its
|
|
280
318
|
* computed style so a nested `data-bs-theme` container themes correctly (#161).
|
|
@@ -287,11 +325,14 @@ export function applyPalette(
|
|
|
287
325
|
scope?: Element | null,
|
|
288
326
|
): any[] {
|
|
289
327
|
const palette = getPalette(scope);
|
|
328
|
+
const linePalette = getLinePalette(scope);
|
|
329
|
+
const edge = getEdgeColor(scope);
|
|
290
330
|
const single = datasets.length === 1;
|
|
291
331
|
|
|
292
332
|
return datasets.map((dataset, index) => {
|
|
293
333
|
const next = { ...dataset };
|
|
294
334
|
const color = palette[index % palette.length];
|
|
335
|
+
const lineColor = linePalette[index % linePalette.length];
|
|
295
336
|
|
|
296
337
|
if (kind === "doughnut") {
|
|
297
338
|
if (next.backgroundColor === undefined) {
|
|
@@ -300,14 +341,18 @@ export function applyPalette(
|
|
|
300
341
|
(_, i) => palette[i % palette.length],
|
|
301
342
|
);
|
|
302
343
|
}
|
|
344
|
+
if (next.borderColor === undefined) next.borderColor = edge;
|
|
345
|
+
if (next.borderWidth === undefined) next.borderWidth = 1;
|
|
303
346
|
return next;
|
|
304
347
|
}
|
|
305
348
|
|
|
306
349
|
if (kind === "line") {
|
|
307
|
-
if (next.borderColor === undefined) next.borderColor =
|
|
350
|
+
if (next.borderColor === undefined) next.borderColor = lineColor;
|
|
308
351
|
if (next.backgroundColor === undefined) {
|
|
309
|
-
next.backgroundColor = withAlpha(color, 0.
|
|
352
|
+
next.backgroundColor = withAlpha(color, 0.35);
|
|
310
353
|
}
|
|
354
|
+
if (next.pointBackgroundColor === undefined) next.pointBackgroundColor = color;
|
|
355
|
+
if (next.pointBorderColor === undefined) next.pointBorderColor = lineColor;
|
|
311
356
|
if (next.tension === undefined) next.tension = 0.3;
|
|
312
357
|
if (next.fill === undefined) next.fill = true;
|
|
313
358
|
if (next.pointRadius === undefined) next.pointRadius = 2;
|
|
@@ -321,6 +366,8 @@ export function applyPalette(
|
|
|
321
366
|
? Array.from({ length: labelsCount }, () => color)
|
|
322
367
|
: color;
|
|
323
368
|
}
|
|
369
|
+
if (next.borderColor === undefined) next.borderColor = edge;
|
|
370
|
+
if (next.borderWidth === undefined) next.borderWidth = 1;
|
|
324
371
|
if (next.borderRadius === undefined) next.borderRadius = 4;
|
|
325
372
|
return next;
|
|
326
373
|
});
|
|
@@ -70,8 +70,10 @@
|
|
|
70
70
|
</template>
|
|
71
71
|
</DXDashboardNavbar>
|
|
72
72
|
|
|
73
|
-
<!-- Page Content
|
|
74
|
-
|
|
73
|
+
<!-- Page Content. Vertical padding only: the horizontal edge is the
|
|
74
|
+
shared dashboard gutter, applied to the container inside (see
|
|
75
|
+
`$dashboard-gutter-x` in theme.scss) so it matches the navbar's. -->
|
|
76
|
+
<main class="dashboard-main py-4">
|
|
75
77
|
<!-- Fluid: full-width, left-aligned content (for wide tables / admin
|
|
76
78
|
pages). Default: a centred reading-width column. -->
|
|
77
79
|
<DContainer v-if="fluid" fluid :class="contentClass">
|
|
@@ -213,34 +213,66 @@ const brandInitial = computed(() => {
|
|
|
213
213
|
const normalizeUrl = (url: string): string =>
|
|
214
214
|
url.toLowerCase().replace(/[?#].*$/, '').replace(/\/$/, '');
|
|
215
215
|
|
|
216
|
+
// An item's query parameters, lowercased, as [name, value] pairs.
|
|
217
|
+
const queryParams = (url: string): [string, string][] => {
|
|
218
|
+
const query = url.toLowerCase().split('#')[0].split('?')[1] ?? '';
|
|
219
|
+
return Array.from(new URLSearchParams(query).entries());
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Identity of a nav item for matching: its path plus its query, so items that
|
|
223
|
+
// differ only by query string (`/tasks?business_unit_id=1`, `...=2`) are told
|
|
224
|
+
// apart.
|
|
225
|
+
const itemKey = (url: string): string =>
|
|
226
|
+
normalizeUrl(url) + '?' + queryParams(url).map(([name, value]) => `${name}=${value}`).sort().join('&');
|
|
227
|
+
|
|
216
228
|
/**
|
|
217
|
-
* The single best-matching nav item
|
|
218
|
-
* match
|
|
219
|
-
* `/rotas/507` activates the `/rotas` item. Root `/` only matches
|
|
220
|
-
* is a prefix of every path, so it is never treated as an
|
|
221
|
-
*
|
|
229
|
+
* The single best-matching nav item for the current route, as an `itemKey`.
|
|
230
|
+
* Paths first: an exact match, otherwise the longest ancestor path, so a detail
|
|
231
|
+
* page like `/rotas/507` activates the `/rotas` item. Root `/` only matches
|
|
232
|
+
* exactly — it is a prefix of every path, so it is never treated as an
|
|
233
|
+
* ancestor. Then, among items on that winning path, queries: an item whose
|
|
234
|
+
* query names parameters only matches when the current URL carries every one of
|
|
235
|
+
* them with the same value (extra current parameters such as `page` are fine),
|
|
236
|
+
* and the item naming the most such parameters wins. An item with no query
|
|
237
|
+
* matches whatever the current query is. If every item on the path names a
|
|
238
|
+
* query the current URL doesn't carry, nothing is active.
|
|
239
|
+
* Returns null if nothing matches.
|
|
222
240
|
*/
|
|
223
|
-
const
|
|
241
|
+
const activeKey = computed<string | null>(() => {
|
|
224
242
|
const current = normalizeUrl(props.currentUrl);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
243
|
+
const currentParams = new URLSearchParams(
|
|
244
|
+
props.currentUrl.toLowerCase().split('#')[0].split('?')[1] ?? '',
|
|
245
|
+
);
|
|
246
|
+
const items = props.navigation
|
|
247
|
+
.filter((group) => group.visible !== false)
|
|
248
|
+
.flatMap((group) => group.items.filter((item) => item.visible !== false));
|
|
249
|
+
|
|
250
|
+
let bestPath: string | null = null;
|
|
251
|
+
for (const item of items) {
|
|
252
|
+
const candidate = normalizeUrl(item.url);
|
|
253
|
+
const matches =
|
|
254
|
+
candidate === current ||
|
|
255
|
+
(candidate !== '' && current.startsWith(candidate + '/'));
|
|
256
|
+
if (matches && (bestPath === null || candidate.length > bestPath.length)) {
|
|
257
|
+
bestPath = candidate;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (bestPath === null) return null;
|
|
261
|
+
|
|
262
|
+
let best: { key: string; score: number } | null = null;
|
|
263
|
+
for (const item of items) {
|
|
264
|
+
if (normalizeUrl(item.url) !== bestPath) continue;
|
|
265
|
+
const params = queryParams(item.url);
|
|
266
|
+
const carried = params.every(([name, value]) => currentParams.getAll(name).includes(value));
|
|
267
|
+
if (carried && (best === null || params.length > best.score)) {
|
|
268
|
+
best = { key: itemKey(item.url), score: params.length };
|
|
237
269
|
}
|
|
238
270
|
}
|
|
239
|
-
return best;
|
|
271
|
+
return best?.key ?? null;
|
|
240
272
|
});
|
|
241
273
|
|
|
242
274
|
const isActive = (url: string): boolean =>
|
|
243
|
-
|
|
275
|
+
activeKey.value !== null && itemKey(url) === activeKey.value;
|
|
244
276
|
|
|
245
277
|
// Index of the group containing the active route (-1 if none).
|
|
246
278
|
const activeGroupIndex = computed(() =>
|