origam 2.17.0 → 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 (213) hide show
  1. package/CHANGELOG.md +744 -1
  2. package/dist/src/assets/css/main.css +1 -1
  3. package/dist/src/assets/css/tokens/dark.css +125 -24
  4. package/dist/src/assets/css/tokens/light.css +49 -12
  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 +2846 -12
  9. package/dist/src/assets/scss/tokens/_light.scss +49 -12
  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 +43 -18
  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 +34 -14
  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 +83 -28
  51. package/dist/src/components/FileField/OrigamFileField.vue +20 -15
  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 +19 -3
  60. package/dist/src/components/Menu/OrigamMenu.vue +79 -9
  61. package/dist/src/components/NumberField/OrigamNumberField.vue +26 -16
  62. package/dist/src/components/OtpInputField/OrigamOtpInputField.vue +48 -24
  63. package/dist/src/components/PasswordField/OrigamPasswordField.vue +20 -15
  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 +57 -34
  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/Switch/OrigamSwitchTrack.vue +20 -0
  73. package/dist/src/components/Tabs/OrigamTab.vue +7 -4
  74. package/dist/src/components/Tabs/OrigamTabPanel.vue +4 -2
  75. package/dist/src/components/Tabs/OrigamTabs.vue +7 -5
  76. package/dist/src/components/TextField/OrigamTextField.vue +20 -15
  77. package/dist/src/components/TextareaField/OrigamTextareaField.vue +20 -15
  78. package/dist/src/components/Transition/OrigamExpandX.vue +19 -3
  79. package/dist/src/components/Transition/OrigamExpandY.vue +19 -3
  80. package/dist/src/components/Transition/OrigamTranslateScale.vue +26 -4
  81. package/dist/src/components/Video/OrigamVideo.vue +3 -2
  82. package/dist/src/components/VirtualScroll/OrigamVirtualScroll.vue +1 -1
  83. package/dist/src/components/Window/OrigamWindow.vue +20 -3
  84. package/dist/src/composables/Commons/accessibleCommand.composable.cjs +26 -0
  85. package/dist/src/composables/Commons/accessibleCommand.composable.d.ts +78 -0
  86. package/dist/src/composables/Commons/accessibleCommand.composable.js +20 -0
  87. package/dist/src/composables/Commons/activator.composable.cjs +10 -1
  88. package/dist/src/composables/Commons/activator.composable.js +11 -2
  89. package/dist/src/composables/Commons/adjacent.composable.cjs +17 -0
  90. package/dist/src/composables/Commons/adjacent.composable.d.ts +12 -0
  91. package/dist/src/composables/Commons/adjacent.composable.js +17 -0
  92. package/dist/src/composables/Commons/adjacentInner.composable.cjs +20 -0
  93. package/dist/src/composables/Commons/adjacentInner.composable.d.ts +24 -5
  94. package/dist/src/composables/Commons/adjacentInner.composable.js +20 -0
  95. package/dist/src/composables/Commons/audio.composable.cjs +14 -1
  96. package/dist/src/composables/Commons/audio.composable.js +14 -1
  97. package/dist/src/composables/Commons/backButton.composable.cjs +3 -1
  98. package/dist/src/composables/Commons/backButton.composable.js +3 -1
  99. package/dist/src/composables/Commons/border.composable.d.ts +13 -0
  100. package/dist/src/composables/Commons/colorEffect.composable.cjs +58 -46
  101. package/dist/src/composables/Commons/colorEffect.composable.d.ts +10 -5
  102. package/dist/src/composables/Commons/colorEffect.composable.js +70 -46
  103. package/dist/src/composables/Commons/createLayout.composable.cjs +21 -12
  104. package/dist/src/composables/Commons/createLayout.composable.js +21 -12
  105. package/dist/src/composables/Commons/delay.composable.d.ts +16 -0
  106. package/dist/src/composables/Commons/dimension.composable.d.ts +11 -0
  107. package/dist/src/composables/Commons/elevation.composable.cjs +6 -2
  108. package/dist/src/composables/Commons/elevation.composable.d.ts +37 -1
  109. package/dist/src/composables/Commons/elevation.composable.js +7 -2
  110. package/dist/src/composables/Commons/goTo.composable.cjs +4 -2
  111. package/dist/src/composables/Commons/goTo.composable.js +5 -3
  112. package/dist/src/composables/Commons/group.composable.cjs +14 -14
  113. package/dist/src/composables/Commons/group.composable.js +14 -14
  114. package/dist/src/composables/Commons/hotkey.composable.d.ts +18 -7
  115. package/dist/src/composables/Commons/hydration.composable.d.ts +8 -0
  116. package/dist/src/composables/Commons/link.composable.d.ts +20 -0
  117. package/dist/src/composables/Commons/locationStrategies.composable.d.ts +17 -0
  118. package/dist/src/composables/Commons/message.composable.d.ts +19 -5
  119. package/dist/src/composables/Commons/position.composable.d.ts +17 -6
  120. package/dist/src/composables/Commons/refs.composable.d.ts +11 -5
  121. package/dist/src/composables/Commons/rounded.composable.d.ts +11 -1
  122. package/dist/src/composables/Commons/scrolling.composable.cjs +15 -12
  123. package/dist/src/composables/Commons/scrolling.composable.js +15 -12
  124. package/dist/src/composables/Commons/size.composable.d.ts +15 -0
  125. package/dist/src/composables/Commons/ssrBoot.composable.cjs +9 -1
  126. package/dist/src/composables/Commons/ssrBoot.composable.js +10 -2
  127. package/dist/src/composables/Commons/stack.composable.cjs +13 -1
  128. package/dist/src/composables/Commons/stack.composable.js +13 -1
  129. package/dist/src/composables/Commons/theme-props-resolver.composable.cjs +41 -32
  130. package/dist/src/composables/Commons/theme-props-resolver.composable.js +41 -32
  131. package/dist/src/composables/Commons/validation.composable.cjs +3 -3
  132. package/dist/src/composables/Commons/validation.composable.js +3 -3
  133. package/dist/src/composables/Commons/virtual.composable.cjs +24 -8
  134. package/dist/src/composables/Commons/virtual.composable.js +24 -8
  135. package/dist/src/composables/Masonry/masonry.composable.cjs +18 -11
  136. package/dist/src/composables/Masonry/masonry.composable.js +18 -11
  137. package/dist/src/composables/Parallax/parallax.composable.cjs +1 -1
  138. package/dist/src/composables/Parallax/parallax.composable.js +1 -1
  139. package/dist/src/composables/index.cjs +36 -0
  140. package/dist/src/composables/index.d.ts +3 -0
  141. package/dist/src/composables/index.js +3 -0
  142. package/dist/src/consts/Chart/chart-cartesian.const.cjs +5 -2
  143. package/dist/src/consts/Chart/chart-cartesian.const.d.ts +42 -0
  144. package/dist/src/consts/Chart/chart-cartesian.const.js +3 -0
  145. package/dist/src/consts/Commons/border.const.cjs +2 -1
  146. package/dist/src/consts/Commons/border.const.d.ts +19 -0
  147. package/dist/src/consts/Commons/border.const.js +15 -0
  148. package/dist/src/consts/Commons/elevation.const.cjs +5 -1
  149. package/dist/src/consts/Commons/elevation.const.d.ts +52 -2
  150. package/dist/src/consts/Commons/elevation.const.js +4 -0
  151. package/dist/src/consts/Commons/ripple.const.cjs +13 -3
  152. package/dist/src/consts/Commons/ripple.const.js +13 -3
  153. package/dist/src/consts/RatingField/rating-field.const.cjs +10 -0
  154. package/dist/src/consts/RatingField/rating-field.const.d.ts +36 -0
  155. package/dist/src/consts/RatingField/rating-field.const.js +12 -0
  156. package/dist/src/consts/index.cjs +11 -0
  157. package/dist/src/consts/index.d.ts +1 -0
  158. package/dist/src/consts/index.js +1 -0
  159. package/dist/src/directives/ClickOutside/clickOutside.directive.cjs +2 -0
  160. package/dist/src/directives/ClickOutside/clickOutside.directive.js +2 -0
  161. package/dist/src/directives/Ripple/ripple.directive.cjs +2 -0
  162. package/dist/src/directives/Ripple/ripple.directive.js +2 -0
  163. package/dist/src/enums/Commons/color.enum.cjs +0 -1
  164. package/dist/src/enums/Commons/color.enum.d.ts +14 -5
  165. package/dist/src/enums/Commons/color.enum.js +0 -1
  166. package/dist/src/enums/Sheet/sheet.enum.d.ts +1 -1
  167. package/dist/src/interfaces/Chart/chart-cartesian.interface.d.ts +6 -2
  168. package/dist/src/interfaces/Commons/activator.interface.d.ts +3 -2
  169. package/dist/src/interfaces/Commons/adjacent.interface.d.ts +29 -0
  170. package/dist/src/interfaces/Commons/group.interface.d.ts +13 -2
  171. package/dist/src/interfaces/Commons/ripple.interface.d.ts +16 -0
  172. package/dist/src/interfaces/Commons/scroll.interface.d.ts +3 -2
  173. package/dist/src/interfaces/DataTable/data-table-header.interface.d.ts +2 -2
  174. package/dist/src/interfaces/DatePicker/date-picker-month.interface.d.ts +2 -1
  175. package/dist/src/interfaces/Field/field.interface.d.ts +21 -1
  176. package/dist/src/interfaces/Menu/menu.interface.d.ts +28 -1
  177. package/dist/src/interfaces/OtpInputField/otp-input-field.interface.d.ts +21 -1
  178. package/dist/src/interfaces/RatingField/rating-field-item.interface.d.ts +16 -1
  179. package/dist/src/interfaces/index.cjs +11 -0
  180. package/dist/src/interfaces/index.d.ts +1 -0
  181. package/dist/src/interfaces/index.js +1 -0
  182. package/dist/src/themes/origam.theme.cjs +8 -14
  183. package/dist/src/themes/origam.theme.js +6 -8
  184. package/dist/src/types/Commons/color.type.d.ts +4 -3
  185. package/dist/src/types/Commons/commons.type.d.ts +33 -0
  186. package/dist/src/types/Commons/theme.type.d.ts +2 -1
  187. package/dist/src/types/Sheet/sheet.type.d.ts +2 -1
  188. package/dist/src/types/index.cjs +22 -0
  189. package/dist/src/types/index.d.ts +2 -0
  190. package/dist/src/types/index.js +2 -0
  191. package/dist/src/types/tokens.type.d.ts +1 -1
  192. package/dist/src/utils/Commons/a11y.util.cjs +35 -0
  193. package/dist/src/utils/Commons/a11y.util.d.ts +63 -0
  194. package/dist/src/utils/Commons/a11y.util.js +33 -0
  195. package/dist/src/utils/Commons/clickOutside.util.cjs +5 -1
  196. package/dist/src/utils/Commons/clickOutside.util.js +5 -1
  197. package/dist/src/utils/Commons/color.util.cjs +10 -5
  198. package/dist/src/utils/Commons/color.util.d.ts +34 -5
  199. package/dist/src/utils/Commons/color.util.js +9 -5
  200. package/dist/src/utils/Commons/goTo.util.cjs +31 -14
  201. package/dist/src/utils/Commons/goTo.util.d.ts +1 -1
  202. package/dist/src/utils/Commons/goTo.util.js +31 -16
  203. package/dist/src/utils/Commons/location.util.cjs +18 -3
  204. package/dist/src/utils/Commons/location.util.js +18 -3
  205. package/dist/src/utils/Commons/ripple.util.cjs +5 -1
  206. package/dist/src/utils/Commons/ripple.util.js +5 -1
  207. package/dist/src/utils/index.cjs +11 -0
  208. package/dist/src/utils/index.d.ts +1 -0
  209. package/dist/src/utils/index.js +1 -0
  210. package/package.json +13 -2
  211. package/dist/src/types/Commons/locale.type.cjs +0 -1
  212. package/dist/src/types/Commons/locale.type.d.ts +0 -0
  213. package/dist/src/types/Commons/locale.type.js +0 -0
