@cnamts/synapse 0.0.7-alpha → 0.0.9-alpha

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 (198) hide show
  1. package/dist/design-system-v3.d.ts +785 -372
  2. package/dist/design-system-v3.js +4993 -3357
  3. package/dist/design-system-v3.umd.cjs +1 -10
  4. package/dist/style.css +1 -1
  5. package/package.json +10 -2
  6. package/src/assets/settings.scss +2 -2
  7. package/src/assets/tokens.scss +107 -112
  8. package/src/components/BackBtn/BackBtn.vue +4 -4
  9. package/src/components/BackToTopBtn/BackToTopBtn.vue +1 -0
  10. package/src/components/CollapsibleList/CollapsibleList.mdx +1 -1
  11. package/src/components/CollapsibleList/CollapsibleList.vue +43 -44
  12. package/src/components/ContextualMenu/Accessibilite.mdx +14 -0
  13. package/src/components/ContextualMenu/Accessibilite.stories.ts +191 -0
  14. package/src/components/ContextualMenu/AccessibiliteItems.ts +89 -0
  15. package/src/components/ContextualMenu/ContextualMenu.mdx +118 -0
  16. package/src/components/ContextualMenu/ContextualMenu.stories.ts +430 -0
  17. package/src/components/ContextualMenu/ContextualMenu.vue +101 -0
  18. package/src/components/ContextualMenu/constants/ExpertiseLevelEnum.ts +4 -0
  19. package/src/components/ContextualMenu/tests/ContextualMenu.spec.ts +115 -0
  20. package/src/components/ContextualMenu/tests/__snapshots__/ContextualMenu.spec.ts.snap +10 -0
  21. package/src/components/ContextualMenu/types.ts +5 -0
  22. package/src/components/CookieBanner/CookieBanner.stories.ts +1 -2
  23. package/src/components/CookieBanner/CookieBanner.vue +13 -10
  24. package/src/components/CookieBanner/tests/__snapshots__/CookieBanner.spec.ts.snap +17 -15
  25. package/src/components/CookiesSelection/CookiesInformation/CookiesInformation.vue +6 -1
  26. package/src/components/CookiesSelection/CookiesInformation/locales.ts +1 -0
  27. package/src/components/CookiesSelection/CookiesTable/CookiesTable.vue +1 -0
  28. package/src/components/CookiesSelection/tests/__snapshots__/CookiesSelection.spec.ts.snap +17 -15
  29. package/src/components/CopyBtn/CopyBtn.vue +7 -7
  30. package/src/components/Customs/SyBtnSelect/SyBtnSelect.vue +26 -26
  31. package/src/components/Customs/SyInputSelect/SyInputSelect.vue +24 -24
  32. package/src/components/Customs/SySelect/SySelect.stories.ts +7 -7
  33. package/src/components/Customs/SySelect/SySelect.vue +36 -30
  34. package/src/components/Customs/SySelect/tests/SySelect.spec.ts +2 -2
  35. package/src/components/Customs/SyTextField/SyTextField.stories.ts +187 -2
  36. package/src/components/Customs/SyTextField/SyTextField.vue +185 -16
  37. package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +2 -4
  38. package/src/components/Customs/SyTextField/tests/__snapshots__/SyTextField.spec.ts.snap +18 -16
  39. package/src/components/Customs/SyTextField/types.d.ts +2 -2
  40. package/src/components/DataList/DataList.stories.ts +3 -2
  41. package/src/components/DataList/DataList.vue +1 -1
  42. package/src/components/DataListGroup/DataListGroup.stories.ts +3 -2
  43. package/src/components/DataListItem/DataListItem.vue +12 -12
  44. package/src/components/DatePicker/DatePicker.mdx +191 -0
  45. package/src/components/DatePicker/DatePicker.stories.ts +787 -0
  46. package/src/components/DatePicker/DatePicker.vue +560 -0
  47. package/src/components/DatePicker/DateTextInput.vue +409 -0
  48. package/src/components/DatePicker/tests/DatePicker.spec.ts +266 -0
  49. package/src/components/DialogBox/DialogBox.mdx +28 -2
  50. package/src/components/DialogBox/DialogBox.stories.ts +2 -2
  51. package/src/components/DialogBox/DialogBox.vue +3 -2
  52. package/src/components/DownloadBtn/DownloadBtn.vue +2 -1
  53. package/src/components/ExternalLinks/Accessibilite.mdx +14 -0
  54. package/src/components/ExternalLinks/Accessibilite.stories.ts +191 -0
  55. package/src/components/ExternalLinks/AccessibiliteItems.ts +197 -0
  56. package/src/components/ExternalLinks/ExternalLinks.mdx +86 -0
  57. package/src/components/ExternalLinks/ExternalLinks.stories.ts +553 -0
  58. package/src/components/ExternalLinks/ExternalLinks.vue +200 -0
  59. package/src/components/ExternalLinks/config.ts +34 -0
  60. package/src/components/ExternalLinks/constants/ExpertiseLevelEnum.ts +4 -0
  61. package/src/components/ExternalLinks/locales.ts +4 -0
  62. package/src/components/ExternalLinks/tests/ExternalLinks.spec.ts +154 -0
  63. package/src/components/ExternalLinks/tests/__snapshots__/ExternalLinks.spec.ts.snap +159 -0
  64. package/src/components/FileUpload/FileUpload.mdx +165 -0
  65. package/src/components/FileUpload/FileUpload.stories.ts +429 -0
  66. package/src/components/FileUpload/FileUpload.vue +195 -0
  67. package/src/components/FileUpload/FileUploadContent.vue +109 -0
  68. package/src/components/FileUpload/locales.ts +10 -0
  69. package/src/components/FileUpload/tests/FileUpload.spec.ts +332 -0
  70. package/src/components/FileUpload/tests/__snapshots__/FileUpload.spec.ts.snap +7 -0
  71. package/src/components/FileUpload/useFileDrop.ts +23 -0
  72. package/src/components/FileUpload/validateFiles.ts +39 -0
  73. package/src/components/FooterBar/FooterBar.vue +105 -80
  74. package/src/components/FranceConnectBtn/FranceConnectBtn.vue +14 -13
  75. package/src/components/HeaderBar/HeaderBar.vue +3 -3
  76. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderBurgerMenu.vue +11 -7
  77. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +5 -5
  78. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuSection/HeaderMenuSection.vue +2 -2
  79. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +10 -8
  80. package/src/components/HeaderBar/HeaderLogo/HeaderLogo.vue +2 -2
  81. package/src/components/HeaderBar/HeaderLogo/logos/Logo-mobile.vue +2 -1
  82. package/src/components/HeaderBar/HeaderLogo/logos/Logo.vue +2 -1
  83. package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +10 -10
  84. package/src/components/HeaderBar/consts.scss +1 -1
  85. package/src/components/HeaderLoading/HeaderLoading.vue +12 -11
  86. package/src/components/HeaderNavigationBar/HeaderNavigationBar.vue +2 -1
  87. package/src/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.vue +9 -9
  88. package/src/components/HeaderToolbar/HeaderToolbar.vue +215 -202
  89. package/src/components/LangBtn/LangBtn.vue +8 -6
  90. package/src/components/LogoBrandSection/LogoBrandSection.stories.ts +2 -2
  91. package/src/components/NirField/NirField.stories.ts +8 -8
  92. package/src/components/NirField/NirField.vue +46 -48
  93. package/src/components/NotFoundPage/NotFoundPage.stories.ts +33 -2
  94. package/src/components/NotFoundPage/NotFoundPage.vue +17 -0
  95. package/src/components/NotificationBar/NotificationBar.mdx +5 -5
  96. package/src/components/NotificationBar/NotificationBar.stories.ts +410 -314
  97. package/src/components/NotificationBar/NotificationBar.vue +43 -41
  98. package/src/components/PageContainer/PageContainer.vue +4 -4
  99. package/src/components/PasswordField/Accessibilite.mdx +14 -0
  100. package/src/components/PasswordField/Accessibilite.stories.ts +191 -0
  101. package/src/components/PasswordField/AccessibiliteItems.ts +184 -0
  102. package/src/components/PasswordField/PasswordField.mdx +70 -0
  103. package/src/components/PasswordField/PasswordField.stories.ts +213 -0
  104. package/src/components/PasswordField/PasswordField.vue +189 -0
  105. package/src/components/PasswordField/config.ts +11 -0
  106. package/src/components/PasswordField/constants/ExpertiseLevelEnum.ts +4 -0
  107. package/src/components/PasswordField/locales.ts +4 -0
  108. package/src/components/PasswordField/tests/PasswordField.spec.ts +96 -0
  109. package/src/components/PhoneField/PhoneField.mdx +0 -2
  110. package/src/components/PhoneField/PhoneField.stories.ts +10 -50
  111. package/src/components/PhoneField/PhoneField.vue +77 -93
  112. package/src/components/PhoneField/tests/PhoneField.spec.ts +0 -15
  113. package/src/components/RangeField/RangeField.mdx +54 -0
  114. package/src/components/RangeField/RangeField.stories.ts +189 -0
  115. package/src/components/RangeField/RangeField.vue +157 -0
  116. package/src/components/RangeField/RangeSlider/RangeSlider.vue +387 -0
  117. package/src/components/RangeField/RangeSlider/Tooltip/Tooltip.vue +64 -0
  118. package/src/components/RangeField/RangeSlider/tests/__snapshots__/rangeSlider.spec.ts.snap +27 -0
  119. package/src/components/RangeField/RangeSlider/tests/rangeSlider.spec.ts +100 -0
  120. package/src/components/RangeField/RangeSlider/tests/useDoubleSlider.spec.ts +246 -0
  121. package/src/components/RangeField/RangeSlider/tests/useMouseSlide.spec.ts +204 -0
  122. package/src/components/RangeField/RangeSlider/tests/useThumb.spec.ts +22 -0
  123. package/src/components/RangeField/RangeSlider/tests/useThumbKeyboard.spec.ts +233 -0
  124. package/src/components/RangeField/RangeSlider/tests/useTooltipsNudge.spec.ts +150 -0
  125. package/src/components/RangeField/RangeSlider/tests/useTrack.spec.ts +314 -0
  126. package/src/components/RangeField/RangeSlider/tests/vAnimateClick.spec.ts +32 -0
  127. package/src/components/RangeField/RangeSlider/types.ts +15 -0
  128. package/src/components/RangeField/RangeSlider/useMouseSlide.ts +109 -0
  129. package/src/components/RangeField/RangeSlider/useRangeSlider.ts +126 -0
  130. package/src/components/RangeField/RangeSlider/useThumb.ts +18 -0
  131. package/src/components/RangeField/RangeSlider/useThumbKeyboard.ts +84 -0
  132. package/src/components/RangeField/RangeSlider/useTooltipsNudge.ts +92 -0
  133. package/src/components/RangeField/RangeSlider/useTrack.ts +116 -0
  134. package/src/components/RangeField/RangeSlider/vAnimateClick.ts +19 -0
  135. package/src/components/RangeField/config.ts +7 -0
  136. package/src/components/RangeField/locales.ts +4 -0
  137. package/src/components/RangeField/tests/RangeField.spec.ts +224 -0
  138. package/src/components/RangeField/tests/__snapshots__/RangeField.spec.ts.snap +379 -0
  139. package/src/components/RatingPicker/EmotionPicker/EmotionPicker.vue +205 -0
  140. package/src/components/RatingPicker/EmotionPicker/locales.ts +3 -0
  141. package/src/components/RatingPicker/EmotionPicker/tests/EmotionPicker.spec.ts +104 -0
  142. package/src/components/RatingPicker/EmotionPicker/tests/__snapshots__/EmotionPicker.spec.ts.snap +66 -0
  143. package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +159 -0
  144. package/src/components/RatingPicker/NumberPicker/locales.ts +4 -0
  145. package/src/components/RatingPicker/NumberPicker/tests/NumberPicker.spec.ts +73 -0
  146. package/src/components/RatingPicker/NumberPicker/tests/__snapshots__/NumberPicker.spec.ts.snap +105 -0
  147. package/src/components/RatingPicker/Rating.ts +45 -0
  148. package/src/components/RatingPicker/RatingPicker.mdx +56 -0
  149. package/src/components/RatingPicker/RatingPicker.stories.ts +515 -0
  150. package/src/components/RatingPicker/RatingPicker.vue +122 -0
  151. package/src/components/RatingPicker/StarsPicker/StarsPicker.vue +116 -0
  152. package/src/components/RatingPicker/StarsPicker/tests/StarsPicker.spec.ts +95 -0
  153. package/src/components/RatingPicker/StarsPicker/tests/__snapshots__/StarsPicker.spec.ts.snap +36 -0
  154. package/src/components/RatingPicker/locales.ts +3 -0
  155. package/src/components/RatingPicker/tests/Rating.spec.ts +104 -0
  156. package/src/components/RatingPicker/tests/RatingPicker.spec.ts +187 -0
  157. package/src/components/RatingPicker/tests/__snapshots__/RatingPicker.spec.ts.snap +108 -0
  158. package/src/components/SearchListField/SearchListField.mdx +74 -0
  159. package/src/components/SearchListField/SearchListField.stories.ts +126 -0
  160. package/src/components/SearchListField/SearchListField.vue +194 -0
  161. package/src/components/SearchListField/locales.ts +5 -0
  162. package/src/components/SearchListField/tests/SearchListField.spec.ts +323 -0
  163. package/src/components/SearchListField/types.d.ts +4 -0
  164. package/src/components/SelectBtnField/SelectBtnField.mdx +50 -0
  165. package/src/components/SelectBtnField/SelectBtnField.stories.ts +763 -0
  166. package/src/components/SelectBtnField/SelectBtnField.vue +283 -0
  167. package/src/components/SelectBtnField/config.ts +11 -0
  168. package/src/components/SelectBtnField/tests/SelectBtnField.spec.ts +327 -0
  169. package/src/components/SelectBtnField/tests/__snapshots__/SelectBtnField.spec.ts.snap +125 -0
  170. package/src/components/SelectBtnField/types.d.ts +11 -0
  171. package/src/components/SkipLink/SkipLink.vue +10 -10
  172. package/src/components/SocialMediaLinks/SocialMediaLinks.vue +28 -26
  173. package/src/components/SubHeader/SubHeader.vue +32 -31
  174. package/src/components/SyAlert/SyAlert.vue +12 -12
  175. package/src/components/UserMenuBtn/UserMenuBtn.vue +1 -1
  176. package/src/components/UserMenuBtn/config.ts +1 -1
  177. package/src/components/index.ts +17 -7
  178. package/src/composables/rules/useFieldValidation.ts +172 -44
  179. package/src/designTokens/index.ts +6 -4
  180. package/src/designTokens/{bootstrapColors.md → paColors.md} +1 -1
  181. package/src/designTokens/tokens/cnam/cnamLightTheme.ts +2 -0
  182. package/src/designTokens/tokens/pa/paColors.ts +171 -0
  183. package/src/designTokens/tokens/pa/paContextual.ts +58 -0
  184. package/src/designTokens/tokens/pa/paDarkTheme.ts +5 -0
  185. package/src/designTokens/tokens/pa/paLightTheme.ts +123 -0
  186. package/src/designTokens/tokens/pa/paSemantic.ts +87 -0
  187. package/src/stories/Fondamentaux/CustomisationEtThemes.mdx +52 -2
  188. package/src/stories/GuideDuDev/CreerUneIssue.mdx +64 -0
  189. package/src/stories/GuideDuDev/{CommentUtiliserLesRules.mdx → UtiliserLesRules.mdx} +2 -2
  190. package/src/stories/GuideDuDev/components.stories.ts +9 -7
  191. package/src/stories/Guidelines/Vuetify/Vuetify.stories.ts +163 -88
  192. package/src/stories/Guidelines/Vuetify/VuetifyItems.ts +250 -23
  193. package/src/temp/TestDTComponent.vue +5 -6
  194. package/src/utils/calcHumanFileSize/index.ts +12 -0
  195. package/src/utils/calcHumanFileSize/tests/calcHumanFileSize.spec.ts +21 -0
  196. package/src/designTokens/tokens/bootstrap/bootstrapColors.ts +0 -158
  197. package/src/designTokens/tokens/bootstrap/bootstrapLightTheme.ts +0 -22
  198. package/src/stories/GuideDuDev/CommentContribuer.mdx +0 -22
