origam 2.17.1 → 2.18.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 (207) hide show
  1. package/CHANGELOG.md +690 -1
  2. package/dist/src/assets/css/main.css +1 -1
  3. package/dist/src/assets/css/tokens/dark.css +119 -20
  4. package/dist/src/assets/css/tokens/light.css +46 -10
  5. package/dist/src/assets/locales/en.json +21 -6
  6. package/dist/src/assets/locales/fr.json +21 -6
  7. package/dist/src/assets/scss/main.scss +10 -2
  8. package/dist/src/assets/scss/tokens/_dark.scss +2843 -10
  9. package/dist/src/assets/scss/tokens/_light.scss +46 -10
  10. package/dist/src/components/Alert/OrigamAlert.vue +6 -6
  11. package/dist/src/components/Avatar/OrigamAvatar.vue +15 -2
  12. package/dist/src/components/Badge/OrigamBadge.vue +7 -7
  13. package/dist/src/components/Breadcrumb/OrigamBreadcrumbItem.vue +23 -4
  14. package/dist/src/components/Btn/OrigamBtn.vue +21 -3
  15. package/dist/src/components/Btn/OrigamBtnGroup.vue +1 -1
  16. package/dist/src/components/Calendar/OrigamCalendar.vue +1 -1
  17. package/dist/src/components/Card/OrigamCard.vue +2 -0
  18. package/dist/src/components/Card/OrigamCardHeader.vue +7 -7
  19. package/dist/src/components/Carousel/OrigamCarousel.vue +14 -1
  20. package/dist/src/components/Chart/OrigamChartBoxPlot.vue +1 -1
  21. package/dist/src/components/Chart/OrigamChartBullet.vue +3 -2
  22. package/dist/src/components/Chart/OrigamChartCandlestick.vue +2 -2
  23. package/dist/src/components/Chart/OrigamChartCartesian.vue +49 -4
  24. package/dist/src/components/Chart/OrigamChartLegend.vue +39 -18
  25. package/dist/src/components/Chart/OrigamChartPareto.vue +7 -1
  26. package/dist/src/components/Chart/OrigamChartPictorial.vue +2 -2
  27. package/dist/src/components/Chart/OrigamChartRadar.vue +3 -1
  28. package/dist/src/components/Chart/OrigamChartSparkline.vue +3 -1
  29. package/dist/src/components/Chart/OrigamChartStreamgraph.vue +2 -1
  30. package/dist/src/components/Checkbox/OrigamCheckbox.vue +2 -0
  31. package/dist/src/components/Checkbox/OrigamCheckboxGroup.vue +36 -11
  32. package/dist/src/components/Chip/OrigamChip.vue +19 -4
  33. package/dist/src/components/Code/OrigamCode.vue +20 -3
  34. package/dist/src/components/ColorPickerField/OrigamColorPickerField.vue +30 -5
  35. package/dist/src/components/CommandPalette/OrigamCommandPalette.vue +3 -3
  36. package/dist/src/components/ConfirmWrapper/OrigamConfirmWrapper.vue +6 -6
  37. package/dist/src/components/ContextualMenu/OrigamContextualMenu.vue +0 -4
  38. package/dist/src/components/DataList/OrigamDataList.vue +2 -0
  39. package/dist/src/components/DataList/OrigamDataText.vue +6 -6
  40. package/dist/src/components/DataList/OrigamDataTitle.vue +6 -6
  41. package/dist/src/components/DataTable/OrigamDataTableHeaderCell.vue +1 -1
  42. package/dist/src/components/DataTable/OrigamDataTableHeadersCell.vue +4 -1
  43. package/dist/src/components/DatePicker/OrigamDatePicker.vue +2 -0
  44. package/dist/src/components/DatePicker/OrigamDatePickerHeader.vue +6 -6
  45. package/dist/src/components/DatePickerField/OrigamDatePickerField.vue +21 -1
  46. package/dist/src/components/Dialog/OrigamDialog.vue +25 -0
  47. package/dist/src/components/Dialog/OrigamDialogConfirmation.vue +6 -0
  48. package/dist/src/components/ExpansionPanel/OrigamExpansionPanel.vue +2 -0
  49. package/dist/src/components/ExpansionPanel/OrigamExpansionPanelHeader.vue +6 -6
  50. package/dist/src/components/Field/OrigamField.vue +47 -15
  51. package/dist/src/components/FileField/OrigamFileField.vue +5 -1
  52. package/dist/src/components/Icon/OrigamSvgIcon.vue +3 -1
  53. package/dist/src/components/Img/OrigamImg.vue +9 -2
  54. package/dist/src/components/InfiniteScroll/OrigamInfiniteScroll.vue +34 -18
  55. package/dist/src/components/InlineEdit/OrigamInlineEdit.vue +46 -5
  56. package/dist/src/components/Input/OrigamInput.vue +6 -6
  57. package/dist/src/components/List/OrigamListGroup.vue +4 -0
  58. package/dist/src/components/List/OrigamListItem.vue +68 -4
  59. package/dist/src/components/Masonry/OrigamMasonry.vue +18 -2
  60. package/dist/src/components/Menu/OrigamMenu.vue +79 -9
  61. package/dist/src/components/NumberField/OrigamNumberField.vue +13 -3
  62. package/dist/src/components/OtpInputField/OrigamOtpInputField.vue +35 -11
  63. package/dist/src/components/PasswordField/OrigamPasswordField.vue +5 -1
  64. package/dist/src/components/Radio/OrigamRadio.vue +2 -0
  65. package/dist/src/components/Radio/OrigamRadioGroup.vue +36 -11
  66. package/dist/src/components/RatingField/OrigamRatingField.vue +58 -7
  67. package/dist/src/components/RatingField/OrigamRatingFieldItem.vue +78 -30
  68. package/dist/src/components/Select/OrigamSelect.vue +44 -21
  69. package/dist/src/components/SliderField/OrigamSliderField.vue +20 -10
  70. package/dist/src/components/Snackbar/OrigamSnackbarGroup.vue +1 -1
  71. package/dist/src/components/Switch/OrigamSwitch.vue +2 -0
  72. package/dist/src/components/Tabs/OrigamTab.vue +7 -4
  73. package/dist/src/components/Tabs/OrigamTabPanel.vue +4 -2
  74. package/dist/src/components/Tabs/OrigamTabs.vue +7 -5
  75. package/dist/src/components/TextField/OrigamTextField.vue +5 -1
  76. package/dist/src/components/TextareaField/OrigamTextareaField.vue +5 -1
  77. package/dist/src/components/Transition/OrigamExpandX.vue +19 -3
  78. package/dist/src/components/Transition/OrigamExpandY.vue +19 -3
  79. package/dist/src/components/Transition/OrigamTranslateScale.vue +26 -4
  80. package/dist/src/components/Video/OrigamVideo.vue +3 -2
  81. package/dist/src/components/Window/OrigamWindow.vue +20 -3
  82. package/dist/src/composables/Commons/accessibleCommand.composable.cjs +26 -0
  83. package/dist/src/composables/Commons/accessibleCommand.composable.d.ts +78 -0
  84. package/dist/src/composables/Commons/accessibleCommand.composable.js +20 -0
  85. package/dist/src/composables/Commons/activator.composable.cjs +10 -1
  86. package/dist/src/composables/Commons/activator.composable.js +11 -2
  87. package/dist/src/composables/Commons/adjacent.composable.cjs +17 -0
  88. package/dist/src/composables/Commons/adjacent.composable.d.ts +12 -0
  89. package/dist/src/composables/Commons/adjacent.composable.js +17 -0
  90. package/dist/src/composables/Commons/adjacentInner.composable.cjs +20 -0
  91. package/dist/src/composables/Commons/adjacentInner.composable.d.ts +24 -5
  92. package/dist/src/composables/Commons/adjacentInner.composable.js +20 -0
  93. package/dist/src/composables/Commons/audio.composable.cjs +14 -1
  94. package/dist/src/composables/Commons/audio.composable.js +14 -1
  95. package/dist/src/composables/Commons/backButton.composable.cjs +3 -1
  96. package/dist/src/composables/Commons/backButton.composable.js +3 -1
  97. package/dist/src/composables/Commons/border.composable.d.ts +13 -0
  98. package/dist/src/composables/Commons/colorEffect.composable.cjs +58 -46
  99. package/dist/src/composables/Commons/colorEffect.composable.d.ts +10 -5
  100. package/dist/src/composables/Commons/colorEffect.composable.js +70 -46
  101. package/dist/src/composables/Commons/createLayout.composable.cjs +21 -12
  102. package/dist/src/composables/Commons/createLayout.composable.js +21 -12
  103. package/dist/src/composables/Commons/delay.composable.d.ts +16 -0
  104. package/dist/src/composables/Commons/dimension.composable.d.ts +11 -0
  105. package/dist/src/composables/Commons/elevation.composable.cjs +6 -2
  106. package/dist/src/composables/Commons/elevation.composable.d.ts +37 -1
  107. package/dist/src/composables/Commons/elevation.composable.js +7 -2
  108. package/dist/src/composables/Commons/goTo.composable.cjs +4 -2
  109. package/dist/src/composables/Commons/goTo.composable.js +5 -3
  110. package/dist/src/composables/Commons/group.composable.cjs +14 -14
  111. package/dist/src/composables/Commons/group.composable.js +14 -14
  112. package/dist/src/composables/Commons/hotkey.composable.d.ts +18 -7
  113. package/dist/src/composables/Commons/hydration.composable.d.ts +8 -0
  114. package/dist/src/composables/Commons/link.composable.d.ts +20 -0
  115. package/dist/src/composables/Commons/locationStrategies.composable.d.ts +17 -0
  116. package/dist/src/composables/Commons/message.composable.d.ts +19 -5
  117. package/dist/src/composables/Commons/position.composable.d.ts +17 -6
  118. package/dist/src/composables/Commons/refs.composable.d.ts +11 -5
  119. package/dist/src/composables/Commons/rounded.composable.d.ts +11 -1
  120. package/dist/src/composables/Commons/scrolling.composable.cjs +15 -12
  121. package/dist/src/composables/Commons/scrolling.composable.js +15 -12
  122. package/dist/src/composables/Commons/size.composable.d.ts +15 -0
  123. package/dist/src/composables/Commons/ssrBoot.composable.cjs +9 -1
  124. package/dist/src/composables/Commons/ssrBoot.composable.js +10 -2
  125. package/dist/src/composables/Commons/stack.composable.cjs +13 -1
  126. package/dist/src/composables/Commons/stack.composable.js +13 -1
  127. package/dist/src/composables/Commons/theme-props-resolver.composable.cjs +41 -32
  128. package/dist/src/composables/Commons/theme-props-resolver.composable.js +41 -32
  129. package/dist/src/composables/Commons/validation.composable.cjs +3 -3
  130. package/dist/src/composables/Commons/validation.composable.js +3 -3
  131. package/dist/src/composables/Commons/virtual.composable.cjs +24 -8
  132. package/dist/src/composables/Commons/virtual.composable.js +24 -8
  133. package/dist/src/composables/Masonry/masonry.composable.cjs +18 -11
  134. package/dist/src/composables/Masonry/masonry.composable.js +18 -11
  135. package/dist/src/composables/Parallax/parallax.composable.cjs +1 -1
  136. package/dist/src/composables/Parallax/parallax.composable.js +1 -1
  137. package/dist/src/composables/index.cjs +36 -0
  138. package/dist/src/composables/index.d.ts +3 -0
  139. package/dist/src/composables/index.js +3 -0
  140. package/dist/src/consts/Chart/chart-cartesian.const.cjs +5 -2
  141. package/dist/src/consts/Chart/chart-cartesian.const.d.ts +42 -0
  142. package/dist/src/consts/Chart/chart-cartesian.const.js +3 -0
  143. package/dist/src/consts/Commons/elevation.const.cjs +5 -1
  144. package/dist/src/consts/Commons/elevation.const.d.ts +52 -2
  145. package/dist/src/consts/Commons/elevation.const.js +4 -0
  146. package/dist/src/consts/Commons/ripple.const.cjs +13 -3
  147. package/dist/src/consts/Commons/ripple.const.js +13 -3
  148. package/dist/src/consts/RatingField/rating-field.const.cjs +10 -0
  149. package/dist/src/consts/RatingField/rating-field.const.d.ts +36 -0
  150. package/dist/src/consts/RatingField/rating-field.const.js +12 -0
  151. package/dist/src/consts/index.cjs +11 -0
  152. package/dist/src/consts/index.d.ts +1 -0
  153. package/dist/src/consts/index.js +1 -0
  154. package/dist/src/directives/ClickOutside/clickOutside.directive.cjs +2 -0
  155. package/dist/src/directives/ClickOutside/clickOutside.directive.js +2 -0
  156. package/dist/src/directives/Ripple/ripple.directive.cjs +2 -0
  157. package/dist/src/directives/Ripple/ripple.directive.js +2 -0
  158. package/dist/src/enums/Commons/color.enum.cjs +0 -1
  159. package/dist/src/enums/Commons/color.enum.d.ts +14 -5
  160. package/dist/src/enums/Commons/color.enum.js +0 -1
  161. package/dist/src/enums/Sheet/sheet.enum.d.ts +1 -1
  162. package/dist/src/interfaces/Chart/chart-cartesian.interface.d.ts +6 -2
  163. package/dist/src/interfaces/Commons/activator.interface.d.ts +3 -2
  164. package/dist/src/interfaces/Commons/adjacent.interface.d.ts +29 -0
  165. package/dist/src/interfaces/Commons/group.interface.d.ts +13 -2
  166. package/dist/src/interfaces/Commons/ripple.interface.d.ts +16 -0
  167. package/dist/src/interfaces/Commons/scroll.interface.d.ts +3 -2
  168. package/dist/src/interfaces/DataTable/data-table-header.interface.d.ts +2 -2
  169. package/dist/src/interfaces/DatePicker/date-picker-month.interface.d.ts +2 -1
  170. package/dist/src/interfaces/Menu/menu.interface.d.ts +28 -1
  171. package/dist/src/interfaces/OtpInputField/otp-input-field.interface.d.ts +21 -1
  172. package/dist/src/interfaces/RatingField/rating-field-item.interface.d.ts +16 -1
  173. package/dist/src/interfaces/index.cjs +11 -0
  174. package/dist/src/interfaces/index.d.ts +1 -0
  175. package/dist/src/interfaces/index.js +1 -0
  176. package/dist/src/themes/origam.theme.cjs +8 -14
  177. package/dist/src/themes/origam.theme.js +6 -8
  178. package/dist/src/types/Commons/color.type.d.ts +4 -3
  179. package/dist/src/types/Commons/commons.type.d.ts +33 -0
  180. package/dist/src/types/Commons/theme.type.d.ts +2 -1
  181. package/dist/src/types/Sheet/sheet.type.d.ts +2 -1
  182. package/dist/src/types/index.cjs +22 -0
  183. package/dist/src/types/index.d.ts +2 -0
  184. package/dist/src/types/index.js +2 -0
  185. package/dist/src/types/tokens.type.d.ts +1 -1
  186. package/dist/src/utils/Commons/a11y.util.cjs +35 -0
  187. package/dist/src/utils/Commons/a11y.util.d.ts +63 -0
  188. package/dist/src/utils/Commons/a11y.util.js +33 -0
  189. package/dist/src/utils/Commons/clickOutside.util.cjs +5 -1
  190. package/dist/src/utils/Commons/clickOutside.util.js +5 -1
  191. package/dist/src/utils/Commons/color.util.cjs +10 -5
  192. package/dist/src/utils/Commons/color.util.d.ts +34 -5
  193. package/dist/src/utils/Commons/color.util.js +9 -5
  194. package/dist/src/utils/Commons/goTo.util.cjs +31 -14
  195. package/dist/src/utils/Commons/goTo.util.d.ts +1 -1
  196. package/dist/src/utils/Commons/goTo.util.js +31 -16
  197. package/dist/src/utils/Commons/location.util.cjs +18 -3
  198. package/dist/src/utils/Commons/location.util.js +18 -3
  199. package/dist/src/utils/Commons/ripple.util.cjs +5 -1
  200. package/dist/src/utils/Commons/ripple.util.js +5 -1
  201. package/dist/src/utils/index.cjs +11 -0
  202. package/dist/src/utils/index.d.ts +1 -0
  203. package/dist/src/utils/index.js +1 -0
  204. package/package.json +13 -2
  205. package/dist/src/types/Commons/locale.type.cjs +0 -1
  206. package/dist/src/types/Commons/locale.type.d.ts +0 -0
  207. package/dist/src/types/Commons/locale.type.js +0 -0
