origam 2.17.1 → 2.18.1

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 (215) hide show
  1. package/CHANGELOG.md +895 -0
  2. package/dist/src/assets/css/main.css +1 -1
  3. package/dist/src/assets/css/tokens/dark.css +193 -26
  4. package/dist/src/assets/css/tokens/light.css +78 -11
  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 +2914 -13
  9. package/dist/src/assets/scss/tokens/_light.scss +78 -11
  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 +22 -16
  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/Btn/btn.interface.d.ts +7 -10
  163. package/dist/src/interfaces/Chart/chart-cartesian.interface.d.ts +6 -2
  164. package/dist/src/interfaces/Commons/activator.interface.d.ts +3 -2
  165. package/dist/src/interfaces/Commons/adjacent.interface.d.ts +29 -0
  166. package/dist/src/interfaces/Commons/group.interface.d.ts +13 -2
  167. package/dist/src/interfaces/Commons/ripple.interface.d.ts +16 -0
  168. package/dist/src/interfaces/Commons/scroll.interface.d.ts +3 -2
  169. package/dist/src/interfaces/DataTable/data-table-header.interface.d.ts +2 -2
  170. package/dist/src/interfaces/DatePicker/date-picker-month.interface.d.ts +2 -1
  171. package/dist/src/interfaces/Menu/menu.interface.d.ts +28 -1
  172. package/dist/src/interfaces/OtpInputField/otp-input-field.interface.d.ts +21 -1
  173. package/dist/src/interfaces/RatingField/rating-field-item.interface.d.ts +16 -1
  174. package/dist/src/interfaces/index.cjs +11 -0
  175. package/dist/src/interfaces/index.d.ts +1 -0
  176. package/dist/src/interfaces/index.js +1 -0
  177. package/dist/src/nuxt/module.cjs +2 -1
  178. package/dist/src/nuxt/module.js +2 -1
  179. package/dist/src/origam.cjs +1 -2
  180. package/dist/src/origam.js +1 -2
  181. package/dist/src/themes/origam.theme.cjs +8 -14
  182. package/dist/src/themes/origam.theme.js +6 -8
  183. package/dist/src/types/Commons/color.type.d.ts +4 -3
  184. package/dist/src/types/Commons/commons.type.d.ts +33 -0
  185. package/dist/src/types/Commons/theme.type.d.ts +2 -1
  186. package/dist/src/types/Sheet/sheet.type.d.ts +2 -1
  187. package/dist/src/types/index.cjs +22 -0
  188. package/dist/src/types/index.d.ts +2 -0
  189. package/dist/src/types/index.js +2 -0
  190. package/dist/src/types/tokens.type.d.ts +1 -1
  191. package/dist/src/utils/Commons/a11y.util.cjs +35 -0
  192. package/dist/src/utils/Commons/a11y.util.d.ts +63 -0
  193. package/dist/src/utils/Commons/a11y.util.js +33 -0
  194. package/dist/src/utils/Commons/clickOutside.util.cjs +5 -1
  195. package/dist/src/utils/Commons/clickOutside.util.js +5 -1
  196. package/dist/src/utils/Commons/color.util.cjs +10 -15
  197. package/dist/src/utils/Commons/color.util.d.ts +34 -28
  198. package/dist/src/utils/Commons/color.util.js +9 -16
  199. package/dist/src/utils/Commons/goTo.util.cjs +31 -14
  200. package/dist/src/utils/Commons/goTo.util.d.ts +1 -1
  201. package/dist/src/utils/Commons/goTo.util.js +31 -16
  202. package/dist/src/utils/Commons/location.util.cjs +18 -3
  203. package/dist/src/utils/Commons/location.util.js +18 -3
  204. package/dist/src/utils/Commons/ripple.util.cjs +5 -1
  205. package/dist/src/utils/Commons/ripple.util.js +5 -1
  206. package/dist/src/utils/index.cjs +11 -0
  207. package/dist/src/utils/index.d.ts +1 -0
  208. package/dist/src/utils/index.js +1 -0
  209. package/package.json +13 -2
  210. package/dist/src/consts/Btn/btn.const.cjs +0 -7
  211. package/dist/src/consts/Btn/btn.const.d.ts +0 -32
  212. package/dist/src/consts/Btn/btn.const.js +0 -1
  213. package/dist/src/types/Commons/locale.type.cjs +0 -1
  214. package/dist/src/types/Commons/locale.type.d.ts +0 -0
  215. package/dist/src/types/Commons/locale.type.js +0 -0
