@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.
- package/dist/design-system-v3.d.ts +785 -372
- package/dist/design-system-v3.js +4993 -3357
- package/dist/design-system-v3.umd.cjs +1 -10
- package/dist/style.css +1 -1
- package/package.json +10 -2
- package/src/assets/settings.scss +2 -2
- package/src/assets/tokens.scss +107 -112
- package/src/components/BackBtn/BackBtn.vue +4 -4
- package/src/components/BackToTopBtn/BackToTopBtn.vue +1 -0
- package/src/components/CollapsibleList/CollapsibleList.mdx +1 -1
- package/src/components/CollapsibleList/CollapsibleList.vue +43 -44
- package/src/components/ContextualMenu/Accessibilite.mdx +14 -0
- package/src/components/ContextualMenu/Accessibilite.stories.ts +191 -0
- package/src/components/ContextualMenu/AccessibiliteItems.ts +89 -0
- package/src/components/ContextualMenu/ContextualMenu.mdx +118 -0
- package/src/components/ContextualMenu/ContextualMenu.stories.ts +430 -0
- package/src/components/ContextualMenu/ContextualMenu.vue +101 -0
- package/src/components/ContextualMenu/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/ContextualMenu/tests/ContextualMenu.spec.ts +115 -0
- package/src/components/ContextualMenu/tests/__snapshots__/ContextualMenu.spec.ts.snap +10 -0
- package/src/components/ContextualMenu/types.ts +5 -0
- package/src/components/CookieBanner/CookieBanner.stories.ts +1 -2
- package/src/components/CookieBanner/CookieBanner.vue +13 -10
- package/src/components/CookieBanner/tests/__snapshots__/CookieBanner.spec.ts.snap +17 -15
- package/src/components/CookiesSelection/CookiesInformation/CookiesInformation.vue +6 -1
- package/src/components/CookiesSelection/CookiesInformation/locales.ts +1 -0
- package/src/components/CookiesSelection/CookiesTable/CookiesTable.vue +1 -0
- package/src/components/CookiesSelection/tests/__snapshots__/CookiesSelection.spec.ts.snap +17 -15
- package/src/components/CopyBtn/CopyBtn.vue +7 -7
- package/src/components/Customs/SyBtnSelect/SyBtnSelect.vue +26 -26
- package/src/components/Customs/SyInputSelect/SyInputSelect.vue +24 -24
- package/src/components/Customs/SySelect/SySelect.stories.ts +7 -7
- package/src/components/Customs/SySelect/SySelect.vue +36 -30
- package/src/components/Customs/SySelect/tests/SySelect.spec.ts +2 -2
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +187 -2
- package/src/components/Customs/SyTextField/SyTextField.vue +185 -16
- package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +2 -4
- package/src/components/Customs/SyTextField/tests/__snapshots__/SyTextField.spec.ts.snap +18 -16
- package/src/components/Customs/SyTextField/types.d.ts +2 -2
- package/src/components/DataList/DataList.stories.ts +3 -2
- package/src/components/DataList/DataList.vue +1 -1
- package/src/components/DataListGroup/DataListGroup.stories.ts +3 -2
- package/src/components/DataListItem/DataListItem.vue +12 -12
- package/src/components/DatePicker/DatePicker.mdx +191 -0
- package/src/components/DatePicker/DatePicker.stories.ts +787 -0
- package/src/components/DatePicker/DatePicker.vue +560 -0
- package/src/components/DatePicker/DateTextInput.vue +409 -0
- package/src/components/DatePicker/tests/DatePicker.spec.ts +266 -0
- package/src/components/DialogBox/DialogBox.mdx +28 -2
- package/src/components/DialogBox/DialogBox.stories.ts +2 -2
- package/src/components/DialogBox/DialogBox.vue +3 -2
- package/src/components/DownloadBtn/DownloadBtn.vue +2 -1
- package/src/components/ExternalLinks/Accessibilite.mdx +14 -0
- package/src/components/ExternalLinks/Accessibilite.stories.ts +191 -0
- package/src/components/ExternalLinks/AccessibiliteItems.ts +197 -0
- package/src/components/ExternalLinks/ExternalLinks.mdx +86 -0
- package/src/components/ExternalLinks/ExternalLinks.stories.ts +553 -0
- package/src/components/ExternalLinks/ExternalLinks.vue +200 -0
- package/src/components/ExternalLinks/config.ts +34 -0
- package/src/components/ExternalLinks/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/ExternalLinks/locales.ts +4 -0
- package/src/components/ExternalLinks/tests/ExternalLinks.spec.ts +154 -0
- package/src/components/ExternalLinks/tests/__snapshots__/ExternalLinks.spec.ts.snap +159 -0
- package/src/components/FileUpload/FileUpload.mdx +165 -0
- package/src/components/FileUpload/FileUpload.stories.ts +429 -0
- package/src/components/FileUpload/FileUpload.vue +195 -0
- package/src/components/FileUpload/FileUploadContent.vue +109 -0
- package/src/components/FileUpload/locales.ts +10 -0
- package/src/components/FileUpload/tests/FileUpload.spec.ts +332 -0
- package/src/components/FileUpload/tests/__snapshots__/FileUpload.spec.ts.snap +7 -0
- package/src/components/FileUpload/useFileDrop.ts +23 -0
- package/src/components/FileUpload/validateFiles.ts +39 -0
- package/src/components/FooterBar/FooterBar.vue +105 -80
- package/src/components/FranceConnectBtn/FranceConnectBtn.vue +14 -13
- package/src/components/HeaderBar/HeaderBar.vue +3 -3
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderBurgerMenu.vue +11 -7
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +5 -5
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuSection/HeaderMenuSection.vue +2 -2
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +10 -8
- package/src/components/HeaderBar/HeaderLogo/HeaderLogo.vue +2 -2
- package/src/components/HeaderBar/HeaderLogo/logos/Logo-mobile.vue +2 -1
- package/src/components/HeaderBar/HeaderLogo/logos/Logo.vue +2 -1
- package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +10 -10
- package/src/components/HeaderBar/consts.scss +1 -1
- package/src/components/HeaderLoading/HeaderLoading.vue +12 -11
- package/src/components/HeaderNavigationBar/HeaderNavigationBar.vue +2 -1
- package/src/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.vue +9 -9
- package/src/components/HeaderToolbar/HeaderToolbar.vue +215 -202
- package/src/components/LangBtn/LangBtn.vue +8 -6
- package/src/components/LogoBrandSection/LogoBrandSection.stories.ts +2 -2
- package/src/components/NirField/NirField.stories.ts +8 -8
- package/src/components/NirField/NirField.vue +46 -48
- package/src/components/NotFoundPage/NotFoundPage.stories.ts +33 -2
- package/src/components/NotFoundPage/NotFoundPage.vue +17 -0
- package/src/components/NotificationBar/NotificationBar.mdx +5 -5
- package/src/components/NotificationBar/NotificationBar.stories.ts +410 -314
- package/src/components/NotificationBar/NotificationBar.vue +43 -41
- package/src/components/PageContainer/PageContainer.vue +4 -4
- package/src/components/PasswordField/Accessibilite.mdx +14 -0
- package/src/components/PasswordField/Accessibilite.stories.ts +191 -0
- package/src/components/PasswordField/AccessibiliteItems.ts +184 -0
- package/src/components/PasswordField/PasswordField.mdx +70 -0
- package/src/components/PasswordField/PasswordField.stories.ts +213 -0
- package/src/components/PasswordField/PasswordField.vue +189 -0
- package/src/components/PasswordField/config.ts +11 -0
- package/src/components/PasswordField/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/PasswordField/locales.ts +4 -0
- package/src/components/PasswordField/tests/PasswordField.spec.ts +96 -0
- package/src/components/PhoneField/PhoneField.mdx +0 -2
- package/src/components/PhoneField/PhoneField.stories.ts +10 -50
- package/src/components/PhoneField/PhoneField.vue +77 -93
- package/src/components/PhoneField/tests/PhoneField.spec.ts +0 -15
- package/src/components/RangeField/RangeField.mdx +54 -0
- package/src/components/RangeField/RangeField.stories.ts +189 -0
- package/src/components/RangeField/RangeField.vue +157 -0
- package/src/components/RangeField/RangeSlider/RangeSlider.vue +387 -0
- package/src/components/RangeField/RangeSlider/Tooltip/Tooltip.vue +64 -0
- package/src/components/RangeField/RangeSlider/tests/__snapshots__/rangeSlider.spec.ts.snap +27 -0
- package/src/components/RangeField/RangeSlider/tests/rangeSlider.spec.ts +100 -0
- package/src/components/RangeField/RangeSlider/tests/useDoubleSlider.spec.ts +246 -0
- package/src/components/RangeField/RangeSlider/tests/useMouseSlide.spec.ts +204 -0
- package/src/components/RangeField/RangeSlider/tests/useThumb.spec.ts +22 -0
- package/src/components/RangeField/RangeSlider/tests/useThumbKeyboard.spec.ts +233 -0
- package/src/components/RangeField/RangeSlider/tests/useTooltipsNudge.spec.ts +150 -0
- package/src/components/RangeField/RangeSlider/tests/useTrack.spec.ts +314 -0
- package/src/components/RangeField/RangeSlider/tests/vAnimateClick.spec.ts +32 -0
- package/src/components/RangeField/RangeSlider/types.ts +15 -0
- package/src/components/RangeField/RangeSlider/useMouseSlide.ts +109 -0
- package/src/components/RangeField/RangeSlider/useRangeSlider.ts +126 -0
- package/src/components/RangeField/RangeSlider/useThumb.ts +18 -0
- package/src/components/RangeField/RangeSlider/useThumbKeyboard.ts +84 -0
- package/src/components/RangeField/RangeSlider/useTooltipsNudge.ts +92 -0
- package/src/components/RangeField/RangeSlider/useTrack.ts +116 -0
- package/src/components/RangeField/RangeSlider/vAnimateClick.ts +19 -0
- package/src/components/RangeField/config.ts +7 -0
- package/src/components/RangeField/locales.ts +4 -0
- package/src/components/RangeField/tests/RangeField.spec.ts +224 -0
- package/src/components/RangeField/tests/__snapshots__/RangeField.spec.ts.snap +379 -0
- package/src/components/RatingPicker/EmotionPicker/EmotionPicker.vue +205 -0
- package/src/components/RatingPicker/EmotionPicker/locales.ts +3 -0
- package/src/components/RatingPicker/EmotionPicker/tests/EmotionPicker.spec.ts +104 -0
- package/src/components/RatingPicker/EmotionPicker/tests/__snapshots__/EmotionPicker.spec.ts.snap +66 -0
- package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +159 -0
- package/src/components/RatingPicker/NumberPicker/locales.ts +4 -0
- package/src/components/RatingPicker/NumberPicker/tests/NumberPicker.spec.ts +73 -0
- package/src/components/RatingPicker/NumberPicker/tests/__snapshots__/NumberPicker.spec.ts.snap +105 -0
- package/src/components/RatingPicker/Rating.ts +45 -0
- package/src/components/RatingPicker/RatingPicker.mdx +56 -0
- package/src/components/RatingPicker/RatingPicker.stories.ts +515 -0
- package/src/components/RatingPicker/RatingPicker.vue +122 -0
- package/src/components/RatingPicker/StarsPicker/StarsPicker.vue +116 -0
- package/src/components/RatingPicker/StarsPicker/tests/StarsPicker.spec.ts +95 -0
- package/src/components/RatingPicker/StarsPicker/tests/__snapshots__/StarsPicker.spec.ts.snap +36 -0
- package/src/components/RatingPicker/locales.ts +3 -0
- package/src/components/RatingPicker/tests/Rating.spec.ts +104 -0
- package/src/components/RatingPicker/tests/RatingPicker.spec.ts +187 -0
- package/src/components/RatingPicker/tests/__snapshots__/RatingPicker.spec.ts.snap +108 -0
- package/src/components/SearchListField/SearchListField.mdx +74 -0
- package/src/components/SearchListField/SearchListField.stories.ts +126 -0
- package/src/components/SearchListField/SearchListField.vue +194 -0
- package/src/components/SearchListField/locales.ts +5 -0
- package/src/components/SearchListField/tests/SearchListField.spec.ts +323 -0
- package/src/components/SearchListField/types.d.ts +4 -0
- package/src/components/SelectBtnField/SelectBtnField.mdx +50 -0
- package/src/components/SelectBtnField/SelectBtnField.stories.ts +763 -0
- package/src/components/SelectBtnField/SelectBtnField.vue +283 -0
- package/src/components/SelectBtnField/config.ts +11 -0
- package/src/components/SelectBtnField/tests/SelectBtnField.spec.ts +327 -0
- package/src/components/SelectBtnField/tests/__snapshots__/SelectBtnField.spec.ts.snap +125 -0
- package/src/components/SelectBtnField/types.d.ts +11 -0
- package/src/components/SkipLink/SkipLink.vue +10 -10
- package/src/components/SocialMediaLinks/SocialMediaLinks.vue +28 -26
- package/src/components/SubHeader/SubHeader.vue +32 -31
- package/src/components/SyAlert/SyAlert.vue +12 -12
- package/src/components/UserMenuBtn/UserMenuBtn.vue +1 -1
- package/src/components/UserMenuBtn/config.ts +1 -1
- package/src/components/index.ts +17 -7
- package/src/composables/rules/useFieldValidation.ts +172 -44
- package/src/designTokens/index.ts +6 -4
- package/src/designTokens/{bootstrapColors.md → paColors.md} +1 -1
- package/src/designTokens/tokens/cnam/cnamLightTheme.ts +2 -0
- package/src/designTokens/tokens/pa/paColors.ts +171 -0
- package/src/designTokens/tokens/pa/paContextual.ts +58 -0
- package/src/designTokens/tokens/pa/paDarkTheme.ts +5 -0
- package/src/designTokens/tokens/pa/paLightTheme.ts +123 -0
- package/src/designTokens/tokens/pa/paSemantic.ts +87 -0
- package/src/stories/Fondamentaux/CustomisationEtThemes.mdx +52 -2
- package/src/stories/GuideDuDev/CreerUneIssue.mdx +64 -0
- package/src/stories/GuideDuDev/{CommentUtiliserLesRules.mdx → UtiliserLesRules.mdx} +2 -2
- package/src/stories/GuideDuDev/components.stories.ts +9 -7
- package/src/stories/Guidelines/Vuetify/Vuetify.stories.ts +163 -88
- package/src/stories/Guidelines/Vuetify/VuetifyItems.ts +250 -23
- package/src/temp/TestDTComponent.vue +5 -6
- package/src/utils/calcHumanFileSize/index.ts +12 -0
- package/src/utils/calcHumanFileSize/tests/calcHumanFileSize.spec.ts +21 -0
- package/src/designTokens/tokens/bootstrap/bootstrapColors.ts +0 -158
- package/src/designTokens/tokens/bootstrap/bootstrapLightTheme.ts +0 -22
- 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
|
+
}
|