@@ -0,0 +1,15 @@
1
+ import { type MaybeRef } from 'vue'
2
+
3
+ export interface Range {
4
+ selectedMin: MaybeRef<number>
5
+ selectedMax: MaybeRef<number>
6
+ rangeMin: MaybeRef<number>
7
+ rangeMax: MaybeRef<number>
8
+ step: MaybeRef<number>
9
+ }
10
+
11
+ export interface PropsStyle {
12
+ 'thumb-color'?: string
13
+ 'track-color'?: string
14
+ 'track-fill-color'?: string
15
+ }
@@ -0,0 +1,109 @@
1
+ import { onMounted, ref, toValue, type MaybeRef } from 'vue'
2
+
3
+ /**
4
+ * Custom hook to handle mouse dragging functionality for a slider component.
5
+ *
6
+ * @param track - The track element of the slider.
7
+ * @param currentValue - The current range value of the slider.
8
+ * @param rangeStart - The minimal value of the slider range.
9
+ * @param rangeEnd - The maximal value of the slider range.
10
+ * @param step - The step value for the slider.
11
+ * @param minSelectableValue - The minimum currently selectable value for the slider.
12
+ * @param maxSelectableValue - The maximum currently selectable value for the slider.
13
+ * @param callback - The callback function to be called with the new value when the slider is dragged.
14
+ *
15
+ * @returns An object containing the `startDrag` function to initiate the dragging.
16
+ */
17
+ export default function useMouseSlide(
18
+ thumb: MaybeRef<HTMLElement>,
19
+ track: MaybeRef<HTMLElement>,
20
+ currentValue: Readonly<MaybeRef<number>>,
21
+ rangeStart: MaybeRef<number>,
22
+ rangeEnd: MaybeRef<number>,
23
+ step: MaybeRef<number>,
24
+ minSelectableValue: MaybeRef<number>,
25
+ maxSelectableValue: MaybeRef<number>,
26
+ callback: (value: number) => void,
27
+ ) {
28
+ const inProgress = ref(false)
29
+ let effectedChange = 0
30
+ let startX: null | number = null
31
+
32
+ onMounted(() => {
33
+ toValue(thumb).addEventListener('mousedown', startDrag)
34
+ toValue(thumb).addEventListener('touchstart', startDrag)
35
+ })
36
+
37
+ function startDrag() {
38
+ inProgress.value = true
39
+ document.addEventListener('mousemove', drag)
40
+ document.addEventListener('mouseup', stopDrag)
41
+
42
+ document.addEventListener('touchmove', drag)
43
+ document.addEventListener('touchend', stopDrag)
44
+ document.addEventListener('touchcancel', stopDrag)
45
+ }
46
+
47
+ function stopDrag() {
48
+ document.removeEventListener('mousemove', drag)
49
+ document.removeEventListener('mouseup', stopDrag)
50
+
51
+ document.removeEventListener('touchmove', drag)
52
+ document.removeEventListener('touchend', stopDrag)
53
+ document.removeEventListener('touchcancel', stopDrag)
54
+
55
+ effectedChange = 0
56
+ startX = null
57
+
58
+ // avoid click on track after dragging
59
+ setTimeout(() => {
60
+ inProgress.value = false
61
+ }, 100)
62
+ }
63
+
64
+ function drag(event: MouseEvent | TouchEvent) {
65
+ event.stopPropagation()
66
+ const pointerPositionX = ('touches' in event) ? event.touches[0].clientX : event.clientX
67
+ if (startX === null) {
68
+ startX = pointerPositionX
69
+ return
70
+ }
71
+ const curStep = toValue(step)
72
+ const curValue = Math.round(toValue(currentValue) / curStep) * curStep
73
+
74
+ const trackRect = toValue(track).getBoundingClientRect()
75
+ const trackWidth = trackRect.width
76
+ const dx = pointerPositionX - startX
77
+
78
+ const percentChange = dx * 100 / trackWidth
79
+ const percentStep = curStep * 100 / (toValue(rangeEnd) - toValue(rangeStart))
80
+ const stepsChange = Math.round(percentChange / percentStep)
81
+
82
+ const theoreticalTotalChange = stepsChange * curStep
83
+ const theoreticalCurrentChange = theoreticalTotalChange - effectedChange
84
+ const theoreticalNewValue = curValue + theoreticalCurrentChange
85
+
86
+ const clampedNewValue = clamp(
87
+ toValue(minSelectableValue),
88
+ theoreticalNewValue,
89
+ toValue(maxSelectableValue),
90
+ )
91
+
92
+ const currentChange = clampedNewValue - curValue
93
+
94
+ if (currentChange === 0) {
95
+ return
96
+ }
97
+
98
+ effectedChange += currentChange
99
+ callback(clampedNewValue)
100
+ }
101
+
102
+ return {
103
+ inProgress,
104
+ }
105
+ }
106
+
107
+ function clamp(min: number, value: number, max: number) {
108
+ return Math.max(min, Math.min(value, max))
109
+ }
@@ -0,0 +1,126 @@
1
+ import { ref, watch, toValue, type MaybeRef } from 'vue'
2
+
3
+ /**
4
+ * Handle the incoming values for a range slider.
5
+ *
6
+ * @param min The minimum value of the range.
7
+ * @param max The maximum value of the range.
8
+ * @param step The step value for the slider.
9
+ * @param value {[number, number]} The current value of the slider.
10
+ * @returns An reactive object containing the selected min and max values, the range min and max values, and the step value.
11
+ */
12
+ export default function useRangeSlider(
13
+ min: MaybeRef<number | string>,
14
+ max: MaybeRef<number | string>,
15
+ step: MaybeRef<number | string>,
16
+ value: MaybeRef<Array<number | string>>,
17
+ ) {
18
+ if (toValue(min) > toValue(max)) {
19
+ [min, max] = [max, min]
20
+ }
21
+
22
+ const range = {
23
+ selectedMin: ref(Number(toValue(min))),
24
+ selectedMax: ref(Number(toValue(max))),
25
+ rangeMin: ref(Number(toValue(min))),
26
+ rangeMax: ref(Number(toValue(max))),
27
+ step: ref(Number(toValue(step))),
28
+ }
29
+
30
+ // if min change, the other values must be coherent
31
+ watch(
32
+ () => toValue(min),
33
+ (newVal) => {
34
+ newVal = Number(newVal)
35
+
36
+ if (isNaN(newVal) || !isFinite(newVal)) {
37
+ return
38
+ }
39
+ if (newVal > range.rangeMax.value) {
40
+ return
41
+ }
42
+ if (range.selectedMin.value < newVal) {
43
+ range.selectedMin.value = newVal
44
+ }
45
+ if (range.selectedMax.value < newVal) {
46
+ range.selectedMax.value = newVal
47
+ }
48
+
49
+ range.rangeMin.value = newVal
50
+ },
51
+ )
52
+
53
+ // if max change, the other values must be coherent
54
+ watch(
55
+ () => toValue(max),
56
+ (newVal) => {
57
+ newVal = Number(newVal)
58
+
59
+ if (isNaN(newVal) || !isFinite(newVal)) {
60
+ return
61
+ }
62
+ if (newVal < range.rangeMin.value) {
63
+ return
64
+ }
65
+ if (range.selectedMin.value > newVal) {
66
+ range.selectedMin.value = newVal
67
+ }
68
+ if (range.selectedMax.value > newVal) {
69
+ range.selectedMax.value = newVal
70
+ }
71
+
72
+ range.rangeMax.value = newVal
73
+ },
74
+ )
75
+
76
+ watch(
77
+ () => toValue(step),
78
+ (newVal) => {
79
+ newVal = Number(newVal)
80
+ if (!isStepValid(newVal, range.rangeMin.value, range.rangeMax.value)) {
81
+ return
82
+ }
83
+ range.step.value = Math.abs(newVal)
84
+ },
85
+ )
86
+
87
+ // the selected values must be in the bounds and coherent
88
+ watch(
89
+ () => toValue(value),
90
+ (newVal) => {
91
+ const newValCasted = newVal.map(Number)
92
+
93
+ if (!isValidNumber(newValCasted[0]) || !isValidNumber(newValCasted[1])) {
94
+ return
95
+ }
96
+ if (newValCasted[0] > newValCasted[1]) {
97
+ return
98
+ }
99
+ if (newValCasted[0] < range.rangeMin.value) {
100
+ range.selectedMin.value = range.rangeMin.value
101
+ }
102
+ else {
103
+ range.selectedMin.value = newValCasted[0]
104
+ }
105
+ if (newValCasted[1] > range.rangeMax.value) {
106
+ range.selectedMax.value = range.rangeMax.value
107
+ }
108
+ else {
109
+ range.selectedMax.value = newValCasted[1]
110
+ }
111
+ },
112
+ { immediate: true },
113
+ )
114
+
115
+ return range
116
+ }
117
+
118
+ function isValidNumber(value: number) {
119
+ return !isNaN(value) && isFinite(value)
120
+ }
121
+
122
+ function isStepValid(step: number, rangeStart: number, rangeEnd: number) {
123
+ return (
124
+ !isNaN(step) && isFinite(step) && step != 0 && step <= rangeEnd - rangeStart
125
+ )
126
+ }
@@ -0,0 +1,18 @@
1
+ import { computed, toValue, type MaybeRef } from 'vue'
2
+
3
+ export default function useThumb(
4
+ selected: MaybeRef<number>,
5
+ rangeMin: MaybeRef<number>,
6
+ rangeMax: MaybeRef<number>,
7
+ ) {
8
+ const thumbStyle = computed(() => {
9
+ const currentValue = toValue(selected)
10
+ const rangeWidth = toValue(rangeMax) - toValue(rangeMin)
11
+ const percent = rangeWidth == 0 ? 100 : (currentValue - toValue(rangeMin)) / rangeWidth * 100
12
+ return {
13
+ left: `${percent}%`,
14
+ }
15
+ })
16
+
17
+ return { thumbStyle }
18
+ }
@@ -0,0 +1,84 @@
1
+ import { onMounted, toValue, type MaybeRef, type Ref } from 'vue'
2
+
3
+ /**
4
+ * Custom hook to handle keyboard events for a slider thumb.
5
+ *
6
+ * @param thumb The thumb element of the slider.
7
+ * @param value The current value of the slider.
8
+ * @param minSelectableValue The minimum currently selectable value for the slider.
9
+ * @param maxSelectableValue The maximum currently selectable value for the slider.
10
+ * @param step The step displacement for the slider.
11
+ * @param setValue The function to set the value of the slider.
12
+ */
13
+ export default function useThumbKeyboard(
14
+ thumb: MaybeRef<HTMLElement>,
15
+ value: Ref<number>,
16
+ minSelectableValue: MaybeRef<number>,
17
+ maxSelectableValue: MaybeRef<number>,
18
+ step: MaybeRef<number>,
19
+ setValue: (value: number) => void,
20
+ ) {
21
+ onMounted(() => {
22
+ toValue(thumb).addEventListener('keydown', handleKeyDown)
23
+ })
24
+
25
+ function handleKeyDown(event: KeyboardEvent) {
26
+ if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {
27
+ event.preventDefault()
28
+ const curStep = toValue(step)
29
+ const curValue = normalizePositionByStep(toValue(value), curStep)
30
+
31
+ const newValue = curValue - curStep
32
+ if (newValue >= toValue(minSelectableValue)) {
33
+ setValue(newValue)
34
+ }
35
+ }
36
+ else if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {
37
+ event.preventDefault()
38
+ const curStep = toValue(step)
39
+ const curValue = normalizePositionByStep(toValue(value), curStep)
40
+ const newValue = curValue + curStep
41
+ if (newValue <= toValue(maxSelectableValue)) {
42
+ setValue(newValue)
43
+ }
44
+ }
45
+ else if (event.key === 'Home') {
46
+ event.preventDefault()
47
+ setValue(toValue(minSelectableValue))
48
+ }
49
+ else if (event.key === 'End') {
50
+ event.preventDefault()
51
+ setValue(toValue(maxSelectableValue))
52
+ }
53
+ else if (event.key === 'PageDown') {
54
+ event.preventDefault()
55
+ const curStep = toValue(step)
56
+ const curValue = normalizePositionByStep(toValue(value), curStep)
57
+
58
+ const newValue = curValue - curStep * 10
59
+ if (newValue >= toValue(minSelectableValue)) {
60
+ setValue(newValue)
61
+ }
62
+ else {
63
+ setValue(toValue(minSelectableValue))
64
+ }
65
+ }
66
+ else if (event.key === 'PageUp') {
67
+ event.preventDefault()
68
+ const curStep = toValue(step)
69
+ const curValue = normalizePositionByStep(toValue(value), curStep)
70
+
71
+ const newValue = curValue + curStep * 10
72
+ if (newValue <= toValue(maxSelectableValue)) {
73
+ setValue(newValue)
74
+ }
75
+ else {
76
+ setValue(toValue(maxSelectableValue))
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ function normalizePositionByStep(position: number, step: number) {
83
+ return Math.round(position / step) * step
84
+ }
@@ -0,0 +1,92 @@
1
+ import {
2
+ nextTick,
3
+ onMounted,
4
+ onUnmounted,
5
+ ref,
6
+ toValue,
7
+ watch,
8
+ type Ref,
9
+ } from 'vue'
10
+ import type Tooltip from './Tooltip/Tooltip.vue'
11
+ import type { Range } from './types'
12
+
13
+ /**
14
+ * Handles nudging the tooltips when they overlap.
15
+ *
16
+ * @param minThumb The min thumb tooltip.
17
+ * @param maxThumb The max thumb tooltip.
18
+ * @param placeholderMinThumb A fake min thumb tooltip with no animations that is used to calculate the nudge.
19
+ * @param placeholderMaxThumb A fake max thumb tooltip with no animations that is used to calculate the nudge.
20
+ * @param range The range informations.
21
+ */
22
+ export default function useTooltipsNudge(
23
+ minThumb: Ref<typeof Tooltip | null>,
24
+ maxThumb: Ref<typeof Tooltip | null>,
25
+ placeholderMinThumb: Ref<typeof Tooltip | null>,
26
+ placeholderMaxThumb: Ref<typeof Tooltip | null>,
27
+ range: Range,
28
+ ) {
29
+ const nudgeMinThumb = ref(0)
30
+ const nudgeMaxThumb = ref(0)
31
+
32
+ async function calculateNudges() {
33
+ await nextTick()
34
+
35
+ const rectMin: DOMRect = placeholderMinThumb.value!.element.getBoundingClientRect()
36
+ const rectMax: DOMRect = placeholderMaxThumb.value!.element.getBoundingClientRect()
37
+
38
+ const tooltipsOverlaps = rectMin.right - rectMax.left
39
+
40
+ if (tooltipsOverlaps >= 0) {
41
+ const tooltipArrowWidth = 12
42
+
43
+ const minOverflow = rectMin.width / 2 - tooltipArrowWidth
44
+ const maxOverflow = rectMax.width / 2 - tooltipArrowWidth
45
+
46
+ const overflowDiff = Math.abs(minOverflow - maxOverflow)
47
+
48
+ let nudgeMin = 0, nudgeMax = 0
49
+
50
+ if (minOverflow > maxOverflow) {
51
+ nudgeMin = Math.min(tooltipsOverlaps, overflowDiff)
52
+ }
53
+ else {
54
+ nudgeMax = Math.min(tooltipsOverlaps, overflowDiff)
55
+ }
56
+
57
+ if (tooltipsOverlaps > overflowDiff) {
58
+ const residualDifference = tooltipsOverlaps - overflowDiff
59
+ const gap = Math.ceil(residualDifference / 2)
60
+ nudgeMin += gap
61
+ nudgeMax += gap
62
+ }
63
+
64
+ nudgeMinThumb.value = nudgeMin + 1
65
+ nudgeMaxThumb.value = nudgeMax + 1
66
+ }
67
+ else {
68
+ nudgeMinThumb.value = 0
69
+ nudgeMaxThumb.value = 0
70
+ }
71
+ }
72
+
73
+ onMounted(() => {
74
+ minThumb.value!.element.style.transition = 'transform 0.1s'
75
+ maxThumb.value!.element.style.transition = 'transform 0.1s'
76
+ watch(
77
+ () => [toValue(range.selectedMin), toValue(range.selectedMax)],
78
+ calculateNudges,
79
+ { immediate: true },
80
+ )
81
+ window.addEventListener('resize', calculateNudges)
82
+ })
83
+
84
+ onUnmounted(() => {
85
+ window.removeEventListener('resize', calculateNudges)
86
+ })
87
+
88
+ return {
89
+ nudgeMinThumb,
90
+ nudgeMaxThumb,
91
+ }
92
+ }
@@ -0,0 +1,116 @@
1
+ import { onMounted, toValue, type MaybeRef } from 'vue'
2
+ import type { Range } from './types'
3
+
4
+ /**
5
+ * Custom hook to handle the click event on the track of a
6
+ * min - max slider component.
7
+ *
8
+ * @param track The track element of the slider.
9
+ * @param range An object containing the the following informations about the range: rangeMin, rangeMax, selectedMin, selectedMax, step.
10
+ * @param setMin A function to set the minimum value.
11
+ * @param setMax A function to set the maximum value.
12
+ * @param disable Temporary disable the click on the track
13
+ */
14
+ export default function useTrack(
15
+ track: MaybeRef<HTMLElement>,
16
+ range: Range,
17
+ setMin: (value: number) => void,
18
+ setMax: (value: number) => void,
19
+ disable: MaybeRef<boolean> = false,
20
+ ) {
21
+ function setPosition(event: MouseEvent) {
22
+ if (toValue(disable)) return
23
+
24
+ const rect = toValue(track).getBoundingClientRect()
25
+ const rangeStartValue = toValue(range.rangeMin)
26
+ const rangeEndValue = toValue(range.rangeMax)
27
+
28
+ const clickX = event.clientX - rect.left
29
+ const clickXPercentage = (clickX / rect.width) * 100
30
+ const setThumb = getThumbMoveFunc(
31
+ clickXPercentage,
32
+ rangeStartValue,
33
+ rangeEndValue,
34
+ toValue(range.selectedMin),
35
+ toValue(range.selectedMax),
36
+ setMin,
37
+ setMax,
38
+ )
39
+ const newPosition = getClosetStep(
40
+ clickXPercentage,
41
+ rangeStartValue,
42
+ rangeEndValue,
43
+ toValue(range.step),
44
+ )
45
+
46
+ setThumb(newPosition)
47
+ }
48
+
49
+ onMounted(() => {
50
+ toValue(track).addEventListener('click', setPosition)
51
+ })
52
+ }
53
+
54
+ /**
55
+ * Get the closest step position to the click position.
56
+ *
57
+ * @param percentPosition The new position in percentage.
58
+ * @param rangeStart The start of the range.
59
+ * @param rangeEnd The end of the range.
60
+ * @param step The gap between each step.
61
+ * @returns The closest step position.
62
+ */
63
+ function getClosetStep(
64
+ percentPosition: number,
65
+ rangeStart: number,
66
+ rangeEnd: number,
67
+ step: number,
68
+ ) {
69
+ const rangeWidth = rangeEnd - rangeStart
70
+ const percentStep = (step * 100) / rangeWidth
71
+ const stepsChange = Math.round(percentPosition / percentStep)
72
+
73
+ return stepsChange * step + rangeStart
74
+ }
75
+
76
+ /**
77
+ * Get the closest thumb to the click position.
78
+ *
79
+ * @param percentPosition The new position in percentage.
80
+ * @param rangeStart The start of the range.
81
+ * @param rangeEnd The end of the range.
82
+ * @param minSelectedValue The current minimum selected value.
83
+ * @param maxSelectedValue The current maximum selected value.
84
+ * @param setMin A function to set the minimum value.
85
+ * @param setMax A function to set the maximum value.
86
+ * @returns The function to set the thumb value.
87
+ */
88
+ function getThumbMoveFunc(
89
+ percentPosition: number,
90
+ rangeStart: number,
91
+ rangeEnd: number,
92
+ minSelectedValue: number,
93
+ maxSelectedValue: number,
94
+ setMin: (value: number) => void,
95
+ setMax: (value: number) => void,
96
+ ) {
97
+ const rangeWidth = rangeEnd - rangeStart
98
+
99
+ const minPercent = Math.abs(
100
+ ((minSelectedValue - rangeStart) / rangeWidth) * 100,
101
+ )
102
+ const maxPercent = Math.abs(
103
+ ((maxSelectedValue - rangeStart) / rangeWidth) * 100,
104
+ )
105
+ const minDistance = Math.abs(minPercent - percentPosition)
106
+ const maxDistance = Math.abs(maxPercent - percentPosition)
107
+
108
+ if (
109
+ minDistance < maxDistance
110
+ || (minDistance === maxDistance && percentPosition < minPercent)
111
+ ) {
112
+ return setMin
113
+ }
114
+
115
+ return setMax
116
+ }
@@ -0,0 +1,19 @@
1
+ function addClass(el: MouseEvent | TouchEvent) {
2
+ (el.currentTarget as HTMLElement).classList.add('animate-click')
3
+ }
4
+
5
+ function removeClass(el: MouseEvent | TouchEvent) {
6
+ (el.currentTarget as HTMLElement).classList.remove('animate-click')
7
+ }
8
+
9
+ export const vAnimateClick = {
10
+ mounted: (el: HTMLElement) => {
11
+ el.addEventListener('mousedown', addClass)
12
+ el.addEventListener('mouseup', removeClass)
13
+ el.addEventListener('mouseleave', removeClass)
14
+
15
+ el.addEventListener('touchstart', addClass)
16
+ el.addEventListener('touchend', removeClass)
17
+ el.addEventListener('touchcancel', removeClass)
18
+ },
19
+ }
@@ -0,0 +1,7 @@
1
+ export const config = {
2
+ textField: {
3
+ // hideDetails: true,
4
+ class: 'ma-3',
5
+ variant: 'outlined',
6
+ },
7
+ } as const
@@ -0,0 +1,4 @@
1
+ export const locales = {
2
+ minLabel: 'Valeur min',
3
+ maxLabel: 'Valeur max',
4
+ }