@@ -28,8 +28,7 @@
28
28
  v-if="hasPrepend"
29
29
  key="prepend"
30
30
  class="origam-list-item__prepend"
31
- :role="isPrependZoneFocusable ? 'button' : undefined"
32
- :tabindex="isPrependZoneFocusable ? 0 : undefined"
31
+ v-bind="prependCommandAttrs"
33
32
  @click="handleClickPrepend"
34
33
  @keydown="handleKeydownPrepend"
35
34
  >
@@ -92,8 +91,7 @@
92
91
  v-if="hasAppend"
93
92
  key="append"
94
93
  class="origam-list-item__append"
95
- :role="isAppendZoneFocusable ? 'button' : undefined"
96
- :tabindex="isAppendZoneFocusable ? 0 : undefined"
94
+ v-bind="appendCommandAttrs"
97
95
  @click="handleClickAppend"
98
96
  @keydown="handleKeydownAppend"
99
97
  >
@@ -123,6 +121,7 @@
123
121
  import { computed, onBeforeMount, StyleValue, toRef, useAttrs, useSlots, watch } from "vue";
124
122
  import OrigamAvatar from "../Avatar/OrigamAvatar.vue";
125
123
  import OrigamIcon from "../Icon/OrigamIcon.vue";
124
+ import { useAccessibleCommand } from "../../composables/Commons/accessibleCommand.composable";
126
125
  import { useAdjacent } from "../../composables/Commons/adjacent.composable";