@@ -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,21 @@ 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 },
207
+ border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
208
+ borderTop: { type: [Boolean, Number, String], required: false },
209
+ borderLeft: { type: [Boolean, Number, String], required: false },
210
+ borderBottom: { type: [Boolean, Number, String], required: false },
211
+ borderRight: { type: [Boolean, Number, String], required: false },
212
+ borderBlock: { type: [Boolean, Number, String], required: false },
213
+ borderInline: { type: [Boolean, Number, String], required: false },
214
+ borderColor: { type: String, required: false },
215
+ borderStyle: { type: String, required: false },
216
+ borderTopColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
217
+ borderRightColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
218
+ borderBottomColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
219
+ borderLeftColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
204
220
  focused: { type: Boolean, required: false },
205
221
  density: { type: null, required: false },
206
222
  text: { type: String, required: false },
@@ -220,19 +236,6 @@ const props = defineProps({
220
236
  paddingRight: { type: [Boolean, Number, String], required: false },
221
237
  paddingBlock: { type: [Boolean, Number, String], required: false },
222
238
  paddingInline: { type: [Boolean, Number, String], required: false },
223
- border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
224
- borderTop: { type: [Boolean, Number, String], required: false },
225
- borderLeft: { type: [Boolean, Number, String], required: false },
226
- borderBottom: { type: [Boolean, Number, String], required: false },
227
- borderRight: { type: [Boolean, Number, String], required: false },
228
- borderBlock: { type: [Boolean, Number, String], required: false },
229
- borderInline: { type: [Boolean, Number, String], required: false },
230
- borderColor: { type: String, required: false },
231
- borderStyle: { type: String, required: false },
232
- borderTopColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
233
- borderRightColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
234
- borderBottomColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
235
- borderLeftColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
236
239
  rounded: { type: [Boolean, Number, String, null], required: false, skipCheck: true },
237
240
  roundedTopRight: { type: [Boolean, Number, String], required: false },
238
241
  roundedTopLeft: { 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 {
@@ -318,6 +318,7 @@ import { useLocale } from "../../composables/Commons/locale.composable";
318
318
  import { useProps } from "../../composables/Commons/props.composable";
319
319
  import { useStyle } from "../../composables/Commons/style.composable";
320
320
  import { useVModel } from "../../composables/Commons/vModel.composable";
321
+ import { BORDER_PROP_KEYS } from "../../consts/Commons/border.const";
321
322
  import { DEFAULT_PASSWORD_REQUIREMENTS, REQUIREMENT_MIN_LENGTH, REQUIREMENT_NUMBER, REQUIREMENT_SPECIAL, REQUIREMENT_TINY, REQUIREMENT_UPPERCASE } from "../../consts/PasswordField/password-field.const";
322
323
  import vIntersect from "../../directives/Intersect/intersect.directive";
323
324
  import { DENSITY } from "../../enums/Commons/density.enum";
@@ -388,6 +389,21 @@ const props = defineProps({
388
389
  prependInnerIcon: { type: null, required: false },
389
390
  clearIcon: { type: null, required: false },
390
391
  clearable: { type: Boolean, required: false },
392
+ prependInnerAriaLabel: { type: String, required: false },
393
+ appendInnerAriaLabel: { type: String, required: false },
394
+ border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
395
+ borderTop: { type: [Boolean, Number, String], required: false },
396
+ borderLeft: { type: [Boolean, Number, String], required: false },
397
+ borderBottom: { type: [Boolean, Number, String], required: false },
398
+ borderRight: { type: [Boolean, Number, String], required: false },
399
+ borderBlock: { type: [Boolean, Number, String], required: false },
400
+ borderInline: { type: [Boolean, Number, String], required: false },
401
+ borderColor: { type: String, required: false },
402
+ borderStyle: { type: String, required: false },
403
+ borderTopColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
404
+ borderRightColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
405
+ borderBottomColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
406
+ borderLeftColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
391
407
  focused: { type: Boolean, required: false },
392
408
  text: { type: String, required: false },
393
409
  floating: { type: Boolean, required: false },
@@ -406,19 +422,6 @@ const props = defineProps({
406
422
  paddingRight: { type: [Boolean, Number, String], required: false },
407
423
  paddingBlock: { type: [Boolean, Number, String], required: false },
408
424
  paddingInline: { type: [Boolean, Number, String], required: false },
409
- border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
410
- borderTop: { type: [Boolean, Number, String], required: false },
411
- borderLeft: { type: [Boolean, Number, String], required: false },
412
- borderBottom: { type: [Boolean, Number, String], required: false },
413
- borderRight: { type: [Boolean, Number, String], required: false },
414
- borderBlock: { type: [Boolean, Number, String], required: false },
415
- borderInline: { type: [Boolean, Number, String], required: false },
416
- borderColor: { type: String, required: false },
417
- borderStyle: { type: String, required: false },
418
- borderTopColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
419
- borderRightColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
420
- borderBottomColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
421
- borderLeftColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
422
425
  rounded: { type: [Boolean, Number, String, null], required: false, skipCheck: true, default: true },
423
426
  roundedTopRight: { type: [Boolean, Number, String], required: false },
424
427
  roundedTopLeft: { type: [Boolean, Number, String], required: false },
@@ -455,7 +458,9 @@ const props = defineProps({
455
458
  appendAvatar: { type: String, required: false },
456
459
  appendIcon: { type: null, required: false },
457
460
  prependAvatar: { type: String, required: false },
458
- prependIcon: { type: null, required: false }
461
+ prependIcon: { type: null, required: false },
462
+ prependAriaLabel: { type: String, required: false },
463
+ appendAriaLabel: { type: String, required: false }
459
464
  });
460
465
  const emits = defineEmits(["click:control", "mousedown:control", "update:strength", "update:focused", "click:appendInner", "click:prependInner", "click:clear", "update:modelValue", "click:append", "click:prepend"]);
461
466
  defineSlots();
@@ -615,7 +620,7 @@ const enrichedRules = computed(() => {
615
620
  });
616
621
  const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
617
622
  const inputProps = computed(() => {
618
- return origamInputRef.value?.filterProps(props, ["modelValue", "class", "style", "focused"]);
623
+ return origamInputRef.value?.filterProps(props, ["modelValue", "class", "style", "focused", ...BORDER_PROP_KEYS]);
619
624
  });
620
625
  const fieldProps = computed(() => {
621
626
  return origamFieldRef.value?.filterProps(props, ["class", "id", "active", "dirty", "disabled", "focused", "error", "style"]);
@@ -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>
@@ -3,8 +3,11 @@
3
3
  :id="id"
4
4
  ref="origamInputRef"
5
5
  v-model="model"
6
+ :aria-labelledby="groupLabelledBy"
7
+ :aria-readonly="readonly || undefined"
6
8
  :class="ratingFieldClasses"
7
9
  :style="ratingFieldStyles"
10
+ role="radiogroup"
8
11
  v-bind="{...rootAttrs, ...inputProps}"
9
12
  >
10
13
  <template
@@ -19,12 +22,15 @@
19
22
  name="default"
20
23
  v-bind="{id,messagesId,isDisabled,isReadonly,isValid}"
21
24
  >
22
- <div class="origam-rating-field__label">
25
+ <div
26
+ :id="groupLabelId"
27
+ class="origam-rating-field__label"
28
+ >
23
29
  <slot name="label">
24
30
  <origam-label
25
- :for="id"
26
31
  :required="required"
27
32
  :text="label"
33
+ tag="span"
28
34
  />
29
35
  </slot>
30
36
  </div>
@@ -158,7 +164,7 @@
158
164
  </template>
159
165
 
160
166
  <script lang="ts" setup>
161
- import { computed, ref, shallowRef, StyleValue, useAttrs, useSlots } from "vue";
167
+ import { computed, nextTick, ref, shallowRef, StyleValue, useAttrs, useSlots } from "vue";
162
168
  import OrigamBtn from "../Btn/OrigamBtn.vue";
163
169
  import OrigamInput from "../Input/OrigamInput.vue";
164
170
  import OrigamLabel from "../Label/OrigamLabel.vue";
@@ -167,8 +173,14 @@ import { useLocale } from "../../composables/Commons/locale.composable";
167
173
  import { useProps } from "../../composables/Commons/props.composable";
168
174
  import { useStyle } from "../../composables/Commons/style.composable";
169
175
  import { useVModel } from "../../composables/Commons/vModel.composable";
176
+ import {
177
+ RATING_FIELD_ENABLED_RADIO_SELECTOR,
178
+ RATING_FIELD_MUTATING_KEYS,
179
+ RATING_FIELD_ROOT_SELECTOR
180
+ } from "../../consts/RatingField/rating-field.const";
170
181
  import { BLOCK } from "../../enums/Commons/anchor.enum";
171
182
  import { DENSITY } from "../../enums/Commons/density.enum";
183
+ import { KEYBOARD_VALUES } from "../../enums/Commons/hotkey.enum";
172
184
  import { MDI_ICONS } from "../../enums/Commons/mdi.enum";
173
185
  import { SIZES } from "../../enums/Commons/size.enum";
174
186
  import { VARIANT } from "../../enums/Commons/variant.enum";
@@ -253,6 +265,8 @@ const props = defineProps({
253
265
  appendIcon: { type: null, required: false },
254
266
  prependAvatar: { type: String, required: false },
255
267
  prependIcon: { type: null, required: false },
268
+ prependAriaLabel: { type: String, required: false },
269
+ appendAriaLabel: { type: String, required: false },
256
270
  size: { type: null, required: false, default: SIZES.DEFAULT },
257
271
  fontSize: { type: null, required: false },
258
272
  fontWeight: { type: null, required: false },
@@ -286,6 +300,21 @@ const name = computed(() => {
286
300
  return props.name ?? `origam-rating-${getUid()}`;
287
301
  });
288
302
  const hoverIndex = shallowRef(-1);
303
+ const handleKeydown = (e) => {
304
+ if (props.disabled || props.readonly) {
305
+ if (RATING_FIELD_MUTATING_KEYS.includes(e.key)) e.preventDefault();
306
+ return;
307
+ }
308
+ if (e.key !== KEYBOARD_VALUES.HOME && e.key !== KEYBOARD_VALUES.END) return;
309
+ const group = e.target?.closest(RATING_FIELD_ROOT_SELECTOR);
310
+ if (!group) return;
311
+ const radios = Array.from(group.querySelectorAll(RATING_FIELD_ENABLED_RADIO_SELECTOR));
312
+ if (!radios.length) return;
313
+ e.preventDefault();
314
+ const target = e.key === KEYBOARD_VALUES.HOME ? radios[0] : radios[radios.length - 1];
315
+ target.focus();
316
+ if (!target.checked) target.click();
317
+ };
289
318
  const itemState = computed(() => {
290
319
  return increments.value.map((value) => {
291
320
  const isFilled = normalizedValue.value >= value;
@@ -303,20 +332,38 @@ const eventState = computed(() => {
303
332
  const onMouseleave = () => {
304
333
  hoverIndex.value = -1;
305
334
  };
306
- const onClick = () => {
307
- if (props.disabled || props.readonly) return;
308
- model.value = normalizedValue.value === value && props.clearable ? 0 : value;
335
+ const onClick = (e) => {
336
+ if (!props.disabled && !props.readonly) {
337
+ model.value = normalizedValue.value === value && props.clearable ? 0 : value;
338
+ }
339
+ void nextTick(() => resyncRadios(e?.target));
340
+ };
341
+ const onChange = (e) => {
342
+ if (!props.disabled && !props.readonly) {
343
+ model.value = value;
344
+ }
345
+ void nextTick(() => resyncRadios(e?.target));
309
346
  };
310
347
  return {
311
348
  onMouseenter: props.hover ? onMouseenter : void 0,
312
349
  onMouseleave: props.hover ? onMouseleave : void 0,
313
- onClick
350
+ onClick,
351
+ onChange,
352
+ onKeydown: handleKeydown
314
353
  };
315
354
  });
316
355
  });
317
356
  const isChecked = (value) => {
318
357
  return normalizedValue.value === value;
319
358
  };
359
+ const resyncRadios = (source) => {
360
+ const group = source?.closest(RATING_FIELD_ROOT_SELECTOR);
361
+ if (!group) return;
362
+ group.querySelectorAll('input[type="radio"]').forEach((radio) => {
363
+ const shouldBeChecked = isChecked(parseFloat(radio.value));
364
+ if (radio.checked !== shouldBeChecked) radio.checked = shouldBeChecked;
365
+ });
366
+ };
320
367
  const hasLabels = computed(() => {
321
368
  return !!props.itemLabels?.length || slots.itemLabel;
322
369
  });
@@ -346,6 +393,10 @@ const ratingFieldClasses = computed(() => {
346
393
  ];
347
394
  });
348
395
  const { id, css, load, isLoaded, unload } = useStyle(ratingFieldStyles, () => props.id);
396
+ const groupLabelId = computed(() => `${id.value}-label`);
397
+ const groupLabelledBy = computed(() => {
398
+ return props.label || slots.label ? groupLabelId.value : void 0;
399
+ });
349
400
  defineExpose({
350
401
  filterProps,
351
402
  css,
@@ -4,37 +4,39 @@
4
4
  :class="ratingFieldItemClasses"
5
5
  :style="ratingFieldItemStyles"
6
6
  >
7
- <label
8
- :for="id"
9
- class="origam-rating-field-item__label"
10
- >
11
- <span class="origam-rating-field-item__hidden">{{ t(itemAriaLabel, value, length) }}</span>
12
- <slot
13
- v-if="showStar"
14
- name="item"
15
- v-bind="{props: ratingBtnProps, value}"
7
+ <template v-if="showStar">
8
+ <label
9
+ :for="id"
10
+ class="origam-rating-field-item__label"
16
11
  >
17
- <origam-btn
18
- ref="origamBtnRef"
19
- v-bind="{...ratingBtnProps}"
20
- @click="handleClick"
21
- @mouseenter="handleMouseEnter"
22
- @mouseleave="handleMouseLeave"
23
- />
24
- </slot>
25
- </label>
12
+ <span class="origam-rating-field-item__hidden">{{ t(itemAriaLabel, value, length) }}</span>
13
+ <slot
14
+ name="item"
15
+ v-bind="{props: ratingBtnProps, value}"
16
+ >
17
+ <origam-btn
18
+ ref="origamBtnRef"
19
+ v-bind="{...ratingBtnProps}"
20
+ @click="handleClick"
21
+ @mouseenter="handleMouseEnter"
22
+ @mouseleave="handleMouseLeave"
23
+ />
24
+ </slot>
25
+ </label>
26
26
 
27
- <input
28
- :id="id"
29
- :checked="checked"
30
- :disabled="disabled"
31
- :name="name"
32
- :readonly="readonly"
33
- :value="value"
34
- class="origam-rating-field-item__hidden"
35
- tabindex="-1"
36
- type="radio"
37
- />
27
+ <input
28
+ :id="id"
29
+ :checked="checked"
30
+ :disabled="disabled"
31
+ :name="name"
32
+ :readonly="readonly"
33
+ :value="value"
34
+ class="origam-rating-field-item__hidden"
35
+ type="radio"
36
+ @change="handleChange"
37
+ @keydown="handleKeydown"
38
+ />
39
+ </template>
38
40
  </component>
39
41
  </template>
40
42
 
@@ -105,7 +107,7 @@ const props = defineProps({
105
107
  roundedBottomRight: { type: [Boolean, Number, String], required: false },
106
108
  elevation: { type: [Number, String], required: false }
107
109
  });
108
- const emits = defineEmits(["mouseenter", "mouseleave", "click"]);
110
+ const emits = defineEmits(["mouseenter", "mouseleave", "change", "keydown", "click"]);
109
111
  defineSlots();
110
112
  const { filterProps } = useProps(props);
111
113
  const { t } = useLocale();
@@ -128,6 +130,12 @@ const handleMouseLeave = (e) => {
128
130
  const handleClick = (e) => {
129
131
  emits("click", e);
130
132
  };
133
+ const handleChange = (e) => {
134
+ emits("change", e);
135
+ };
136
+ const handleKeydown = (e) => {
137
+ emits("keydown", e);
138
+ };
131
139
  const ratingFieldItemStyles = computed(() => {
132
140
  return [
133
141
  props.style
@@ -157,6 +165,30 @@ defineExpose({
157
165
 
158
166
  <style lang="scss" scoped>
159
167
  .origam-rating-field-item {
168
+ /*
169
+ * #812 — WCAG 2.4.7 (Focus Visible).
170
+ *
171
+ * The control a keyboard user focuses is the native
172
+ * `<input type="radio">`, and `&__hidden` below makes it
173
+ * `height:0; width:0; opacity:0`. Putting it back in the tab order
174
+ * without this rule would have swapped one defect for another: the
175
+ * group would become operable while the focus indicator landed on a
176
+ * 0x0 transparent element. That is the trade #810 refused to make and
177
+ * #812 names explicitly.
178
+ *
179
+ * `:has()` is the CSS-first answer — the ring is painted on the STAR,
180
+ * which is the box the user actually sees, while focus stays on the
181
+ * input that owns the radio semantics. No JS, no second focusable
182
+ * element, no `tabindex` bookkeeping.
183
+ *
184
+ * `:focus-visible` (not `:focus`) so a mouse click on a star does not
185
+ * leave a ring behind — the browser's own heuristic, not ours.
186
+ */
187
+ &:has(:focus-visible) {
188
+ outline: var(--origam-border__width---2, 2px) solid var(--origam-color__border---focus, currentColor);
189
+ outline-offset: var(--origam-space---1, 4px);
190
+ }
191
+
160
192
  &__label {
161
193
  cursor: pointer;
162
194
 
@@ -184,6 +216,22 @@ defineExpose({
184
216
  clip-path: polygon(0 0, 50% 0, 50% 100%, 0 100%);
185
217
  z-index: 1;
186
218
 
219
+ /*
220
+ * #812 — a `clip-path` clips the element's OWN outline too, not
221
+ * just its descendants. Measured: with the shared
222
+ * `outline-offset: 4px` above, focusing a half-step radio painted
223
+ * a ring entirely outside the polygon — computed `outline-width`
224
+ * read `2px` while the screenshot showed nothing at all. That is
225
+ * the exact shape of a false green: the property is set, the pixel
226
+ * is not there.
227
+ *
228
+ * An INSET offset puts the ring back inside the clipped region, so
229
+ * it survives and outlines precisely the half the step selects.
230
+ */
231
+ &:has(:focus-visible) {
232
+ outline-offset: calc(-1 * var(--origam-border__width---2, 2px));
233
+ }
234
+
187
235
  &,
188
236
  &:hover {
189
237
  :deep(.origam-btn__overlay) {