qc-trousse-sdg 1.4.6 → 1.4.8
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 +14 -0
- package/dist/css/qc-sdg-no-grid.min.css +1 -1
- package/dist/css/qc-sdg.min.css +1 -1
- package/dist/js/qc-sdg.min.js +1 -1
- package/package.json +1 -1
- package/public/css/qc-doc-sdg.css +38 -6
- package/public/css/qc-sdg-no-grid.css +67 -40
- package/public/css/qc-sdg.css +67 -40
- package/public/index.html +77 -23
- package/public/js/qc-doc-sdg.js +381 -225
- package/public/js/qc-sdg.js +1053 -633
- package/src/doc/qc-doc-sdg.js +6 -1
- package/src/doc/scss/components/_exemple.scss +5 -1
- package/src/doc/scss/qc-doc-sdg.scss +22 -4
- package/src/sdg/bases/Icon/Icon.svelte +2 -0
- package/src/sdg/bases/links/_links.scss +18 -12
- package/src/sdg/components/Alert/Alert.svelte +28 -9
- package/src/sdg/components/Alert/AlertWC.svelte +20 -5
- package/src/sdg/components/Alert/Test/AlertSvelteTest.svelte +25 -0
- package/src/sdg/components/Alert/Test/alertBaselineTest.html +13 -0
- package/src/sdg/components/Alert/Test/alertSvelteTest.html +1 -0
- package/src/sdg/components/Alert/_alert.html +23 -11
- package/src/sdg/components/Checkbox/Checkbox.svelte +6 -5
- package/src/sdg/components/DropdownList/DropdownList.svelte +65 -14
- package/src/sdg/components/DropdownList/DropdownListButton/DropdownListButton.svelte +2 -6
- package/src/sdg/components/DropdownList/DropdownListItems/DropdownListItems.svelte +4 -22
- package/src/sdg/components/DropdownList/DropdownListItems/DropdownListItemsMultiple/DropdownListItemsMultiple.svelte +2 -1
- package/src/sdg/components/DropdownList/DropdownListItems/DropdownListItemsSingle/DropdownListItemsSingle.svelte +2 -1
- package/src/sdg/components/DropdownList/SelectWC.svelte +39 -13
- package/src/sdg/components/DropdownList/Test/DropdownListSvelteTest.svelte +2 -2
- package/src/sdg/components/DropdownList/Test/dropdownListBaselineTest.html +7 -0
- package/src/sdg/components/DropdownList/_dropdownList.scss +7 -5
- package/src/sdg/components/DropdownList/_select.html +31 -5
- package/src/sdg/components/ExternalLink/ExternalLink.svelte +36 -74
- package/src/sdg/components/ExternalLink/ExternalLinkWC.svelte +44 -1
- package/src/sdg/components/ExternalLink/externalLinkBaselineTest.html +45 -0
- package/src/sdg/components/Fieldset/_fieldset.scss +1 -1
- package/src/sdg/components/Label/LabelText.svelte +2 -1
- package/src/sdg/components/Label/_label.scss +10 -2
- package/src/sdg/components/PivFooter/_pivFooter.html +4 -4
- package/src/sdg/components/SearchInput/SearchInput.svelte +14 -2
- package/src/sdg/components/SearchInput/SearchInputWC.svelte +2 -0
- package/src/sdg/components/SearchInput/_searchInput.html +18 -2
- package/src/sdg/components/SearchInput/_searchInput.scss +10 -1
- package/src/sdg/components/TextField/Test/TextFieldEmbededTest.svelte +19 -3
- package/src/sdg/components/TextField/Test/textFieldBaselineTest.html +5 -2
- package/src/sdg/components/TextField/TextField.svelte +12 -6
- package/src/sdg/components/TextField/TextFieldWC.svelte +19 -4
- package/src/sdg/components/TextField/_textField.html +1 -1
- package/src/sdg/components/TextField/_textField.scss +0 -6
- package/src/sdg/components/TextField/textFieldUtils.js +3 -2
- package/src/sdg/components/utils.js +23 -0
- package/src/sdg/qc-sdg-test.js +2 -1
- package/src/sdg/scss/lib/_mixins.scss +6 -0
- package/src/sdg/scss/utilities/_states.scss +1 -1
- package/tests/alert-baseline.spec.ts +23 -0
- package/tests/alert-svelte.spec.ts +23 -0
- package/tests/buildSvelteTestsIgnore.json +2 -1
- package/tests/dropdown-list-baseline.spec.ts +8 -0
- package/tests/external-link-baseline.spec.ts +30 -0
- package/tests/textfield-baseline.spec.ts +5 -5
- package/tests/textfield-svelte.spec.ts +5 -5
package/src/doc/qc-doc-sdg.js
CHANGED
|
@@ -16,7 +16,8 @@ const maskableAlert = document.getElementById("alerte-masquable");
|
|
|
16
16
|
if (displayAlertLink) {
|
|
17
17
|
displayAlertLink.addEventListener(
|
|
18
18
|
'click',
|
|
19
|
-
() => {
|
|
19
|
+
(evt) => {
|
|
20
|
+
evt.preventDefault();
|
|
20
21
|
maskableAlert.setAttribute('hide', 'false');
|
|
21
22
|
displayAlertLink.hidden = true;
|
|
22
23
|
}
|
|
@@ -29,6 +30,10 @@ if (displayAlertLink) {
|
|
|
29
30
|
}
|
|
30
31
|
)
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
if (maskableAlert && displayAlertLink && !maskableAlert.querySelector('.qc-general-alert')) {
|
|
35
|
+
displayAlertLink.removeAttribute('hidden');
|
|
36
|
+
}
|
|
32
37
|
// add version
|
|
33
38
|
|
|
34
39
|
|
|
@@ -94,24 +94,39 @@ h1,h2,h3,h4,h5,h6 {
|
|
|
94
94
|
|
|
95
95
|
a {
|
|
96
96
|
|
|
97
|
+
&.pseudo-visited {
|
|
98
|
+
color: token-value(color,link,visited);
|
|
99
|
+
& > .qc-ext-link-img {
|
|
100
|
+
background-color: token-value(color, link, visited);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
97
103
|
&.pseudo-hover {
|
|
98
104
|
@include hover-link();
|
|
105
|
+
& > .qc-ext-link-img {
|
|
106
|
+
background-color: token-value(color, link, hover);
|
|
107
|
+
}
|
|
99
108
|
}
|
|
100
109
|
&.pseudo-active {
|
|
101
110
|
@include active-link();
|
|
111
|
+
& > .qc-ext-link-img {
|
|
112
|
+
background-color: token-value(color, link, active);
|
|
113
|
+
}
|
|
102
114
|
}
|
|
103
115
|
&.pseudo-focus {
|
|
104
116
|
@include focus-link();
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
& > .qc-ext-link-img {
|
|
118
|
+
background-color: token-value(color, link, text);
|
|
119
|
+
}
|
|
108
120
|
}
|
|
109
121
|
&.not-visited:visited:not(:hover) {
|
|
110
122
|
color: token-value(color,link,text);
|
|
123
|
+
& > .qc-ext-link-text {
|
|
124
|
+
color: token-value(color,link,text);
|
|
125
|
+
}
|
|
111
126
|
}
|
|
112
127
|
}
|
|
113
128
|
|
|
114
|
-
|
|
129
|
+
div.qc-ext-link-img {
|
|
115
130
|
a.pseudo-visited & {
|
|
116
131
|
background: token-value(color link visited)!important;
|
|
117
132
|
}
|
|
@@ -124,6 +139,9 @@ span.qc-ext-link-img {
|
|
|
124
139
|
a.pseudo-active & {
|
|
125
140
|
background: token-value(color link active)!important;
|
|
126
141
|
}
|
|
142
|
+
a.not-visited:visited:not(:hover) & {
|
|
143
|
+
background: token-value(color link text)!important;
|
|
144
|
+
}
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
p,h1,h2,h3,h4,h5,h6 {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
height='auto',
|
|
9
9
|
src='',
|
|
10
10
|
rotate = 0,
|
|
11
|
+
rootElement = $bindable(),
|
|
11
12
|
...rest
|
|
12
13
|
} = $props();
|
|
13
14
|
let attributes = $derived(width === 'auto' ? { 'data-img-size': size } : {});
|
|
@@ -30,5 +31,6 @@
|
|
|
30
31
|
{...attributes}
|
|
31
32
|
{...rest}
|
|
32
33
|
aria-hidden={label ? undefined : true}
|
|
34
|
+
bind:this={rootElement}
|
|
33
35
|
>
|
|
34
36
|
</div>
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
a {
|
|
6
6
|
color: token-value(color, link, text);
|
|
7
|
+
&:has(.qc-ext-link-text) {
|
|
8
|
+
white-space: nowrap;
|
|
9
|
+
text-decoration: none;
|
|
10
|
+
}
|
|
7
11
|
&:visited {
|
|
8
12
|
color: token-value(color, link, visited);
|
|
9
13
|
}
|
|
@@ -19,33 +23,35 @@ a {
|
|
|
19
23
|
.img-wrap {
|
|
20
24
|
white-space: nowrap;
|
|
21
25
|
}
|
|
26
|
+
}
|
|
22
27
|
|
|
28
|
+
.qc-external-link {
|
|
29
|
+
.qc-ext-link-text {
|
|
30
|
+
white-space: normal;
|
|
31
|
+
text-decoration: underline;
|
|
32
|
+
}
|
|
23
33
|
}
|
|
24
|
-
span.qc-ext-link-img {
|
|
25
34
|
|
|
35
|
+
.qc-ext-link-img {
|
|
26
36
|
@extend .icon-external-link;
|
|
27
37
|
$ratio : math.div(11,16) * 1em;
|
|
28
38
|
height: $ratio;
|
|
29
39
|
width: $ratio;
|
|
30
|
-
display: inline-block;
|
|
31
40
|
background: token-value(color link text);
|
|
32
41
|
mask-size: $ratio;
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
display: inline-block;
|
|
43
|
+
mask-repeat: no-repeat;
|
|
44
|
+
|
|
45
|
+
a:visited & {
|
|
35
46
|
background: token-value(color link visited);
|
|
36
47
|
}
|
|
37
|
-
a:focus
|
|
48
|
+
a:focus & {
|
|
38
49
|
background: token-value(color link hover);
|
|
39
50
|
}
|
|
40
|
-
a:hover
|
|
51
|
+
a:hover & {
|
|
41
52
|
background: token-value(color link hover);
|
|
42
53
|
}
|
|
43
|
-
a:active
|
|
54
|
+
a:active & {
|
|
44
55
|
background: token-value(color link active);
|
|
45
56
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.img-wrap + & {
|
|
49
|
-
display: none;
|
|
50
|
-
}
|
|
51
57
|
}
|
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
import {Utils} from "../utils";
|
|
3
3
|
import Icon from "../../bases/Icon/Icon.svelte";
|
|
4
4
|
import IconButton from "../IconButton/IconButton.svelte";
|
|
5
|
+
import {onMount} from "svelte";
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
type = "general",
|
|
8
9
|
maskable = "",
|
|
9
10
|
content = "",
|
|
10
|
-
hide = "false",
|
|
11
|
+
hide = $bindable("false"),
|
|
11
12
|
fullWidth = "false",
|
|
12
13
|
slotContent,
|
|
14
|
+
id,
|
|
15
|
+
persistenceKey,
|
|
16
|
+
persistHidden = false,
|
|
17
|
+
rootElement = $bindable(),
|
|
18
|
+
hideAlertCallback = () => {},
|
|
13
19
|
} = $props();
|
|
14
20
|
|
|
15
21
|
const language = Utils.getPageLanguage();
|
|
@@ -21,18 +27,31 @@
|
|
|
21
27
|
|
|
22
28
|
const label = type === 'general' ? generalLabel : warningLabel;
|
|
23
29
|
|
|
24
|
-
let rootElement = $state(null);
|
|
25
|
-
|
|
26
30
|
let containerClass = "qc-container" + (fullWidth === 'true' ? '-fluid' : '');
|
|
27
31
|
|
|
32
|
+
onMount(() => {
|
|
33
|
+
const key = getPersistenceKey();
|
|
34
|
+
if (!key) return;
|
|
35
|
+
hide = sessionStorage.getItem(key) ? "true" : "false";
|
|
36
|
+
})
|
|
37
|
+
|
|
28
38
|
function hideAlert() {
|
|
29
39
|
hide = "true";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
40
|
+
persistHiddenState();
|
|
41
|
+
hideAlertCallback();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getPersistenceKey() {
|
|
45
|
+
if (!persistHidden) return false;
|
|
46
|
+
const key = persistenceKey || id;
|
|
47
|
+
if (! key) return false;
|
|
48
|
+
return'qc-alert:' + key;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function persistHiddenState() {
|
|
52
|
+
const key = getPersistenceKey();
|
|
53
|
+
if (!key) return;
|
|
54
|
+
sessionStorage.setItem(key, Utils.now());
|
|
36
55
|
}
|
|
37
56
|
</script>
|
|
38
57
|
|
|
@@ -6,20 +6,35 @@
|
|
|
6
6
|
maskable : {attribute: 'maskable'},
|
|
7
7
|
fullWidth : {attribute: 'full-width'},
|
|
8
8
|
content: {attribute: 'content'},
|
|
9
|
-
hide: {attribute: 'hide'},
|
|
9
|
+
hide: {attribute: 'hide', reflect: true},
|
|
10
|
+
persistHidden: {attribute: 'persist-hidden', type: 'Boolean'},
|
|
11
|
+
persistenceKey: {attribute: 'persistence-key', type: 'String'},
|
|
10
12
|
}
|
|
11
|
-
}}"
|
|
13
|
+
}}"></svelte:options>
|
|
12
14
|
|
|
13
15
|
<script>
|
|
14
16
|
import Alert from "./Alert.svelte";
|
|
15
17
|
import {Utils} from "../utils";
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
let {hide = "false", ...props} = $props();
|
|
18
20
|
|
|
21
|
+
let rootElement = $state();
|
|
22
|
+
|
|
23
|
+
function hideAlertCallback() {
|
|
24
|
+
rootElement?.dispatchEvent(
|
|
25
|
+
new CustomEvent('qc.alert.hide', {
|
|
26
|
+
bubbles: true,
|
|
27
|
+
composed: true
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
}
|
|
19
31
|
</script>
|
|
20
32
|
|
|
21
33
|
<Alert
|
|
22
|
-
|
|
23
|
-
|
|
34
|
+
bind:hide
|
|
35
|
+
bind:rootElement
|
|
36
|
+
{hideAlertCallback}
|
|
37
|
+
{...props}
|
|
38
|
+
slotContent = {`<slot />`}
|
|
24
39
|
/>
|
|
25
40
|
<link rel='stylesheet' href='{Utils.cssPath}'>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Alert from "../Alert.svelte";
|
|
3
|
+
import {Utils} from "../../utils";
|
|
4
|
+
|
|
5
|
+
let regularAlertContent = $state();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<svelte:options customElement={{tag: "qc-alert-svelte-test" }} />
|
|
9
|
+
|
|
10
|
+
<Alert
|
|
11
|
+
id="alerte-masquable"
|
|
12
|
+
type="warning"
|
|
13
|
+
maskable="true"
|
|
14
|
+
content="Alerte jaune d’importance élevée"
|
|
15
|
+
persistHidden="true"
|
|
16
|
+
persistenceKey="hash-1238"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<div hidden>
|
|
20
|
+
<p style="margin: 0;" bind:this={regularAlertContent}>Alerte bleue d’importance modérée <a href="#">avec un lien textuel</a></p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<Alert type="general" slotContent={regularAlertContent?.outerHTML} />
|
|
24
|
+
|
|
25
|
+
<link rel='stylesheet' href='{Utils.cssPath}'>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<qc-alert id="alerte-masquable"
|
|
2
|
+
type="warning"
|
|
3
|
+
maskable="true"
|
|
4
|
+
content="Alerte jaune d’importance élevée"
|
|
5
|
+
persist-hidden
|
|
6
|
+
persistence-key="hash-1234"
|
|
7
|
+
>
|
|
8
|
+
</qc-alert>
|
|
9
|
+
|
|
10
|
+
<qc-alert type="general"
|
|
11
|
+
maskable="false">
|
|
12
|
+
<p>Alerte bleue d’importance modérée <a href="#">avec un lien textuel</a></p>
|
|
13
|
+
</qc-alert>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<qc-alert-svelte-test></qc-alert-svelte-test>
|
|
@@ -11,15 +11,16 @@
|
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
<qc-doc-exemple id="alert-warning"
|
|
14
|
-
caption="Exemple d’alerte jaune">
|
|
14
|
+
caption="Exemple d’alerte jaune masquable. Elle reste masquée après rafraîchissement de la page tant que la session courante est active.">
|
|
15
15
|
<qc-alert id="alerte-masquable"
|
|
16
16
|
type="warning"
|
|
17
17
|
maskable="true"
|
|
18
18
|
content="Alerte jaune d’importance élevée"
|
|
19
|
+
persist-hidden
|
|
20
|
+
persistence-key="hash-1234"
|
|
19
21
|
>
|
|
20
22
|
</qc-alert>
|
|
21
23
|
</qc-doc-exemple>
|
|
22
|
-
<!-- <qc-code target-id="alert-warning"></qc-code>-->
|
|
23
24
|
|
|
24
25
|
<h4>Alerte bleue</h4>
|
|
25
26
|
<qc-doc-exemple id="alert-general"
|
|
@@ -30,8 +31,6 @@
|
|
|
30
31
|
</qc-alert>
|
|
31
32
|
</qc-doc-exemple>
|
|
32
33
|
|
|
33
|
-
<!-- <qc-code target-id="alert-general"></qc-code>-->
|
|
34
|
-
|
|
35
34
|
<h3>Documentation technique</h3>
|
|
36
35
|
|
|
37
36
|
<h4>Attributs</h4>
|
|
@@ -51,12 +50,6 @@
|
|
|
51
50
|
<td>"general"</td>
|
|
52
51
|
<td>Type de l’alerte : s'il s'agit d’une alerte bleue ou jaune</td>
|
|
53
52
|
</tr>
|
|
54
|
-
<tr>
|
|
55
|
-
<td>maskable</td>
|
|
56
|
-
<td>"true" ou "false"</td>
|
|
57
|
-
<td>"true"</td>
|
|
58
|
-
<td>Afficher le bouton de fermeture de l’alerte</td>
|
|
59
|
-
</tr>
|
|
60
53
|
<tr>
|
|
61
54
|
<td>content</td>
|
|
62
55
|
<td>Texte</td>
|
|
@@ -77,6 +70,25 @@
|
|
|
77
70
|
qc-container-fluid)
|
|
78
71
|
</td>
|
|
79
72
|
</tr>
|
|
73
|
+
<tr>
|
|
74
|
+
<td>maskable</td>
|
|
75
|
+
<td>"true" ou "false"</td>
|
|
76
|
+
<td>"true"</td>
|
|
77
|
+
<td>Afficher le bouton de fermeture de l’alerte</td>
|
|
78
|
+
</tr>
|
|
79
|
+
<tr>
|
|
80
|
+
<td>persist-hidden</td>
|
|
81
|
+
<td>na</td>
|
|
82
|
+
<td></td>
|
|
83
|
+
<td>Masque l'alerte de façon persistente une fois qu'elle est masquée par l'internaute, c‑à‑d. qu'elle reste masquée lorsque l'utilisateur rafraîchit la page, pendant tout le temps de sa session.</td>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td>persistence-key</td>
|
|
87
|
+
<td>Chaîne de caractère</td>
|
|
88
|
+
<td>Id de l'élément, valeur nulle sinon</td>
|
|
89
|
+
<td>Clé pour le stockage de session (<code>Window.sessionStorage</code>). Si non défini, l'id de l'élément sera utilisé. Si celui-ci non plus n'est pas défini, la persistence du masquage n'aura pas lieu.</td>
|
|
90
|
+
</tr>
|
|
91
|
+
|
|
80
92
|
</table>
|
|
81
93
|
</div>
|
|
82
94
|
|
|
@@ -89,7 +101,7 @@
|
|
|
89
101
|
document.addEventListener(
|
|
90
102
|
'qc.alert.hide',
|
|
91
103
|
(e) => {
|
|
92
|
-
console.log(
|
|
104
|
+
console.log(`Fermeture de l'alerte id='${e.target.id}'`);
|
|
93
105
|
}
|
|
94
106
|
)
|
|
95
107
|
</script>
|
|
@@ -40,15 +40,16 @@
|
|
|
40
40
|
if (labelElement) {
|
|
41
41
|
label = labelElement.querySelector('span')?.textContent;
|
|
42
42
|
}
|
|
43
|
-
})
|
|
43
|
+
});
|
|
44
44
|
|
|
45
45
|
$effect(_ => updateChoiceInput(input, required, invalid, compact, false, false))
|
|
46
46
|
|
|
47
47
|
$effect(() => {
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
if (required && label && requiredSpan) {
|
|
49
|
+
const textSpan = labelElement.querySelector('span');
|
|
50
|
+
textSpan.appendChild(requiredSpan);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
52
53
|
</script>
|
|
53
54
|
|
|
54
55
|
{#snippet requiredSpanSnippet()}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import DropdownListItems from "./DropdownListItems/DropdownListItems.svelte";
|
|
6
6
|
import DropdownListButton from "./DropdownListButton/DropdownListButton.svelte";
|
|
7
7
|
import Label from "../Label/Label.svelte";
|
|
8
|
+
import {onMount, tick} from "svelte";
|
|
8
9
|
|
|
9
10
|
const lang = Utils.getPageLanguage();
|
|
10
11
|
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
rootElement = $bindable(),
|
|
28
29
|
errorElement = $bindable(),
|
|
29
30
|
webComponentMode = false,
|
|
31
|
+
expanded = $bindable(false),
|
|
30
32
|
} = $props();
|
|
31
33
|
|
|
32
34
|
const
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
itemsId = `${id}-items`,
|
|
37
39
|
labelId = `${id}-label`,
|
|
38
40
|
errorId = `${id}-error`,
|
|
39
|
-
availableWidths = ["xs", "sm", "md", "lg", "xl"]
|
|
41
|
+
availableWidths = ["xs", "sm", "md", "lg", "xl"], buttonHeight = 40
|
|
40
42
|
;
|
|
41
43
|
|
|
42
44
|
let
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
parentRow = $derived(instance?.closest(".qc-formfield-row")),
|
|
45
47
|
button = $state(),
|
|
46
48
|
searchInput = $state(),
|
|
49
|
+
popup = $state(),
|
|
47
50
|
dropdownItems = $state(),
|
|
48
51
|
selectedItems = $derived(items.filter((item) => item.checked) ?? []),
|
|
49
52
|
selectedOptionsText = $derived.by(() => {
|
|
@@ -64,7 +67,6 @@
|
|
|
64
67
|
return "";
|
|
65
68
|
}),
|
|
66
69
|
previousValue = $state(value),
|
|
67
|
-
expanded = $state(false),
|
|
68
70
|
searchText = $state(""),
|
|
69
71
|
hiddenSearchText = $state(""),
|
|
70
72
|
displayedItems = $state(items),
|
|
@@ -77,8 +79,6 @@
|
|
|
77
79
|
}
|
|
78
80
|
})),
|
|
79
81
|
widthClass = $derived.by(() => {
|
|
80
|
-
const keyword = webComponentMode ? "container" : "root";
|
|
81
|
-
|
|
82
82
|
if (availableWidths.includes(width)) {
|
|
83
83
|
return `qc-dropdown-list-${width}`;
|
|
84
84
|
}
|
|
@@ -93,7 +93,27 @@
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
return "";
|
|
96
|
-
})
|
|
96
|
+
}),
|
|
97
|
+
buttonElementYPosition = $state(0),
|
|
98
|
+
usedHeight = $derived.by(() => {
|
|
99
|
+
const maxItemsHeight = 336;
|
|
100
|
+
const searchInputTotalHeight = 56;
|
|
101
|
+
|
|
102
|
+
if (enableSearch) {
|
|
103
|
+
if (displayedItems.length > 7) {
|
|
104
|
+
return maxItemsHeight - searchInputTotalHeight - 17;
|
|
105
|
+
}
|
|
106
|
+
return maxItemsHeight - searchInputTotalHeight;
|
|
107
|
+
} else {
|
|
108
|
+
if (displayedItems.length > 8) {
|
|
109
|
+
return maxItemsHeight - 33;
|
|
110
|
+
}
|
|
111
|
+
return maxItemsHeight;
|
|
112
|
+
}
|
|
113
|
+
}),
|
|
114
|
+
topOffset = $state(0),
|
|
115
|
+
popupTopBorderThickness = $derived(topOffset && topOffset < 0 ? 1 : 0),
|
|
116
|
+
popupBottomBorderThickness = $derived(topOffset && topOffset >= 0 ? 1 : 0)
|
|
97
117
|
;
|
|
98
118
|
|
|
99
119
|
function focusOnSelectedOption(value) {
|
|
@@ -120,7 +140,7 @@
|
|
|
120
140
|
function handleTab(event) {
|
|
121
141
|
// Le changement de focus a lieu après le lancement de l'événement clavier.
|
|
122
142
|
// Il faut donc faire un court sleep pour avoir le nouvel élément en focus.
|
|
123
|
-
|
|
143
|
+
tick().then(() => {
|
|
124
144
|
if (event.key === "Tab" && !Utils.componentIsActive(instance)) {
|
|
125
145
|
expanded = false;
|
|
126
146
|
}
|
|
@@ -214,6 +234,7 @@
|
|
|
214
234
|
|
|
215
235
|
$effect(() => {
|
|
216
236
|
if (previousValue?.toString() !== value?.toString()) {
|
|
237
|
+
previousValue = value;
|
|
217
238
|
invalid = false;
|
|
218
239
|
}
|
|
219
240
|
});
|
|
@@ -257,8 +278,18 @@
|
|
|
257
278
|
? optionWithEmptyValue.label
|
|
258
279
|
: defaultPlaceholder
|
|
259
280
|
;
|
|
260
|
-
})
|
|
281
|
+
});
|
|
261
282
|
|
|
283
|
+
$effect(() => {
|
|
284
|
+
if (expanded) {
|
|
285
|
+
const borderThickness = 2 * (invalid ? 2 : 1);
|
|
286
|
+
const popupHeight = popup ? popup.getBoundingClientRect().height : usedHeight;
|
|
287
|
+
|
|
288
|
+
topOffset = buttonElementYPosition + buttonHeight > innerHeight - popupHeight ?
|
|
289
|
+
-popupHeight
|
|
290
|
+
: buttonHeight - borderThickness;
|
|
291
|
+
}
|
|
292
|
+
});
|
|
262
293
|
|
|
263
294
|
function findOptionWithEmptyValue() {
|
|
264
295
|
return items?.find(
|
|
@@ -267,9 +298,21 @@
|
|
|
267
298
|
|| item.value === undefined
|
|
268
299
|
);
|
|
269
300
|
}
|
|
301
|
+
|
|
302
|
+
function setRemainingBottomHeight() {
|
|
303
|
+
if (!button) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
buttonElementYPosition = button.getBoundingClientRect().y;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
onMount(() => {
|
|
310
|
+
setRemainingBottomHeight();
|
|
311
|
+
});
|
|
270
312
|
</script>
|
|
271
313
|
|
|
272
314
|
<svelte:body onclick={handleOuterEvent} onkeydown={handleTab}/>
|
|
315
|
+
<svelte:window onscroll={setRemainingBottomHeight} />
|
|
273
316
|
<div
|
|
274
317
|
class={[
|
|
275
318
|
!parentRow && !webComponentMode && "qc-select"
|
|
@@ -315,19 +358,28 @@
|
|
|
315
358
|
aria-invalid={invalid}
|
|
316
359
|
{selectedOptionsText}
|
|
317
360
|
{placeholder}
|
|
361
|
+
{usedHeight}
|
|
318
362
|
onclick={handleDropdownButtonClick}
|
|
319
363
|
onkeydown={(e) => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
bind:
|
|
364
|
+
handleButtonKeyDown(e, enableSearch ? searchInput : dropdownItems);
|
|
365
|
+
}}
|
|
366
|
+
bind:buttonElement={button}
|
|
323
367
|
/>
|
|
324
368
|
|
|
325
369
|
<div
|
|
326
370
|
id={popupId}
|
|
327
371
|
class="qc-dropdown-list-expanded"
|
|
372
|
+
style={`
|
|
373
|
+
--dropdown-items-top-offset: ${topOffset};
|
|
374
|
+
--dropdown-items-height: ${usedHeight};
|
|
375
|
+
--dropdown-items-bottom-border: ${popupBottomBorderThickness};
|
|
376
|
+
--dropdown-items-top-border: ${popupTopBorderThickness};
|
|
377
|
+
--dropdown-button-border: ${invalid ? 2 : 1};
|
|
378
|
+
`}
|
|
328
379
|
tabindex="-1"
|
|
329
380
|
hidden={!expanded}
|
|
330
381
|
role="listbox"
|
|
382
|
+
bind:this={popup}
|
|
331
383
|
>
|
|
332
384
|
|
|
333
385
|
{#if enableSearch}
|
|
@@ -352,16 +404,15 @@
|
|
|
352
404
|
|
|
353
405
|
<DropdownListItems
|
|
354
406
|
id={itemsId}
|
|
355
|
-
{enableSearch}
|
|
356
407
|
{placeholder}
|
|
357
408
|
{multiple}
|
|
358
409
|
{items}
|
|
359
410
|
{displayedItems}
|
|
360
411
|
{noOptionsMessage}
|
|
361
412
|
selectionCallbackSingle={() => {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
413
|
+
closeDropdown("");
|
|
414
|
+
button?.focus();
|
|
415
|
+
}}
|
|
365
416
|
handleExitSingle={(key) => closeDropdown(key)}
|
|
366
417
|
handleExitMultiple={(key) => closeDropdown(key)}
|
|
367
418
|
focusOnOuterElement={() => enableSearch ? searchInput?.focus() : button?.focus()}
|
|
@@ -7,13 +7,9 @@
|
|
|
7
7
|
disabled,
|
|
8
8
|
selectedOptionsText = "",
|
|
9
9
|
placeholder,
|
|
10
|
+
buttonElement = $bindable(),
|
|
10
11
|
...rest
|
|
11
12
|
} = $props()
|
|
12
|
-
|
|
13
|
-
let button;
|
|
14
|
-
export function focus() {
|
|
15
|
-
button?.focus();
|
|
16
|
-
}
|
|
17
13
|
</script>
|
|
18
14
|
|
|
19
15
|
<button
|
|
@@ -22,7 +18,7 @@
|
|
|
22
18
|
{disabled}
|
|
23
19
|
class="qc-dropdown-button"
|
|
24
20
|
role="combobox"
|
|
25
|
-
bind:this={
|
|
21
|
+
bind:this={buttonElement}
|
|
26
22
|
{...rest}
|
|
27
23
|
>
|
|
28
24
|
{#if selectedOptionsText.length > 0}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
let {
|
|
8
8
|
id,
|
|
9
|
-
enableSearch,
|
|
10
9
|
multiple,
|
|
11
10
|
items,
|
|
12
11
|
displayedItems,
|
|
@@ -18,41 +17,25 @@
|
|
|
18
17
|
focusOnOuterElement = () => {},
|
|
19
18
|
handlePrintableCharacter = () => {},
|
|
20
19
|
placeholder,
|
|
21
|
-
} = $props()
|
|
20
|
+
} = $props();
|
|
22
21
|
|
|
23
22
|
let itemsComponent = $state();
|
|
24
|
-
let usedHeight = $derived.by(() => {
|
|
25
|
-
const maxItemsHeight = 336;
|
|
26
|
-
const searchInputTotalHeight = 56;
|
|
27
|
-
|
|
28
|
-
if (enableSearch) {
|
|
29
|
-
if (displayedItems.length > 7) {
|
|
30
|
-
return maxItemsHeight - searchInputTotalHeight - 17;
|
|
31
|
-
}
|
|
32
|
-
return maxItemsHeight - searchInputTotalHeight;
|
|
33
|
-
} else {
|
|
34
|
-
if (displayedItems.length > 8) {
|
|
35
|
-
return maxItemsHeight - 33;
|
|
36
|
-
}
|
|
37
|
-
return maxItemsHeight;
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
23
|
|
|
41
24
|
export function focus() {
|
|
42
|
-
|
|
25
|
+
tick().then(() => {
|
|
43
26
|
itemsComponent?.focusOnFirstElement();
|
|
44
27
|
}).catch(console.error);
|
|
45
28
|
}
|
|
46
29
|
|
|
47
30
|
export function focusOnLastElement() {
|
|
48
|
-
|
|
31
|
+
tick().then(() => {
|
|
49
32
|
itemsComponent?.focusOnLastElement();
|
|
50
33
|
}).catch(console.error);
|
|
51
34
|
}
|
|
52
35
|
|
|
53
36
|
export function focusOnFirstMatchingElement(value) {
|
|
54
37
|
if (itemsComponent && value && value.length > 0) {
|
|
55
|
-
|
|
38
|
+
tick().then(() => {
|
|
56
39
|
itemsComponent?.focusOnFirstMatchingElement(value);
|
|
57
40
|
}).catch(console.error);
|
|
58
41
|
}
|
|
@@ -63,7 +46,6 @@
|
|
|
63
46
|
id={id}
|
|
64
47
|
class="qc-dropdown-list-items"
|
|
65
48
|
tabindex="-1"
|
|
66
|
-
style="--dropdown-items-height: {usedHeight};"
|
|
67
49
|
>
|
|
68
50
|
{#if multiple}
|
|
69
51
|
<DropdownListItemsMultiple
|