127
126
  import { useBothColor } from "../../composables/Commons/bothColor.composable";
128
127
  import { useDensity } from "../../composables/Commons/density.composable";
@@ -207,6 +206,8 @@ const props = defineProps({
207
206
  appendIcon: { type: null, required: false },
208
207
  prependAvatar: { type: String, required: false },
209
208
  prependIcon: { type: null, required: false },
209
+ prependAriaLabel: { type: String, required: false },
210
+ appendAriaLabel: { type: String, required: false },
210
211
  hover: { type: [Boolean, Object], required: false },
211
212
  hoverClass: { type: String, required: false },
212
213
  fontSize: { type: null, required: false },
@@ -261,6 +262,20 @@ const {
261
262
  } = useAdjacent(props, toRef(props, "prependIcon"), toRef(props, "appendIcon"));
262
263
  const isPrependZoneFocusable = computed(() => isPrependClickable.value && !link.isLink.value);
263
264
  const isAppendZoneFocusable = computed(() => isAppendClickable.value && !link.isLink.value);
265
+ const prependCommandAttrs = useAccessibleCommand({
266
+ component: "OrigamListItem",
267
+ zone: "prepend",
268
+ prop: "prependAriaLabel",
269
+ active: isPrependZoneFocusable,
270
+ label: () => props.prependAriaLabel
271
+ });
272
+ const appendCommandAttrs = useAccessibleCommand({
273
+ component: "OrigamListItem",
274
+ zone: "append",
275
+ prop: "appendAriaLabel",
276
+ active: isAppendZoneFocusable,
277
+ label: () => props.appendAriaLabel
278
+ });
264
279
  const isActive = computed(() => {
265
280
  return isActiveFlag.value || link.isActive?.value || isSelected.value;
266
281
  });
@@ -473,6 +488,55 @@ defineExpose({
473
488
  --origam-list-item---padding-inline-end: var(--origam-list-item--slim---padding-inline-end, 8px);
474
489
  }
475
490
 
491
+ /*********************************************************
492
+ * Densité — la LIGNE fait autorité, la liste ne pose qu'un défaut
493
+ *
494
+ * @description
495
+ * Ces trois règles REDÉCLARENT `--origam-list---density`, la même
496
+ * custom property que `.origam-list--density-*` pose sur la liste
497
+ * (`OrigamList.vue:453-463`), avec les mêmes valeurs. Elles
498
+ * n'ajoutent PAS un second terme au `calc()` de la règle de base.
499
+ *
500
+ * @description
501
+ * ⛔ CETTE DISTINCTION EST TOUT LE TICKET (#571). Mesuré en
502
+ * Chromium, liste en `compact` :
503
+ *
504
+ * redéclaration (ici) min-height = 48px — inchangé
505
+ * second terme dans le calc() min-height = 40px — -8px compté DEUX FOIS
506
+ *
507
+ * Une redéclaration écrit la même valeur sur un élément plus
508
+ * proche : le `calc()` la lit une fois. C'est ce qui rend ce
509
+ * câblage gratuit en rendu à l'intérieur d'une liste, et c'est la
510
+ * promesse sur laquelle l'arbitrage a été rendu.
511
+ *
512
+ * @description
513
+ * Ce que ça répare : une ligne SANS liste ancêtre n'avait aucune
514
+ * densité — la variable ne venait que de la liste, donc la prop
515
+ * rendait 56px quelle que soit sa valeur. Mesuré avant/après :
516
+ * `density="compact"` hors liste passe de 56px à 48px.
517
+ *
518
+ * @description
519
+ * ⚠️ `withDefaults` ne pose AUCUNE densité par défaut et
520
+ * `useDensity` n'émet rien sur `null`/`undefined` : une ligne à
521
+ * laquelle personne ne passe `density` n'émet donc aucune de ces
522
+ * classes, et la valeur de la liste la traverse intacte. C'est ce
523
+ * qui préserve `<origam-list density="comfortable">`. Le thème
524
+ * livré posait `density: 'compact'` sur CHAQUE ligne, ce qui aurait
525
+ * fait gagner la ligne contre la liste sur tout le catalogue —
526
+ * l'entrée a été retirée de `origam.theme.ts` dans le même lot.
527
+ ********************************************************/
528
+ &--density-default {
529
+ --origam-list---density: 0px;
530
+ }
531
+
532
+ &--density-compact {
533
+ --origam-list---density: -8px;
534
+ }
535
+
536
+ &--density-comfortable {
537
+ --origam-list---density: 8px;
538
+ }
539
+
476
540
  // Row-height scale, aligned rung for rung on the control-height scale
477
541
  // (`--origam-input__control---height-{sm,md,lg,xl}` = 28/36/44/52px) so
478
542
  // a row and a field of the same `size` share an identical vertical
@@ -30,6 +30,7 @@
30
30
  <script lang="ts" setup>
31
31
  import {
32
32
  computed,
33
+ onBeforeUnmount,
33
34
  onMounted,
34
35
  ref,
35
36
  StyleValue,
@@ -138,14 +139,29 @@ const { containerRef, setItem, layout, relayout } = useMasonry({
138
139
  breakpointsRef: toRef(props, "columnBreakpoints"),
139
140
  alignRef: toRef(props, "align")
140
141
  });
142
+ const frames = /* @__PURE__ */ new Set();
143
+ let disposed = false;
144
+ const scheduleFrame = (cb) => {
145
+ if (disposed || typeof window === "undefined") return;
146
+ const id = window.requestAnimationFrame(() => {
147
+ frames.delete(id);
148
+ cb();
149
+ });
150
+ frames.add(id);
151
+ };
152
+ onBeforeUnmount(() => {
153
+ disposed = true;
154
+ for (const id of frames) window.cancelAnimationFrame(id);
155
+ frames.clear();
156
+ });
141
157
  onMounted(() => {
142
158
  containerRef.value = rootEl.value;
143
159
  resolveGapPx();
144
- requestAnimationFrame(() => relayout());
160
+ scheduleFrame(() => relayout());
145
161
  });
146
162
  const { theme, mode } = useTheme();
147
163
  watch([() => props.gap, theme, mode], () => {
148
- requestAnimationFrame(resolveGapPx);
164
+ scheduleFrame(resolveGapPx);
149
165
  });
150
166
  const childrenVNodes = computed(() => {
151
167
  const raw = slots.default?.() ?? [];
@@ -76,7 +76,7 @@
76
76
  </template>
77
77
 
78
78
  <script lang="ts" setup>
79
- import { computed, inject, mergeProps, nextTick, provide, ref, shallowRef, StyleValue, toRef, watch } from "vue";
79
+ import { computed, inject, mergeProps, nextTick, onBeforeUnmount, provide, ref, shallowRef, StyleValue, toRef, watch } from "vue";
80
80
  import OrigamList from "../List/OrigamList.vue";
81
81
  import OrigamListGroup from "../List/OrigamListGroup.vue";
82
82
  import OrigamListItem from "../List/OrigamListItem.vue";
@@ -206,10 +206,6 @@ const props = defineProps({
206
206
  to: { type: null, required: false },
207
207
  exact: { type: Boolean, required: false },
208
208
  ripple: { type: [Boolean, Object], required: false },
209
- appendAvatar: { type: String, required: false },
210
- appendIcon: { type: null, required: false },
211
- prependAvatar: { type: String, required: false },
212
- prependIcon: { type: null, required: false },
213
209
  hover: { type: [Boolean, Object], required: false },
214
210
  hoverClass: { type: String, required: false },
215
211
  fontSize: { type: null, required: false },
@@ -240,6 +236,22 @@ const id = computed(() => props.id || `origam-menu-${uid}`);
240
236
  const origamOverlayRef = ref();
241
237
  const parent = inject(ORIGAM_MENU_KEY, null);
242
238
  const openChildren = shallowRef(0);
239
+ let disposed = false;
240
+ const timers = /* @__PURE__ */ new Set();
241
+ const scheduleTimeout = (cb, delay) => {
242
+ if (disposed) return;
243
+ const id2 = setTimeout(() => {
244
+ timers.delete(id2);
245
+ if (disposed) return;
246
+ cb();
247
+ }, delay);
248
+ timers.add(id2);
249
+ };
250
+ onBeforeUnmount(() => {
251
+ disposed = true;
252
+ for (const id2 of timers) clearTimeout(id2);
253
+ timers.clear();
254
+ });
243
255
  provide(ORIGAM_MENU_KEY, {
244
256
  register() {
245
257
  ++openChildren.value;
@@ -248,7 +260,7 @@ provide(ORIGAM_MENU_KEY, {
248
260
  --openChildren.value;
249
261
  },
250
262
  closeParents() {
251
- setTimeout(() => {
263
+ scheduleTimeout(() => {
252
264
  if (!openChildren.value) {
253
265
  isActive.value = false;
254
266
  parent?.closeParents();
@@ -316,7 +328,7 @@ const handleActivatorKeydown = (e) => {
316
328
  } else if (keyDown.includes(e.key)) {
317
329
  isActive.value = true;
318
330
  e.preventDefault();
319
- setTimeout(() => setTimeout(() => handleActivatorKeydown(e)));
331
+ scheduleTimeout(() => scheduleTimeout(() => handleActivatorKeydown(e)));
320
332
  }
321
333
  };
322
334
  const activatorProps = computed(() => {
@@ -382,6 +394,50 @@ defineExpose(forwardRefs({
382
394
  box-shadow: none;
383
395
  }
384
396
 
397
+ /**
398
+ * #742 — le plafond de hauteur et le défilement DOIVENT porter sur la
399
+ * même boîte.
400
+ *
401
+ * Deux plafonds coexistent, et aucun des deux n'était le scrollport :
402
+ *
403
+ * 1. `.origam-overlay__content` — plafond posé en INLINE par
404
+ * `useDimension` quand le consommateur passe `max-height`
405
+ * (c'est le chemin d'`OrigamSelect`, qui passe `:max-height="310"`),
406
+ * ou par la stratégie de placement quand il n'y a pas de prop.
407
+ * 2. `.origam-menu__content` — plafond du token
408
+ * `--origam-menu---max-height`.
409
+ *
410
+ * Le `overflow` était, lui, posé sur `.origam-menu__list` — une boîte
411
+ * SANS plafond (`max-height: none`), donc `clientHeight === scrollHeight`
412
+ * et rien à faire défiler. Mesuré avant correctif, Chromium, 30 options :
413
+ * overlay 240/1453, menu__content 580/1448, list 1448/1448, les trois à
414
+ * `scrollTop === 0`. Le nom même du token (`--origam-menu__content---overflow`)
415
+ * disait déjà où la règle devait vivre.
416
+ *
417
+ * Le correctif rend `.origam-menu__content` — la boîte qui porte la
418
+ * surface visible, donc celle dont l'ascenseur doit épouser le rayon et
419
+ * le fond — scrollport :
420
+ *
421
+ * • `overflow` revient sur `.origam-menu__content` (token inchangé) ;
422
+ * • le parent `.origam-overlay__content` devient un conteneur flex
423
+ * colonne, ce qui rend son plafond (inline ou calculé) opposable à
424
+ * son enfant : sans cela un `max-height` posé sur le PARENT ne
425
+ * contraint pas un enfant en flux normal, et le contenu ressort.
426
+ * • `min-height: 0` lève le plancher `min-height: auto` des items
427
+ * flex, sans quoi l'enfant refuserait de descendre sous la hauteur
428
+ * de son contenu et ne défilerait toujours pas.
429
+ *
430
+ * Les sous-menus ne sont PAS rognés : `useTeleport` résout
431
+ * `props.attach || props.contained` à `false` → `document.body`, donc
432
+ * chaque niveau est téléporté dans `body > .origam-overlay-container` et
433
+ * n'est jamais un descendant DOM du scrollport. Vérifié en runtime
434
+ * (`root.contains(flyout) === false`).
435
+ */
436
+ .origam-menu :deep(.origam-overlay__content) {
437
+ display: flex;
438
+ flex-direction: column;
439
+ }
440
+
385
441
  .origam-menu__content {
386
442
  background: var(--origam-menu---background, var(--origam-color__surface---raised));
387
443
  backdrop-filter: var(--origam-menu---backdrop-filter, none);
@@ -390,11 +446,25 @@ defineExpose(forwardRefs({
390
446
  border-radius: var(--origam-menu---border-radius, 8px);
391
447
  box-shadow: var(--origam-menu---box-shadow);
392
448
  max-height: var(--origam-menu---max-height, calc(100vh - 32px));
393
- display: inline-block;
449
+ min-height: 0;
450
+ overflow: var(--origam-menu__content---overflow, auto);
451
+ /**
452
+ * `block`, plus `inline-block`. Un item flex est BLOCKIFIÉ : depuis
453
+ * que le parent est un conteneur flex, `inline-block` ne pouvait
454
+ * plus gagner — le déclarer aurait été exactement le genre de
455
+ * déclaration morte que ce ticket corrige. Le rétrécissement au
456
+ * contenu était déjà assuré par `width: max-content`, pas par
457
+ * `inline-block` : mesuré avant/après sur 8 panneaux (menu, select,
458
+ * contextual-menu, date-picker-field, color-picker-field,
459
+ * media-controller), le rectangle du panneau est IDENTIQUE au pixel.
460
+ * Seule la boîte parente perd 5px — le descendeur de la line-box
461
+ * qu'un enfant inline-block imposait à son bloc parent, bande
462
+ * transparente sous chaque panneau.
463
+ */
464
+ display: block;
394
465
  width: max-content;
395
466
 
396
467
  .origam-menu__list {
397
- overflow: var(--origam-menu__content---overflow, auto);
398
468
  max-width: var(--origam-menu__content---max-width, 320px);
399
469
  padding: var(--origam-menu__content---padding, 4px);
400
470
  }
@@ -348,6 +348,8 @@ const props = defineProps({
348
348
  prependInnerIcon: { type: null, required: false },
349
349
  clearIcon: { type: null, required: false },
350
350
  clearable: { type: Boolean, required: false },
351
+ prependInnerAriaLabel: { type: String, required: false },
352
+ appendInnerAriaLabel: { type: String, required: false },
351
353
  border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true, default: true },
352
354
  borderTop: { type: [Boolean, Number, String], required: false },
353
355
  borderLeft: { type: [Boolean, Number, String], required: false },
@@ -415,7 +417,9 @@ const props = defineProps({
415
417
  appendAvatar: { type: String, required: false },
416
418
  appendIcon: { type: null, required: false },
417
419
  prependAvatar: { type: String, required: false },
418
- prependIcon: { type: null, required: false }
420
+ prependIcon: { type: null, required: false },
421
+ prependAriaLabel: { type: String, required: false },
422
+ appendAriaLabel: { type: String, required: false }
419
423
  });
420
424
  const emits = defineEmits(["click:control", "mousedown:control", "click:clear", "increment", "decrement", "update:focused", "click:appendInner", "click:prependInner", "update:modelValue", "click:append", "click:prepend"]);
421
425
  defineSlots();
@@ -733,7 +737,11 @@ defineExpose(forwardRefs({
733
737
  }
734
738
 
735
739
  :deep(.origam-field--appended) {
736
- --origam-field---padding-end: 0;
740
+ // #800 — l'unite est obligatoire (`0` nu = `<number>`, jete toute
741
+ // declaration `max()` / `calc()` qui le lit). Sans effet de rendu ici
742
+ // (mesure Chromium) : `--padding-end` n'est pas un terme du plancher,
743
+ // il est passe tel quel comme seconde valeur de `padding-inline`.
744
+ --origam-field---padding-end: 0px;
737
745
  }
738
746
 
739
747
  &__control {
@@ -761,7 +769,9 @@ defineExpose(forwardRefs({
761
769
  }
762
770
 
763
771
  :deep(.origam-field--prepended) {
764
- --origam-field---padding-start: 0;
772
+ // #800 — idem : l'unite est obligatoire. Sans effet de rendu, la
773
+ // cible etant `--prepended`, qui n'applique pas le plancher.
774
+ --origam-field---padding-start: 0px;
765
775
  }
766
776
  }
767
777
 
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <div
3
+ :id="styleId"
3
4
  :aria-label="label || undefined"
4
5
  :class="otpInputFieldClasses"
5
6
  :style="otpInputFieldStyles"
@@ -148,7 +149,7 @@
148
149
  </template>
149
150
 
150
151
  <script lang="ts" setup>
151
- import { computed, nextTick, ref, StyleValue, useAttrs, useSlots, watch } from "vue";
152
+ import { computed, nextTick, onBeforeUnmount, ref, StyleValue, useAttrs, useSlots, watch } from "vue";
152
153
  import OrigamField from "../Field/OrigamField.vue";
153
154
  import OrigamOverlay from "../Overlay/OrigamOverlay.vue";
154
155
  import OrigamProgress from "../Progress/OrigamProgress.vue";
@@ -201,6 +202,8 @@ const props = defineProps({
201
202
  prependInnerIcon: { type: null, required: false },
202
203
  clearIcon: { type: null, required: false },
203
204
  clearable: { type: Boolean, required: false },
205
+ prependInnerAriaLabel: { type: String, required: false },
206
+ appendInnerAriaLabel: { type: String, required: false },
204
207
  border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
205
208
  borderTop: { type: [Boolean, Number, String], required: false },
206
209
  borderLeft: { type: [Boolean, Number, String], required: false },
@@ -261,14 +264,26 @@ const props = defineProps({
261
264
  readonly: { type: Boolean, required: false },
262
265
  rules: { type: Array, required: false },
263
266
  validateOn: { type: null, required: false },
264
- validationValue: { type: null, required: false },
265
- appendAvatar: { type: String, required: false },
266
- appendIcon: { type: null, required: false },
267
- prependAvatar: { type: String, required: false },
268
- prependIcon: { type: null, required: false }
267
+ validationValue: { type: null, required: false }
269
268
  });
270
269
  const emits = defineEmits(["finish", "click:control", "mousedown:control", "update:modelValue", "update:focused", "click:appendInner", "click:prependInner", "click:clear"]);
271
270
  defineSlots();
271
+ let disposed = false;
272
+ const frames = /* @__PURE__ */ new Set();
273
+ const scheduleFrame = (cb) => {
274
+ if (disposed) return;
275
+ const id2 = requestAnimationFrame(() => {
276
+ frames.delete(id2);
277
+ if (disposed) return;
278
+ cb();
279
+ });
280
+ frames.add(id2);
281
+ };
282
+ onBeforeUnmount(() => {
283
+ disposed = true;
284
+ for (const id2 of frames) cancelAnimationFrame(id2);
285
+ frames.clear();
286
+ });
272
287
  const { filterProps } = useProps(props);
273
288
  const { t } = useLocale();
274
289
  const attrs = useAttrs();
@@ -376,12 +391,12 @@ const handleKeydown = (e) => {
376
391
  if (focusIndex.value > 0 && e.key === "Backspace") {
377
392
  target = "prev";
378
393
  } else {
379
- requestAnimationFrame(() => {
394
+ scheduleFrame(() => {
380
395
  inputRef.value[index]?.select();
381
396
  });
382
397
  }
383
398
  }
384
- requestAnimationFrame(() => {
399
+ scheduleFrame(() => {
385
400
  if (target != null) {
386
401
  focusChild(contentRef.value, target);
387
402
  }
@@ -454,7 +469,7 @@ const otpInputFieldClasses = computed(() => {
454
469
  props.class
455
470
  ];
456
471
  });
457
- const { id: styleId, css, load, isLoaded, unload } = useStyle(otpInputFieldStyles);
472
+ const { id: styleId, css, load, isLoaded, unload } = useStyle(otpInputFieldStyles, () => props.id);
458
473
  defineExpose(forwardRefs({
459
474
  blur: () => {
460
475
  inputRef.value?.forEach((input) => input.blur());
@@ -493,8 +508,17 @@ defineExpose(forwardRefs({
493
508
 
494
509
  .origam-field {
495
510
  height: 100%;
496
- --origam-field---padding-start: 0;
497
- --origam-field---padding-end: 0;
511
+ // ⛔ #800 — une cellule OTP n'a ni etiquette ni texte aligne au bord :
512
+ // son chiffre est CENTRE. Le plancher de degagement des coins d'
513
+ // `OrigamField` n'y protege de rien et decentre le chiffre de 2px
514
+ // (mesure Chromium : `padding-inline` 4px / 0px). On le neutralise
515
+ // explicitement. Jusqu'a #800 le meme rendu etait obtenu par un `0`
516
+ // SANS UNITE qui rendait la declaration invalide — le bon rendu
517
+ // reposait donc sur une erreur de parse, et serait tombe au premier
518
+ // refactor du `max()`.
519
+ --origam-field---corner-clearance: 0px;
520
+ --origam-field---padding-start: 0px;
521
+ --origam-field---padding-end: 0px;
498
522
  }
499
523
 
500
524
  &__divider {
@@ -389,6 +389,8 @@ const props = defineProps({
389
389
  prependInnerIcon: { type: null, required: false },
390
390
  clearIcon: { type: null, required: false },
391
391
  clearable: { type: Boolean, required: false },
392
+ prependInnerAriaLabel: { type: String, required: false },
393
+ appendInnerAriaLabel: { type: String, required: false },
392
394
  border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
393
395
  borderTop: { type: [Boolean, Number, String], required: false },
394
396
  borderLeft: { type: [Boolean, Number, String], required: false },
@@ -456,7 +458,9 @@ const props = defineProps({
456
458
  appendAvatar: { type: String, required: false },
457
459
  appendIcon: { type: null, required: false },
458
460
  prependAvatar: { type: String, required: false },
459
- prependIcon: { type: null, required: false }
461
+ prependIcon: { type: null, required: false },
462
+ prependAriaLabel: { type: String, required: false },
463
+ appendAriaLabel: { type: String, required: false }
460
464
  });
461
465
  const emits = defineEmits(["click:control", "mousedown:control", "update:strength", "update:focused", "click:appendInner", "click:prependInner", "click:clear", "update:modelValue", "click:append", "click:prepend"]);
462
466
  defineSlots();
@@ -182,6 +182,8 @@ const props = defineProps({
182
182
  appendIcon: { type: null, required: false },
183
183
  prependAvatar: { type: String, required: false },
184
184
  prependIcon: { type: null, required: false },
185
+ prependAriaLabel: { type: String, required: false },
186
+ appendAriaLabel: { type: String, required: false },
185
187
  size: { type: null, required: false },
186
188
  fontSize: { type: null, required: false },
187
189
  fontWeight: { type: null, required: false },
@@ -13,16 +13,21 @@
13
13
  v-bind="{id, messagesId, isDisabled, isReadonly, isValid}"
14
14
  >
15
15
 
16
- <slot
17
- name="label"
18
- v-bind="{label, required}"
16
+ <div
17
+ :id="groupLabelId"
18
+ class="origam-radio-group__label"
19
19
  >
20
- <origam-label
21
- :id="id"
22
- :required="required"
23
- :text="label"
24
- />
25
- </slot>
20
+ <slot
21
+ name="label"
22
+ v-bind="{label, required}"
23
+ >
24
+ <origam-label
25
+ :required="required"
26
+ :text="label"
27
+ tag="span"
28
+ />
29
+ </slot>
30
+ </div>
26
31
 
27
32
  <origam-defaults-provider :defaults="radioDefaults">
28
33
  <origam-selection-control-group
@@ -30,7 +35,7 @@
30
35
  ref="origamSelectionControlGroupRef"
31
36
  v-model="model"
32
37
  :aria-describedby="messagesId"
33
- :aria-labelledby="label ? id : undefined"
38
+ :aria-labelledby="groupLabelledBy"
34
39
  :disabled="isDisabled"
35
40
  :items="items"
36
41
  :multiple="false"
@@ -141,6 +146,8 @@ const props = defineProps({
141
146
  appendIcon: { type: null, required: false },
142
147
  prependAvatar: { type: String, required: false },
143
148
  prependIcon: { type: null, required: false },
149
+ prependAriaLabel: { type: String, required: false },
150
+ appendAriaLabel: { type: String, required: false },
144
151
  size: { type: null, required: false },
145
152
  fontSize: { type: null, required: false },
146
153
  fontWeight: { type: null, required: false },
@@ -161,7 +168,7 @@ const props = defineProps({
161
168
  activeClass: { type: String, required: false },
162
169
  items: { type: [Array, Object], required: false }
163
170
  });
164
- defineSlots();
171
+ const slots = defineSlots();
165
172
  defineEmits(["update:modelValue"]);
166
173
  const { filterProps } = useProps(props);
167
174
  const origamSelectionControlGroupRef = ref();
@@ -172,6 +179,10 @@ const id = computed(() => {
172
179
  return props.id || `radio-group-${uid}`;
173
180
  });
174
181
  const model = useVModel(props, "modelValue");
182
+ const groupLabelId = computed(() => `${id.value}-label`);
183
+ const groupLabelledBy = computed(() => {
184
+ return props.label || slots.label ? groupLabelId.value : void 0;
185
+ });
175
186
  const [rootAttrs, controlAttrs] = filterInputAttrs(attrs);
176
187
  const inputProps = computed(() => {
177
188
  return origamInputRef.value?.filterProps(props, ["modelValue", "id", "focused", "style", "class"]);
@@ -213,3 +224,17 @@ defineExpose({
213
224
  styleId
214
225
  });
215
226
  </script>
227
+
228
+ <style lang="scss" scoped>
229
+ /*
230
+ * Le wrapper n'existe que pour porter l'id cible d'`aria-labelledby`
231
+ * (#814). `display: contents` le retire de l'arbre des boites : les
232
+ * enfants du slot `label` restent les elements flex directs de
233
+ * `.origam-input__control`, donc la mise en page est inchangee — mesure
234
+ * avant/apres identique au pixel sur les variantes « Default » et
235
+ * « Slots - Label ».
236
+ */
237
+ .origam-radio-group__label {
238
+ display: contents;
239
+ }
240
+ </style>