@@ -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) {
@@ -275,6 +275,7 @@ import {
275
275
  inject,
276
276
  mergeProps,
277
277
  nextTick,
278
+ onBeforeUnmount,
278
279
  onMounted,
279
280
  ref,
280
281
  shallowRef,
@@ -374,6 +375,8 @@ const props = defineProps({
374
375
  prependInnerIcon: { type: null, required: false },
375
376
  clearIcon: { type: null, required: false },
376
377
  clearable: { type: Boolean, required: false },
378
+ prependInnerAriaLabel: { type: String, required: false },
379
+ appendInnerAriaLabel: { type: String, required: false },
377
380
  border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true, default: true },
378
381
  borderTop: { type: [Boolean, Number, String], required: false },
379
382
  borderLeft: { type: [Boolean, Number, String], required: false },
@@ -442,6 +445,8 @@ const props = defineProps({
442
445
  appendIcon: { type: null, required: false },
443
446
  prependAvatar: { type: String, required: false },
444
447
  prependIcon: { type: null, required: false },
448
+ prependAriaLabel: { type: String, required: false },
449
+ appendAriaLabel: { type: String, required: false },
445
450
  items: { type: Array, required: false, default: () => [] },
446
451
  itemTitle: { type: null, required: false, default: "title" },
447
452
  itemValue: { type: null, required: false, default: "value" },
@@ -592,6 +597,32 @@ const {
592
597
  onListScroll: handleListScroll,
593
598
  onListKeydown: handleListKeydown
594
599
  } = useScrolling(origamListRef, origamTextFieldRef);
600
+ const scrollFrames = /* @__PURE__ */ new Set();
601
+ let disposed = false;
602
+ const scheduleScrollFrame = (cb) => {
603
+ if (disposed || !IN_BROWSER) return;
604
+ const id2 = window.requestAnimationFrame(() => {
605
+ scrollFrames.delete(id2);
606
+ cb();
607
+ });
608
+ scrollFrames.add(id2);
609
+ };
610
+ const macrotasks = /* @__PURE__ */ new Set();
611
+ const scheduleMacrotask = (cb) => {
612
+ if (disposed || !IN_BROWSER) return;
613
+ const id2 = window.setTimeout(() => {
614
+ macrotasks.delete(id2);
615
+ cb();
616
+ }, 0);
617
+ macrotasks.add(id2);
618
+ };
619
+ onBeforeUnmount(() => {
620
+ disposed = true;
621
+ for (const id2 of scrollFrames) window.cancelAnimationFrame(id2);
622
+ for (const id2 of macrotasks) window.clearTimeout(id2);
623
+ scrollFrames.clear();
624
+ macrotasks.clear();
625
+ });
595
626
  const handleSelect = (item, set = true) => {
596
627
  if (item.props?.disabled) return;
597
628
  if (props.multiple) {
@@ -648,11 +679,11 @@ const handleMousedownControl = (e) => {
648
679
  if (menuDisabled.value) return;
649
680
  menu.value = !menu.value;
650
681
  if (props.autocomplete && !props.multiple && search.value) {
651
- setTimeout(() => {
682
+ scheduleMacrotask(() => {
652
683
  const root = vm?.proxy?.$el;
653
684
  const input = root?.querySelector("input");
654
685
  input?.select();
655
- }, 0);
686
+ });
656
687
  }
657
688
  };
658
689
  const handleMousedownMenuIcon = (e) => {
@@ -759,13 +790,11 @@ const handleKeydown = (e) => {
759
790
  if (item !== void 0) {
760
791
  model.value = [item];
761
792
  const index = displayItems.value.indexOf(item);
762
- if (IN_BROWSER) {
763
- window.requestAnimationFrame(() => {
764
- if (index >= 0) {
765
- origamVirtualScrollRef.value?.scrollToIndex(index);
766
- }
767
- });
768
- }
793
+ scheduleScrollFrame(() => {
794
+ if (index >= 0) {
795
+ origamVirtualScrollRef.value?.scrollToIndex(index);
796
+ }
797
+ });
769
798
  }
770
799
  }
771
800
  };
@@ -791,7 +820,7 @@ const handleAfterLeave = () => {
791
820
  };
792
821
  const handleFocusin = () => {
793
822
  isFocused.value = true;
794
- setTimeout(() => {
823
+ scheduleMacrotask(() => {
795
824
  listHasFocus.value = true;
796
825
  });
797
826
  };
@@ -880,13 +909,11 @@ watch(menu, () => {
880
909
  const index = displayItems.value.findIndex(
881
910
  (item) => model.value.some((s) => props.valueComparator ? props.valueComparator(s.value, item.value) : deepEqual(s.value, item.value))
882
911
  );
883
- if (IN_BROWSER) {
884
- window.requestAnimationFrame(() => {
885
- if (index >= 0) {
886
- origamVirtualScrollRef.value?.scrollToIndex(index);
887
- }
888
- });
889
- }
912
+ scheduleScrollFrame(() => {
913
+ if (index >= 0) {
914
+ origamVirtualScrollRef.value?.scrollToIndex(index);
915
+ }
916
+ });
890
917
  }
891
918
  });
892
919
  watch(() => props.items, (newVal, oldVal) => {
@@ -1134,10 +1161,6 @@ defineExpose(forwardRefs({
1134
1161
  max-width: none;
1135
1162
  }
1136
1163
 
1137
- .origam-select__content .origam-menu__list {
1138
- max-width: none;
1139
- }
1140
-
1141
1164
  .origam-select__content .origam-list-item {
1142
1165
  --origam-list-item---padding-inline-start: 32px;
1143
1166
  }
@@ -113,7 +113,7 @@
113
113
  <input
114
114
  :id="`${id}__start`"
115
115
  :aria-describedby="messagesId"
116
- :aria-label="label ? `${label} (start)` : undefined"
116
+ :aria-label="rangeStartAriaLabel"
117
117
  :class="nativeInputClasses(false)"
118
118
  :disabled="isDisabled"
119
119
  :max="resolvedMax"
@@ -139,7 +139,7 @@
139
139
  <input
140
140
  :id="`${id}__stop`"
141
141
  :aria-describedby="messagesId"
142
- :aria-label="label ? `${label} (end)` : undefined"
142
+ :aria-label="rangeEndAriaLabel"
143
143
  :class="nativeInputClasses(true)"
144
144
  :disabled="isDisabled"
145
145
  :max="resolvedMax"
@@ -327,7 +327,7 @@
327
327
  </template>
328
328
  <template v-else>
329
329
  <input
330
- :aria-label="label ? `${label} (start)` : undefined"
330
+ :aria-label="rangeStartAriaLabel"
331
331
  :class="nativeInputClasses(false)"
332
332
  :disabled="disabled"
333
333
  :max="resolvedMax"
@@ -351,7 +351,7 @@
351
351
  />
352
352
 
353
353
  <input
354
- :aria-label="label ? `${label} (end)` : undefined"
354
+ :aria-label="rangeEndAriaLabel"
355
355
  :class="nativeInputClasses(true)"
356
356
  :disabled="disabled"
357
357
  :max="resolvedMax"
@@ -416,6 +416,7 @@ import OrigamSliderFieldTrack from "./OrigamSliderFieldTrack.vue";
416
416
  import { OrigamAudioWaveform } from "../Audio";
417
417
  import { useBackgroundColor } from "../../composables/Commons/backgroundColor.composable";
418
418
  import { useFocus } from "../../composables/Commons/focus.composable";
419
+ import { useLocale } from "../../composables/Commons/locale.composable";
419
420
  import { useProps } from "../../composables/Commons/props.composable";
420
421
  import { useRounded } from "../../composables/Commons/rounded.composable";
421
422
  import { useRtl } from "../../composables/Commons/rtl.composable";
@@ -513,6 +514,8 @@ const props = defineProps({
513
514
  appendIcon: { type: null, required: false },
514
515
  prependAvatar: { type: String, required: false },
515
516
  prependIcon: { type: null, required: false },
517
+ prependAriaLabel: { type: String, required: false },
518
+ appendAriaLabel: { type: String, required: false },
516
519
  size: { type: null, required: false },
517
520
  fontSize: { type: null, required: false },
518
521
  fontWeight: { type: null, required: false },
@@ -524,6 +527,7 @@ defineSlots();
524
527
  const { filterProps } = useProps(props);
525
528
  const origamInputRef = ref();
526
529
  const slots = useSlots();
530
+ const { t } = useLocale();
527
531
  const isFieldVariant = computed(() => props.variant === SLIDER_FIELD_VARIANT.FIELD);
528
532
  const isAudioVariant = computed(() => props.variant === SLIDER_FIELD_VARIANT.AUDIO);
529
533
  const isBareVariant = computed(() => !isFieldVariant.value);
@@ -531,6 +535,12 @@ const isRange = computed(() => !!props.range);
531
535
  const isVertical = computed(() => props.direction === DIRECTION.VERTICAL);
532
536
  const isReversed = computed(() => !!props.reverse);
533
537
  const indexFromEnd = computed(() => isVertical.value !== isReversed.value);
538
+ const rangeStartAriaLabel = computed(() => {
539
+ return props.label ? t("origam.slider_field.thumb_start_aria_label", props.label) : void 0;
540
+ });
541
+ const rangeEndAriaLabel = computed(() => {
542
+ return props.label ? t("origam.slider_field.thumb_end_aria_label", props.label) : void 0;
543
+ });
534
544
  const steps = useSteps(props);
535
545
  const { min: resolvedMin, max: resolvedMax, step: resolvedStep, roundValue } = steps;
536
546
  const model = useVModel(
@@ -642,8 +652,8 @@ const parsedTicks = computed(() => {
642
652
  if (!step || span <= 0) return [];
643
653
  const count = span / step;
644
654
  if (!isFinite(count)) return [];
645
- return createRange(count + 1).map((t) => {
646
- const value = resolvedMin.value + t * step;
655
+ return createRange(count + 1).map((t2) => {
656
+ const value = resolvedMin.value + t2 * step;
647
657
  return {
648
658
  value,
649
659
  position: toPercent(value)
@@ -651,10 +661,10 @@ const parsedTicks = computed(() => {
651
661
  });
652
662
  }
653
663
  if (Array.isArray(ticksProp)) {
654
- return ticksProp.map((t) => ({
655
- value: t,
656
- position: toPercent(t),
657
- label: String(t)
664
+ return ticksProp.map((t2) => ({
665
+ value: t2,
666
+ position: toPercent(t2),
667
+ label: String(t2)
658
668
  }));
659
669
  }
660
670
  if (typeof ticksProp === "object" && ticksProp !== null) {
@@ -123,7 +123,7 @@ const stackClasses = computed(() => {
123
123
  ];
124
124
  });
125
125
  const resolvedDomId = computed(() => `origam-snackbar-group-${props.id}`);
126
- const { id: styleId, css, load, isLoaded, unload } = useStyle(stackStyles);
126
+ const { id: styleId, css, load, isLoaded, unload } = useStyle(stackStyles, () => resolvedDomId.value);
127
127
  defineExpose({
128
128
  filterProps,
129
129
  visibleItems,
@@ -229,6 +229,8 @@ const props = defineProps({
229
229
  appendIcon: { type: null, required: false },
230
230
  prependAvatar: { type: String, required: false },
231
231
  prependIcon: { type: null, required: false },
232
+ prependAriaLabel: { type: String, required: false },
233
+ appendAriaLabel: { type: String, required: false },
232
234
  size: { type: null, required: false },
233
235
  fontSize: { type: null, required: false },
234
236
  fontWeight: { type: null, required: false },
@@ -13,7 +13,6 @@
13
13
  :aria-disabled="isDisabled || undefined"
14
14
  :tabindex="tabIndex"
15
15
  :disabled="isDisabled || undefined"
16
- :data-origam-tab-id="groupItem?.id"
17
16
  @click="handleClick"
18
17
  >
19
18
  <span
@@ -75,6 +74,7 @@ import { useProps } from "../../composables/Commons/props.composable";
75
74
  import { useStyle } from "../../composables/Commons/style.composable";
76
75
  import { useTypography } from "../../composables/Commons/typography.composable";
77
76
  import vContrast from "../../directives/Contrast/contrast.directive";
77
+ import { getUid } from "../../utils/Commons/getCurrentInstance.util";
78
78
  import { ORIGAM_TABS_KEY, ORIGAM_TAB_PANELS_LINK_KEY } from "../../consts/Tabs/tabs.const";
79
79
  const props = defineProps({
80
80
  tag: { type: String, required: false, default: "button" },
@@ -93,7 +93,9 @@ const props = defineProps({
93
93
  appendAvatar: { type: String, required: false },
94
94
  appendIcon: { type: null, required: false },
95
95
  prependAvatar: { type: String, required: false },
96
- prependIcon: { type: null, required: false }
96
+ prependIcon: { type: null, required: false },
97
+ prependAriaLabel: { type: String, required: false },
98
+ appendAriaLabel: { type: String, required: false }
97
99
  });
98
100
  defineEmits(["group:selected"]);
99
101
  defineSlots();
@@ -116,7 +118,8 @@ const slotProps = computed(() => ({
116
118
  value: groupItem.value.value,
117
119
  disabled: groupItem.disabled.value
118
120
  }));
119
- const tabDomId = computed(() => props.id || `origam-tab-${groupItem.id}`);
121
+ const uid = getUid();
122
+ const tabDomId = computed(() => props.id || `origam-tab-${uid}`);
120
123
  watchEffect(() => {
121
124
  const self = groupItem.group.items.value.find((item) => item.id === groupItem.id);
122
125
  if (self) self.domId = tabDomId.value;
@@ -126,7 +129,7 @@ const panelId = computed(() => {
126
129
  if (!panelsGroup) return void 0;
127
130
  const panel = panelsGroup.items.value.find((item) => item.value === groupItem.value.value);
128
131
  if (!panel) return void 0;
129
- return panel.domId || `origam-tab-panel-${panel.id}`;
132
+ return panel.domId;
130
133
  });
131
134
  const ariaSelected = computed(() => groupItem.isSelected.value ? "true" : "false");
132
135
  const tabIndex = computed(() => {