@schukai/monster 3.97.0 → 3.98.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 (34) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +1 -1
  3. package/source/components/accessibility/locale-picker.mjs +553 -482
  4. package/source/components/accessibility/stylesheet/locale-picker.mjs +13 -6
  5. package/source/components/datatable/columnbar.mjs +50 -3
  6. package/source/components/datatable/constants.mjs +7 -0
  7. package/source/components/datatable/datatable/header.mjs +1 -0
  8. package/source/components/datatable/datatable.mjs +1168 -934
  9. package/source/components/datatable/filter/date-range.mjs +145 -14
  10. package/source/components/datatable/filter/input.mjs +50 -3
  11. package/source/components/datatable/filter/range.mjs +92 -7
  12. package/source/components/datatable/filter-button.mjs +46 -3
  13. package/source/components/datatable/filter.mjs +95 -10
  14. package/source/components/datatable/pagination.mjs +82 -7
  15. package/source/components/datatable/save-button.mjs +46 -3
  16. package/source/components/datatable/style/datatable.pcss +1 -0
  17. package/source/components/datatable/stylesheet/datatable.mjs +7 -14
  18. package/source/components/form/field-set.mjs +77 -6
  19. package/source/components/form/select.mjs +149 -30
  20. package/source/components/layout/details.mjs +50 -3
  21. package/source/components/layout/tabs.mjs +50 -3
  22. package/source/components/notify/monitor-attribute-errors.mjs +235 -0
  23. package/source/components/notify/style/monitor-attribute-errors.pcss +0 -0
  24. package/source/components/notify/stylesheet/monitor-attribute-errors.mjs +38 -0
  25. package/source/dom/customelement.mjs +3 -3
  26. package/source/i18n/locale.mjs +151 -151
  27. package/source/i18n/map/languages.mjs +88 -88
  28. package/source/i18n/util.mjs +122 -114
  29. package/source/monster.mjs +1 -0
  30. package/source/types/version.mjs +1 -1
  31. package/test/cases/monster.mjs +1 -1
  32. package/test/web/import.js +1 -0
  33. package/test/web/test.html +2 -2
  34. package/test/web/tests.js +432 -13
@@ -12,7 +12,7 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import {languages} from "./map/languages.mjs";
15
+ import { languages } from "./map/languages.mjs";
16
16
 
17
17
  /**
18
18
  * Determines the user's preferred language based on browser settings and available language options.
@@ -23,117 +23,125 @@ import {languages} from "./map/languages.mjs";
23
23
  * @return {Object} An object containing information about the detected language, preferred language, and available languages.
24
24
  */
