qc-trousse-sdg 1.4.6 → 1.4.7

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 (57) hide show
  1. package/README.md +10 -0
  2. package/dist/css/qc-sdg-no-grid.min.css +1 -1
  3. package/dist/css/qc-sdg.min.css +1 -1
  4. package/dist/js/qc-sdg.min.js +1 -1
  5. package/package-lock.json +4689 -0
  6. package/package.json +1 -1
  7. package/public/css/qc-doc-sdg.css +38 -6
  8. package/public/css/qc-sdg-no-grid.css +47 -20
  9. package/public/css/qc-sdg.css +47 -20
  10. package/public/index.html +58 -20
  11. package/public/js/qc-doc-sdg.js +294 -204
  12. package/public/js/qc-sdg.js +663 -315
  13. package/src/doc/qc-doc-sdg.js +6 -1
  14. package/src/doc/scss/components/_exemple.scss +5 -1
  15. package/src/doc/scss/qc-doc-sdg.scss +22 -4
  16. package/src/sdg/bases/Icon/Icon.svelte +2 -0
  17. package/src/sdg/bases/links/_links.scss +18 -12
  18. package/src/sdg/components/Alert/Alert.svelte +28 -9
  19. package/src/sdg/components/Alert/AlertWC.svelte +20 -5
  20. package/src/sdg/components/Alert/Test/AlertSvelteTest.svelte +25 -0
  21. package/src/sdg/components/Alert/Test/alertBaselineTest.html +13 -0
  22. package/src/sdg/components/Alert/Test/alertSvelteTest.html +1 -0
  23. package/src/sdg/components/Alert/_alert.html +23 -11
  24. package/src/sdg/components/Checkbox/Checkbox.svelte +6 -5
  25. package/src/sdg/components/DropdownList/DropdownList.svelte +65 -14
  26. package/src/sdg/components/DropdownList/DropdownListButton/DropdownListButton.svelte +2 -6
  27. package/src/sdg/components/DropdownList/DropdownListItems/DropdownListItems.svelte +4 -22
  28. package/src/sdg/components/DropdownList/DropdownListItems/DropdownListItemsMultiple/DropdownListItemsMultiple.svelte +2 -1
  29. package/src/sdg/components/DropdownList/DropdownListItems/DropdownListItemsSingle/DropdownListItemsSingle.svelte +2 -1
  30. package/src/sdg/components/DropdownList/SelectWC.svelte +39 -13
  31. package/src/sdg/components/DropdownList/Test/DropdownListSvelteTest.svelte +2 -2
  32. package/src/sdg/components/DropdownList/Test/dropdownListBaselineTest.html +7 -0
  33. package/src/sdg/components/DropdownList/_dropdownList.scss +7 -5
  34. package/src/sdg/components/DropdownList/_select.html +31 -5
  35. package/src/sdg/components/ExternalLink/ExternalLink.svelte +36 -74
  36. package/src/sdg/components/ExternalLink/ExternalLinkWC.svelte +44 -1
  37. package/src/sdg/components/ExternalLink/externalLinkBaselineTest.html +45 -0
  38. package/src/sdg/components/Fieldset/_fieldset.scss +1 -1
  39. package/src/sdg/components/Label/LabelText.svelte +2 -1
  40. package/src/sdg/components/Label/_label.scss +10 -2
  41. package/src/sdg/components/PivFooter/_pivFooter.html +4 -4
  42. package/src/sdg/components/TextField/Test/TextFieldEmbededTest.svelte +19 -3
  43. package/src/sdg/components/TextField/Test/textFieldBaselineTest.html +5 -2
  44. package/src/sdg/components/TextField/TextField.svelte +12 -6
  45. package/src/sdg/components/TextField/TextFieldWC.svelte +18 -6
  46. package/src/sdg/components/TextField/textFieldUtils.js +3 -2
  47. package/src/sdg/components/utils.js +23 -0
  48. package/src/sdg/qc-sdg-test.js +2 -1
  49. package/src/sdg/scss/lib/_mixins.scss +6 -0
  50. package/src/sdg/scss/utilities/_states.scss +1 -1
  51. package/tests/alert-baseline.spec.ts +23 -0
  52. package/tests/alert-svelte.spec.ts +23 -0
  53. package/tests/buildSvelteTestsIgnore.json +2 -1
  54. package/tests/dropdown-list-baseline.spec.ts +8 -0
  55. package/tests/external-link-baseline.spec.ts +30 -0
  56. package/tests/textfield-baseline.spec.ts +5 -5
  57. package/tests/textfield-svelte.spec.ts +5 -5
@@ -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
 
@@ -18,4 +18,8 @@
18
18
  & > figcaption {
19
19
  display: block;
20
20
  }
21
- }
21
+ &:has(qc-select) {
22
+ overflow-x: visible;
23
+ }
24
+ }
25
+
@@ -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
- &.pseudo-visited {
107
- color: token-value(color,link,visited)!important;
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
- span.qc-ext-link-img {
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
- margin-left: 4px;
34
- a:visited & {
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
- rootElement.dispatchEvent(
31
- new CustomEvent('qc.alert.hide', {
32
- bubbles: true,
33
- composed: true
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
- const props = $props();
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
- {...props}
23
- slotContent = {`<slot />`}
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('Fermeture de l\'alerte id=\'' + e.target.id + '\'');
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 (!required) return;
49
- if (!labelElement) return;
50
- labelElement.appendChild(requiredSpan);
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
- Utils.sleep(5).then(() => {
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
- handleButtonKeyDown(e, enableSearch ? searchInput : dropdownItems);
321
- }}
322
- bind:this={button}
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
- closeDropdown("");
363
- button?.focus();
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={button}
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
- Utils.sleep(5).then(() => {
25
+ tick().then(() => {
43
26
  itemsComponent?.focusOnFirstElement();
44
27
  }).catch(console.error);
45
28
  }
46
29
 
47
30
  export function focusOnLastElement() {
48
- Utils.sleep(5).then(() => {
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
- Utils.sleep(5).then(() => {
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