25
25
  export function detectUserLanguagePreference() {
26
- const currentLang = document.documentElement.lang;
27
-
28
- let preferredLanguages = [];
29
-
30
- if (typeof navigator.language === "string" && navigator.language.length > 0) {
31
- preferredLanguages = [navigator.language];
32
- }
33
-
34
- if (Array.isArray(navigator.languages) && navigator.languages.length > 0) {
35
- preferredLanguages = navigator.languages;
36
- }
37
-
38
- // add to preferredLanguages all the base languages of the preferred languages
39
- preferredLanguages = preferredLanguages.concat(preferredLanguages.map(lang => lang.split("-")[0]));
40
-
41
-
42
- if (!currentLang && preferredLanguages.length === 0) {
43
- return {
44
- message: "No language information available.",
45
- };
46
- }
47
-
48
- const linkTags = document.querySelectorAll("link[hreflang]");
49
- if (linkTags.length === 0) {
50
- return {
51
- current: currentLang || null,
52
- message: "No <link> tags with hreflang available.",
53
- };
54
- }
55
-
56
- const availableLanguages = [...linkTags].map((link) => {
57
- const fullLang = link.hreflang;
58
- const baseLang = fullLang.split("-")[0];
59
- let label = link.getAttribute('data-monster-label')
60
- if (!label) {
61
- label = languages?.[fullLang]
62
- if (!label) {
63
- label = languages?.[baseLang]
64
- }
65
- }
66
-
67
- return {
68
- fullLang,
69
- baseLang,
70
- label,
71
- href: link.href,
72
- };
73
- });
74
-
75
- // filter availableLanguages to only include languages that are in the preferredLanguages array
76
- const offerableLanguages = availableLanguages.filter(lang => preferredLanguages.includes(lang.fullLang) || preferredLanguages.includes(lang.baseLang));
77
-
78
- if (offerableLanguages.length === 0) {
79
- return {
80
- current: currentLang || null,
81
- message: "No available languages match the user's preferences.",
82
- available: availableLanguages.map((lang) => ({
83
- ...lang,
84
- weight: 1,
85
- })),
86
- };
87
- }
88
-
89
- // Helper function to determine the "weight" of a language match
90
- function getWeight(langEntry) {
91
- // Full match has priority 3
92
- if (preferredLanguages.includes(langEntry.fullLang)) return 3;
93
- // Base language match has priority 2
94
- if (preferredLanguages.includes(langEntry.baseLang)) return 2;
95
- // No match is priority 1
96
- return 1;
97
- }
98
-
99
- // Sort the available languages by descending weight
100
- offerableLanguages.sort((a, b) => getWeight(b) - getWeight(a));
101
-
102
- // The best match is the first in the sorted list
103
- const bestMatch = offerableLanguages[0];
104
- const bestMatchWeight = getWeight(bestMatch);
105
-
106
- const currentLabel = languages?.[currentLang] || currentLang
107
-
108
- // If we found a language that matches user preferences (weight > 1)
109
- if (bestMatchWeight > 0) {
110
- return {
111
- current: currentLang || null,
112
- currentLabel: currentLabel,
113
- preferred: {
114
- full: bestMatch.fullLang,
115
- base: bestMatch.baseLang,
116
- label: bestMatch.label,
117
- href : bestMatch.href,
118
- },
119
- available: availableLanguages.map((lang) => ({
120
- ...lang,
121
- weight: getWeight(lang),
122
- })),
123
- offerable: offerableLanguages.map((lang) => ({
124
- ...lang,
125
- weight: getWeight(lang),
126
- })),
127
- };
128
- }
129
-
130
- // If no language matched the user's preferences
131
- return {
132
- current: currentLang || null,
133
- message: "None of the preferred languages are available.",
134
- available: availableLanguages.map((lang) => ({
135
- ...lang,
136
- weight: getWeight(lang),
137
- })),
138
- };
26
+ const currentLang = document.documentElement.lang;
27
+
28
+ let preferredLanguages = [];
29
+
30
+ if (typeof navigator.language === "string" && navigator.language.length > 0) {
31
+ preferredLanguages = [navigator.language];
32
+ }
33
+
34
+ if (Array.isArray(navigator.languages) && navigator.languages.length > 0) {
35
+ preferredLanguages = navigator.languages;
36
+ }
37
+
38
+ // add to preferredLanguages all the base languages of the preferred languages
39
+ preferredLanguages = preferredLanguages.concat(
40
+ preferredLanguages.map((lang) => lang.split("-")[0]),
41
+ );
42
+
43
+ if (!currentLang && preferredLanguages.length === 0) {
44
+ return {
45
+ message: "No language information available.",
46
+ };
47
+ }
48
+
49
+ const linkTags = document.querySelectorAll("link[hreflang]");
50
+ if (linkTags.length === 0) {
51
+ return {
52
+ current: currentLang || null,
53
+ message: "No <link> tags with hreflang available.",
54
+ };
55
+ }
56
+
57
+ const availableLanguages = [...linkTags].map((link) => {
58
+ const fullLang = link.hreflang;
59
+ const baseLang = fullLang.split("-")[0];
60
+ let label = link.getAttribute("data-monster-label");
61
+ if (!label) {
62
+ label = link.getAttribute("title");
63
+ if (!label) {
64
+ label = languages?.[fullLang];
65
+ if (!label) {
66
+ label = languages?.[baseLang];
67
+ }
68
+ }
69
+ }
70
+
71
+ return {
72
+ fullLang,
73
+ baseLang,
74
+ label,
75
+ href: link.href,
76
+ };
77
+ });
78
+
79
+ // filter availableLanguages to only include languages that are in the preferredLanguages array
80
+ const offerableLanguages = availableLanguages.filter(
81
+ (lang) =>
82
+ preferredLanguages.includes(lang.fullLang) ||
83
+ preferredLanguages.includes(lang.baseLang),
84
+ );
85
+
86
+ if (offerableLanguages.length === 0) {
87
+ return {
88
+ current: currentLang || null,
89
+ message: "No available languages match the user's preferences.",
90
+ available: availableLanguages.map((lang) => ({
91
+ ...lang,
92
+ weight: 1,
93
+ })),
94
+ };
95
+ }
96
+
97
+ // Helper function to determine the "weight" of a language match
98
+ function getWeight(langEntry) {
99
+ // Full match has priority 3
100
+ if (preferredLanguages.includes(langEntry.fullLang)) return 3;
101
+ // Base language match has priority 2
102
+ if (preferredLanguages.includes(langEntry.baseLang)) return 2;
103
+ // No match is priority 1
104
+ return 1;
105
+ }
106
+
107
+ // Sort the available languages by descending weight
108
+ offerableLanguages.sort((a, b) => getWeight(b) - getWeight(a));
109
+
110
+ // The best match is the first in the sorted list
111
+ const bestMatch = offerableLanguages[0];
112
+ const bestMatchWeight = getWeight(bestMatch);
113
+
114
+ const currentLabel = languages?.[currentLang] || currentLang;
115
+
116
+ // If we found a language that matches user preferences (weight > 1)
117
+ if (bestMatchWeight > 0) {
118
+ return {
119
+ current: currentLang || null,
120
+ currentLabel: currentLabel,
121
+ preferred: {
122
+ full: bestMatch.fullLang,
123
+ base: bestMatch.baseLang,
124
+ label: bestMatch.label,
125
+ href: bestMatch.href,
126
+ },
127
+ available: availableLanguages.map((lang) => ({
128
+ ...lang,
129
+ weight: getWeight(lang),
130
+ })),
131
+ offerable: offerableLanguages.map((lang) => ({
132
+ ...lang,
133
+ weight: getWeight(lang),
134
+ })),
135
+ };
136
+ }
137
+
138
+ // If no language matched the user's preferences
139
+ return {
140
+ current: currentLang || null,
141
+ message: "None of the preferred languages are available.",
142
+ available: availableLanguages.map((lang) => ({
143
+ ...lang,
144
+ weight: getWeight(lang),
145
+ })),
146
+ };
139
147
  }
@@ -91,6 +91,7 @@ export * from "./components/datatable/embedded-pagination.mjs";
91
91
  export * from "./components/datatable/status.mjs";
92
92
  export * from "./components/datatable/change-button.mjs";
93
93
  export * from "./components/datatable/constants.mjs";
94
+ export * from "./components/accessibility/locale-picker.mjs";
94
95
  export * from "./components/state/log/entry.mjs";
95
96
  export * from "./components/state/state.mjs";
96
97
  export * from "./components/state/log.mjs";
@@ -156,7 +156,7 @@ function getMonsterVersion() {
156
156
  }
157
157
 
158
158
  /** don't touch, replaced by make with package.json version */
159
- monsterVersion = new Version("3.96.2");
159
+ monsterVersion = new Version("3.97.1");
160
160
 
161
161
  return monsterVersion;
162
162
  }
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("3.96.2")
10
+ monsterVersion = new Version("3.97.1")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -113,4 +113,5 @@ import "../cases/i18n/locale.mjs";
113
113
  import "../cases/i18n/provider.mjs";
114
114
  import "../cases/i18n/providers/fetch.mjs";
115
115
  import "../cases/i18n/providers/embed.mjs";
116
+ import "../cases/i18n/util.mjs";
116
117
  import "../cases/i18n/translations.mjs";
@@ -9,8 +9,8 @@
9
9
  </head>
10
10
  <body>
11
11
  <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
12
- <h1 style='margin-bottom: 0.1em;'>Monster 3.96.2</h1>
13
- <div id="lastupdate" style='font-size:0.7em'>last update Fr 3. Jan 15:17:06 CET 2025</div>
12
+ <h1 style='margin-bottom: 0.1em;'>Monster 3.97.1</h1>
13
+ <div id="lastupdate" style='font-size:0.7em'>last update So 5. Jan 14:38:20 CET 2025</div>
14
14
  </div>
15
15
  <div id="mocha-errors"
16
16
  style="color: red;font-weight: bold;display: flex;align-items: center;justify-content: center;flex-direction: column;margin:20px;"></div>