@cnamts/synapse 0.0.2-alpha → 0.0.3-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 (46) hide show
  1. package/README.md +1 -1
  2. package/dist/design-system-v3.js +957 -4826
  3. package/dist/design-system-v3.umd.cjs +1 -2
  4. package/dist/style.css +1 -1
  5. package/package.json +29 -29
  6. package/src/components/Alert/Alert.vue +8 -8
  7. package/src/components/FranceConnectBtn/FranceConnectBtn.vue +2 -2
  8. package/src/components/HeaderBar/HeaderBar.mdx +137 -0
  9. package/src/components/HeaderBar/HeaderBar.stories.ts +159 -0
  10. package/src/components/HeaderBar/HeaderBar.vue +238 -0
  11. package/src/components/HeaderBar/HeaderComplexMenu/HeaderComplexMenu.stories.ts +272 -0
  12. package/src/components/HeaderBar/HeaderComplexMenu/HeaderComplexMenu.vue +205 -0
  13. package/src/components/HeaderBar/HeaderComplexMenu/HeaderMenuItem/HeaderMenuItem.stories.ts +49 -0
  14. package/src/components/HeaderBar/HeaderComplexMenu/HeaderMenuItem/HeaderMenuItem.vue +51 -0
  15. package/src/components/HeaderBar/HeaderComplexMenu/HeaderMenuItem/tests/HeaderMenuItem.spec.ts +16 -0
  16. package/src/components/HeaderBar/HeaderComplexMenu/HeaderMenuItem/tests/__snapshots__/HeaderMenuItem.spec.ts.snap +3 -0
  17. package/src/components/HeaderBar/HeaderComplexMenu/HeaderMenuSection/HeaderMenuSection.stories.ts +56 -0
  18. package/src/components/HeaderBar/HeaderComplexMenu/HeaderMenuSection/HeaderMenuSection.vue +51 -0
  19. package/src/components/HeaderBar/HeaderComplexMenu/HeaderMenuSection/tests/HeaderMenuSection.spec.ts +33 -0
  20. package/src/components/HeaderBar/HeaderComplexMenu/HeaderSubMenu/HeaderSubMenu.stories.ts +137 -0
  21. package/src/components/HeaderBar/HeaderComplexMenu/HeaderSubMenu/HeaderSubMenu.vue +180 -0
  22. package/src/components/HeaderBar/HeaderComplexMenu/HeaderSubMenu/tests/HeaderSubMenu.spec.ts +63 -0
  23. package/src/components/HeaderBar/HeaderComplexMenu/conts.ts +1 -0
  24. package/src/components/HeaderBar/HeaderComplexMenu/locals.ts +4 -0
  25. package/src/components/HeaderBar/HeaderComplexMenu/tests/HeaderComplexMenu.spec.ts +129 -0
  26. package/src/components/HeaderBar/HeaderComplexMenu/tests/__snapshots__/HeaderComplexMenu.spec.ts.snap +18 -0
  27. package/src/components/HeaderBar/HeaderComplexMenu/tests/useHandleSubMenus.spec.ts +158 -0
  28. package/src/components/HeaderBar/HeaderComplexMenu/useHandleSubMenus.ts +49 -0
  29. package/src/components/HeaderBar/HeaderLogo/HeaderLogo.vue +106 -0
  30. package/src/components/HeaderBar/HeaderLogo/locales.ts +3 -0
  31. package/src/components/HeaderBar/HeaderLogo/logos/Logo-mobile.vue +117 -0
  32. package/src/components/HeaderBar/HeaderLogo/logos/Logo.vue +279 -0
  33. package/src/components/HeaderBar/HeaderLogo/tests/HeaderLogo.spec.ts +71 -0
  34. package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +88 -0
  35. package/src/components/HeaderBar/HeaderMenuBtn/locals.ts +4 -0
  36. package/src/components/HeaderBar/consts.scss +7 -0
  37. package/src/components/HeaderBar/consts.ts +2 -0
  38. package/src/components/HeaderBar/locales.ts +3 -0
  39. package/src/components/HeaderBar/tests/HeaderBar.spec.ts +210 -0
  40. package/src/components/HeaderBar/tests/__snapshots__/HeaderBar.spec.ts.snap +50 -0
  41. package/src/components/HeaderBar/tests/useHeaderResponsiveMode.spec.ts +26 -0
  42. package/src/components/HeaderBar/tests/useScrollDirection.spec.ts +34 -0
  43. package/src/components/HeaderBar/useHeaderResponsiveMode.ts +25 -0
  44. package/src/components/HeaderBar/useScrollDirection.ts +26 -0
  45. package/src/components/NotificationBar/NotificationBar.vue +5 -7
  46. package/src/components/PageContainer/PageContainer.vue +0 -1
@@ -0,0 +1,2 @@
1
+ export const headerBreakpoint = 990
2
+ export const registerHeaderMenuKey = Symbol('registerHeaderMenu')
@@ -0,0 +1,3 @@
1
+ export const locales = {
2
+ homeAriaLabel: 'Logo de l\'Assurance Maladie, cliquez pour revenir à l\'accueil',
3
+ }
@@ -0,0 +1,210 @@
1
+ import { vuetify } from '@tests/unit/setup'
2
+ import { mount } from '@vue/test-utils'
3
+ import { afterEach, describe, expect, it, vi } from 'vitest'
4
+ import { defineComponent, inject, onMounted, ref, type Ref } from 'vue'
5
+ import HeaderBar from '../HeaderBar.vue'
6
+ import { registerHeaderMenuKey } from '../consts'
7
+
8
+ describe('HeaderBar', () => {
9
+ afterEach(() => {
10
+ vi.restoreAllMocks()
11
+ document.body.innerHTML = ''
12
+ })
13
+
14
+ it('should render the component', async () => {
15
+ const wrapper = mount(HeaderBar, {
16
+ props: {
17
+ sticky: false,
18
+ },
19
+ global: {
20
+ plugins: [vuetify],
21
+ },
22
+ attachTo: document.body,
23
+ })
24
+ window.dispatchEvent(new CustomEvent('scroll'))
25
+
26
+ expect(wrapper.html()).toMatchSnapshot()
27
+
28
+ // mock scroll related values
29
+ vi.spyOn(window, 'scrollY', 'get').mockReturnValue(2000)
30
+ const header = wrapper.find('.header')
31
+ vi.spyOn(header.element, 'getBoundingClientRect').mockReturnValue({
32
+ top: -2000,
33
+ } as DOMRect)
34
+ window.dispatchEvent(new CustomEvent('scroll'))
35
+
36
+ await wrapper.vm.$nextTick()
37
+
38
+ const stickyHeader = wrapper.find('.sticky-header')
39
+ const stickyHeaderStyle = stickyHeader.attributes('style')
40
+ expect(stickyHeaderStyle).toContain('position: relative;')
41
+ expect(stickyHeaderStyle).toContain('top: auto;')
42
+
43
+ wrapper.unmount()
44
+ })
45
+
46
+ it('should render all the component slots', async () => {
47
+ const wrapper = mount(HeaderBar, {
48
+ global: {
49
+ plugins: [vuetify],
50
+ },
51
+ attachTo: document.body,
52
+ slots: {
53
+ 'default': '<div>Default slot</div>',
54
+ 'prepend': '<div>Prepend slot</div>',
55
+ 'append': '<div>Append slot</div>',
56
+ 'menu': '<div>Menu slot</div>',
57
+ 'logo': '<div>Logo slot</div>',
58
+ 'header-side': '<div>Header side slot</div>',
59
+ },
60
+ })
61
+
62
+ const text = wrapper.text()
63
+
64
+ expect(text).not.toContain('Default slot')
65
+ expect(text).toContain('Prepend slot')
66
+ expect(text).toContain('Append slot')
67
+ expect(text).toContain('Menu slot')
68
+ expect(text).toContain('Logo slot')
69
+ expect(text).toContain('Header side slot')
70
+
71
+ wrapper.unmount()
72
+ })
73
+
74
+ const TestMenu = defineComponent({
75
+ setup() {
76
+ const menu = ref(false)
77
+ const registerHeaderMenu = inject<(r: Ref<boolean>) => void>(registerHeaderMenuKey)
78
+
79
+ onMounted(() => {
80
+ if (registerHeaderMenu) {
81
+ registerHeaderMenu(menu)
82
+ }
83
+ })
84
+
85
+ return {
86
+ menu,
87
+ }
88
+ },
89
+ template: '<button @click="menu = true">Menu</button>',
90
+ })
91
+
92
+ it('should render the menu slot', async () => {
93
+ const wrapper = mount({
94
+ components: {
95
+ HeaderBar,
96
+ TestMenu,
97
+ },
98
+ template: `
99
+ <HeaderBar>
100
+ <template #menu>
101
+ <TestMenu />
102
+ </template>
103
+ <template #logo="{menuOpen}">
104
+ <div>{{menuOpen ? 'the menu is open' : 'the menu is closed'}}</div>
105
+ </template>
106
+ </HeaderBar>`,
107
+ }, {
108
+ global: {
109
+ plugins: [vuetify],
110
+ },
111
+ attachTo: document.body,
112
+ })
113
+ const text = wrapper.text()
114
+ expect(text).toContain('Menu')
115
+ expect(text).toContain('the menu is closed')
116
+
117
+ const button = wrapper.find('button')
118
+ await button.trigger('click')
119
+
120
+ expect(wrapper.text()).toContain('the menu is open')
121
+
122
+ wrapper.unmount()
123
+ })
124
+
125
+ it('should render in sticky mode', async () => {
126
+ const wrapper = mount(HeaderBar, {
127
+ props: {
128
+ sticky: true,
129
+ },
130
+ global: {
131
+ plugins: [vuetify],
132
+ },
133
+ attachTo: document.body,
134
+ })
135
+ window.dispatchEvent(new CustomEvent('scroll'))
136
+
137
+ // mock scroll related values
138
+ vi.spyOn(window, 'scrollY', 'get').mockReturnValue(2000)
139
+ const header = wrapper.find('.header')
140
+ vi.spyOn(header.element, 'getBoundingClientRect').mockReturnValue({
141
+ top: -2000,
142
+ } as DOMRect)
143
+ window.dispatchEvent(new CustomEvent('scroll'))
144
+
145
+ await wrapper.vm.$nextTick()
146
+
147
+ const stickyHeader = wrapper.find('.sticky-header')
148
+ const stickyHeaderStyle = stickyHeader.attributes('style')
149
+ expect(stickyHeaderStyle).toContain('position: fixed;')
150
+ expect(stickyHeaderStyle).toContain('top: 0px;')
151
+
152
+ wrapper.unmount()
153
+ })
154
+
155
+ it('should only show the header when the user scrolls up', async () => {
156
+ // @ts-expect-error - Property 'happyDOM' does not exist on type 'Window & typeof globalThis'.
157
+ window.happyDOM.setInnerWidth(600)
158
+ const wrapper = mount(HeaderBar, {
159
+ props: {
160
+ hideWhenDown: true,
161
+ },
162
+ global: {
163
+ plugins: [vuetify],
164
+ },
165
+ attachTo: document.body,
166
+ })
167
+ window.dispatchEvent(new CustomEvent('scroll'))
168
+
169
+ const header = wrapper.find('.header')
170
+ const mockHeaderRect = vi.spyOn(header.element, 'getBoundingClientRect')
171
+
172
+ // mock scroll related values
173
+ const mockScrollY = vi.spyOn(window, 'scrollY', 'get')
174
+ const mockScrollTop = vi.spyOn(document.documentElement, 'scrollTop', 'get')
175
+ mockScrollY.mockReturnValue(2000)
176
+ mockScrollTop.mockReturnValue(2000)
177
+ mockHeaderRect.mockReturnValue({
178
+ top: -2000,
179
+ height: 77,
180
+ } as DOMRect)
181
+ window.dispatchEvent(new CustomEvent('scroll'))
182
+ await wrapper.vm.$nextTick()
183
+
184
+ const stickyHeader = wrapper.find('.sticky-header')
185
+ let stickyHeaderStyle = stickyHeader.attributes('style')
186
+
187
+ // Do not show the header when the user scrolls down
188
+ expect(stickyHeaderStyle).toContain('position: fixed;')
189
+ expect(stickyHeaderStyle).toContain('top: 0px;')
190
+ expect(stickyHeaderStyle).toContain('transform: translateY(-100%);')
191
+
192
+ mockScrollY.mockReturnValue(800)
193
+ mockScrollTop.mockReturnValue(800)
194
+ mockHeaderRect.mockReturnValue({
195
+ top: -800,
196
+ height: 77,
197
+ } as DOMRect)
198
+ window.dispatchEvent(new CustomEvent('scroll'))
199
+ await wrapper.vm.$nextTick()
200
+
201
+ stickyHeaderStyle = stickyHeader.attributes('style')
202
+
203
+ // Do show the header when the user scrolls up
204
+ expect(stickyHeaderStyle).toContain('position: fixed;')
205
+ expect(stickyHeaderStyle).toContain('top: 0px;')
206
+ expect(stickyHeaderStyle).toContain('transform: none;')
207
+
208
+ wrapper.unmount()
209
+ })
210
+ })
@@ -0,0 +1,50 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`HeaderBar > should render the component 1`] = `
4
+ "<header data-v-efdbe191="" class="header" style="min-height: auto;">
5
+ <div data-v-efdbe191="" class="sticky-header" style="position: relative; top: auto; transform: none; transition: none;">
6
+ <!--v-if-->
7
+ <div data-v-efdbe191="" class="inner-header d-flex">
8
+ <div data-v-efdbe191="" class="header-logo">
9
+ <div data-v-a3ce97de="" data-v-efdbe191="" class="logo"><svg data-v-d1ac2910="" data-v-a3ce97de="" fill="#0c419a" width="141" height="42" viewBox="0 0 211 64" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" aria-label="Logo de l'Assurance Maladie, cliquez pour revenir à l'accueil">
10
+ <g data-v-d1ac2910="">
11
+ <path d="M100.1 45V30.6h3.8v14.6c0 .8.4 1 .7 1h.5l.5 2.9a5 5 0 0 1-2 .3c-2.6 0-3.5-1.7-3.5-4.4Z" data-v-d1ac2910=""></path>
12
+ <path d="m75.7 40.4 3.8-8.5h4V49h-3.6V38.4l-3 7.8h-2.4l-3-7.8v10.7H68V32h4l3.8 8.5Z" data-v-d1ac2910=""></path>
13
+ <path d="M107.1 45.5c0-2.8 2.3-4.3 7.4-4.9 0-1.1-.8-1.8-2.2-1.8-1 0-2.1.4-3.4 1.2l-1.3-2.6c1.6-1 3.5-1.7 5.5-1.7 3.3 0 5 1.9 5 5.9V49h-3.5v-1.3s-1.5 1.6-3.6 1.6c-2.4 0-3.9-1.7-3.9-4Zm7.4-.3V43c-2.7.3-3.7 1.2-3.7 2.2 0 .8.5 1.2 1.5 1.2.8 0 1.5-.5 2.2-1.2Z" data-v-d1ac2910=""></path>
14
+ <path d="M86.1 45.5c0-2.8 2.3-4.3 7.4-4.9 0-1.1-.8-1.8-2.2-1.8-1 0-2.1.4-3.4 1.2l-1.3-2.6c1.6-1 3.5-1.7 5.5-1.7 3.3 0 5 1.9 5 5.9V49h-3.5v-1.3S92 49.4 90 49.4c-2.4 0-3.9-1.7-3.9-4Zm7.4-.3V43c-2.7.3-3.7 1.2-3.7 2.2 0 .8.6 1.2 1.5 1.2.8 0 1.6-.5 2.2-1.2Z" data-v-d1ac2910=""></path>
15
+ <path d="M129 30.6v6.3a4.1 4.1 0 0 0-3.2-1.2c-2.7 0-5.3 2.6-5.3 6.8 0 4.3 2 7 5.2 7 1.8 0 3.1-1.4 3.2-1.5v1.1h3.8V30.6H129Zm-2.3 15.7c-1.5 0-2.4-1.2-2.4-3.8 0-2.5 1.1-3.6 2.4-3.6.6 0 1.4.2 2.2.8v5.4c-.7.8-1.4 1.2-2.2 1.2Z" data-v-d1ac2910=""></path>
16
+ <path d="M135.7 32c0-1.2 1-2 2.2-2 1.2 0 2.2.8 2.2 2s-1 2-2.2 2c-1.3 0-2.2-.8-2.2-2Zm.3 4h3.8V49H136V36Z" data-v-d1ac2910=""></path>
17
+ <path d="M142.2 42.5c0-4.2 2.6-6.8 5.9-6.8 3.6 0 5.6 2.7 5.6 6.3l-.1 1.7h-7.7c.3 2 1.6 2.8 3.3 2.8 1 0 1.8-.4 2.8-1l1.3 2.5c-1.3.9-3 1.4-4.6 1.4-3.8 0-6.5-2.5-6.5-6.9Zm8.3-1.4c0-1.4-.9-2.5-2.4-2.5-1.2 0-2 .9-2.3 2.5h4.7Z" data-v-d1ac2910=""></path>
18
+ <path d="M68 23.3V8.8h3.7v14.7c0 .8.4 1 .7 1h.5l.4 2.9a5 5 0 0 1-1.9.3c-2.6 0-3.5-1.7-3.5-4.4Z" data-v-d1ac2910=""></path>
19
+ <path d="m88.1 23.3 1 4.1h4l-5.3-17.3h-4.4l-5.3 17.3h4l1-4.1h5Zm-4.2-3 1.6-6.6h.2l1.6 6.5H84Z" data-v-d1ac2910=""></path>
20
+ <path d="m93.9 25.9 1.7-2.4c1.1.9 2.2 1.3 3.3 1.3 1.1 0 1.6-.4 1.6-1 0-1-1.2-1.4-2.6-1.9-1.6-.6-3.4-1.7-3.4-3.8 0-2.5 2-4.2 5-4.2 1.9 0 3.4.8 4.5 1.7l-1.7 2.4c-1-.7-1.8-1.2-2.8-1.2-1 0-1.4.4-1.4 1 0 1 1.2 1.2 2.5 1.7 1.6.7 3.5 1.6 3.5 4s-1.9 4.2-5.3 4.2c-1.7 0-3.6-.7-5-1.8Z" data-v-d1ac2910=""></path>
21
+ <path d="m105.3 25.9 1.7-2.4c1.1.9 2.2 1.3 3.3 1.3 1.1 0 1.6-.4 1.6-1 0-1-1.2-1.4-2.6-1.9-1.6-.6-3.4-1.7-3.4-3.8 0-2.5 2-4.2 5-4.2 1.9 0 3.4.8 4.5 1.7l-1.7 2.4c-1-.7-1.8-1.2-2.8-1.2-1 0-1.4.4-1.4 1 0 1 1.2 1.2 2.5 1.7 1.6.7 3.5 1.6 3.5 4s-1.9 4.2-5.3 4.2c-1.7 0-3.6-.7-5-1.8Z" data-v-d1ac2910=""></path>
22
+ <path d="M132.2 14.3h3.8V16c.8-1.4 2.5-2 3.4-2 .8 0 1.2.2 1.6.3l-.4 3.6c-.5-.2-1.1-.4-1.7-.4-1.5 0-2.8 1.3-2.9 3v6.8h-3.8V14.3Z" data-v-d1ac2910=""></path>
23
+ <path d="M155.7 14.3h3.8v1.6c1-1 2.2-2 4-2 2.7 0 4 2 4 5.3v8.2h-3.9v-7.7c0-1.9-.5-2.5-1.6-2.5-1 0-1.6.5-2.5 1.3v8.8h-3.8v-13Z" data-v-d1ac2910=""></path>
24
+ <path d="M180.6 20.8c0-4.3 2.7-6.9 5.9-6.9 3.7 0 5.7 2.8 5.7 6.3 0 .7 0 1.4-.2 1.8h-7.7c.4 1.9 1.7 2.8 3.4 2.8 1 0 1.8-.4 2.7-1l1.3 2.4c-1.3 1-3 1.5-4.6 1.5-3.7 0-6.5-2.5-6.5-6.9Zm8.4-1.4c0-1.4-.9-2.5-2.4-2.5-1.2 0-2 .8-2.3 2.5h4.7Z" data-v-d1ac2910=""></path>
25
+ <path d="M141.8 23.7c0-2.7 2.3-4.3 7.4-4.8 0-1.2-.8-1.9-2.2-1.9a7 7 0 0 0-3.4 1.2l-1.3-2.5c1.6-1 3.4-1.8 5.5-1.8 3.3 0 5 2 5 6v7.5h-3.6V26s-1.4 1.6-3.5 1.6c-2.4 0-4-1.7-4-4Zm7.4-.2v-2.3c-2.7.4-3.8 1.3-3.8 2.3 0 .8.6 1.2 1.5 1.2s1.7-.5 2.3-1.2Z" data-v-d1ac2910=""></path>
26
+ <path d="M76.7 8.8c-.8 0-1.9.6-1.9 1.7 0 1 .8 1.5 1.6 1.5 0 1.2-1 1.8-1.5 2.1l1 1.4c3.3-1 4-6.7.8-6.7Z" data-v-d1ac2910=""></path>
27
+ <path d="M125.3 14.3V23c-.8 1-1.4 1.4-2.3 1.4-1 0-1.6-.6-1.6-2.5v-7.6h-3.8v8.3c0 3 1.4 5.1 4 5.1 2.5 0 3.8-1.6 3.8-1.6v1.3h3.7V14.3h-3.8Z" data-v-d1ac2910=""></path>
28
+ <path d="M176.3 24.6c-1.7 0-3-1.5-3-3.7 0-2.3 1.3-3.8 3-3.8.6 0 1.1.2 1.7.7l1.8-2.5c-.9-.8-2.1-1.3-3.7-1.3a6.5 6.5 0 0 0-6.6 6.9c0 4.3 2.7 6.8 6.3 6.8 1.4 0 3-.4 4.2-1.5l-1.4-2.7c-.7.6-1.5 1-2.3 1Z" data-v-d1ac2910=""></path>
29
+ </g>
30
+ <path d="M34.8 9c-.1-.2-.4-.3-.6-.3H34c-.2-.2-5.3 0-6.5.7 0-.8-.4-1.8-1.7-2-1-.3-3.1.7-2.4 2.8 0 .3.3.6.7.8.2 0 .5 0 .7.6h.3l.2.2h.5c.4.4.8-.1 1-.5l.7 2c-.2-.2-1.8-.6-2.5.2s-1.2 1.8-1.7 3.5c-.2.4-.5.7-1 .9-.9.3-.4.7-.2.7a6 6 0 0 0 2 0c.4-.2.1-1 .5-1.4l.8-1c0 .5 0 .8-.3 1.5-.2.7-1 1-1.3 1.3-.4.5.6.5.8.5.7-.2 1-.2 1.3-.4.4-.2.2-.8.3-1 .3-.7 1.4-2 1.6-2.3.8.2 1.7.2 2 .2 1.9-.2 2.3-1.5 2-2.6-.1-1-.7-1.5-.9-1.8l-.9-1.4 3.5-.9c0 .3.3.3.4.4.2.1.3 0 .2-.1a.6.6 0 0 1-.2-.2h.7c.3 0 .4-.4.3-.5Z" data-v-d1ac2910=""></path>
31
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M61 26.7c-.5-2.6-2.9-4.1-4.1-4.7l-1.4-.4-.4-.2c.1-.5.8-.5 1.2-1.3 1-2.3-1.2-3.8-2.8-3.5-.8.2-1.3.4-1.5 1 0 .4-.3.4-.7.8-.2.2.1.4.2.5l-.1.3v.5s-.3.3 0 .7l.3.8c0 .1 0 .2-.3 0l-3.4-1a37.4 37.4 0 0 1-4.2-4.7c-.7 0 .5 1.3-.1.8-.2 0-.7-.4-.8-.5l-.1-.2-.3-.3H42.2c-.2 0-.1.5 0 .7 0 0 .4 1.2.6 1.4l1.5 1 2.6 3c.7.7 3.8 2.5 3.9 2.6.2 0 .5.8 0 5.5-.1 1.8-1.4 5.6-.8 7.5.4 1.3.9 2.2 1.3 5.6.1.8-.5 1.3-1.3 2l-1.4.8c-.2.2 0 .4.2.5.2 0 .5.3.8.3.3.1.9 0 1.4-.3.7-.3.7-.4 1-.6.5-.3 1-.3 1.2-.8.2-.6-.3-1.4-.3-1.7.4-2.8.9-4.1.2-5.8-.2-.3 1-4 1-3.4.2.9.3 3.8.3 5.9 0 .9.8 4.2 1 5.4.4 1.8-2.6 3.2-2 3.8l1 .3 1-.1c.3-.1.6-.5.7-.6.6-.8.4-.6 1-1.1.4-.5 0-1.4 0-1.8v-2.9c0-1.9 0-2.4.2-3.2l1-6.2s.2 0 .3-.2c.2-.2.1-.5.2-.7 0-.2 0 0 0 0l.2.6-.2.8c-.2.4.1.4.2.5 0 0 1-.7 1.2-1l.1-.8-.2-1.8c.2-.9 1-3.2.8-3.8Zm-2.2 3.6c-.2.5-.2 0-.6 1.7V31.1c0-2.1-1.5-6.5-.6-5.6 0 0 1.2.8 1.4 1.4.2.5.1 1.8-.2 3.4Z" data-v-d1ac2910=""></path>
32
+ <path d="M20 35.3c-.3-.3-1.6-2-2.4-2.7l-1-1.5c-1.6-2-1.9-2.1-3-2.2-.1-.2 0-.3 0-.4 1.3-.5.9-1.6.8-1.7l.2-.6-.2-1-.1-.7c-.8-.9-2.6-.9-3.5-.3-.8.5-.3.9-.6 1.6-.1.5 0 .9.3 1.2 0 .3.8.7.7 1.2-.2.6-1 .7-1 .7l-.3.1c-1.4.8-2.7 2-2.9 3.2 0 1 1.7 2.5 2.7 3.3.1 1.7 1.2 4.4 1.3 4.5.1.3-.3-.3-1.4-.7-.6-.1-1.2-.9-1.4 0 0 .4 0 1-.3 1.8-.2.6 0 1.5.7 1.5.5 0 .8-1.4.8-1.4s2.3 1 3.3.7c.7-.2.8-2.7.2-5-.2-.8.4 1.3 2 3.1.2.4 0 .7.2 2.2v1c0 .7-.7 1.6.9 2 1.6.4 1.5.8 2.5.1.5-.3-1.4-.5-1.8-1.6v-.2c0-.9.3-2.9.2-3.6-.6-3.4-2-5-2.2-5.8 0-2.4-.2-3.2 0-2.6l1 1.5.3.3.4.4 2.8 2.1s.1 1 .6 1c.4 0 2.3-1 2.2-1.1 0-.3-2-.3-2-.4ZM9.7 34l-1-1.8c.4-.7 1-1 1-.9.2 0 0 2.7 0 2.7Z" data-v-d1ac2910=""></path>
33
+ <path d="M51.8 54.8c-.1 0-1.2-.5-2.2-2-.5-.8-3.8-7-4.3-8.2-.9-2.4-8-6.7-8.2-7-.2-.1-.2-.5-.1-.8.5-2 .3-3.8.4-4.1.3-.7.8-1.7.4-2.3l-.7-1.2s3.7 1.4 4.6 1.2c.8-.2 4.5-2.8 4.7-2.9.5-.2 1.8.1 3-1.3.5-.5.2-.6 0-.6.1 0-.1-.2-.4 0-.2.1-1.3.4-1.1.2.8-.5.2-.7.2-.7-.3.3-1.8.7-2.1 1l-.8.5c-.9.5-2.4 1.3-4 1.7-.3 0-3.3-2-5-2.2-1-.1-1.3 0-1.8.1-.6.2-.6-.7 0-1 .2 0 .6-.1.7-.4.2-.4 0-.7 0-.8v-.2l-.1-.1v-.5c.3-.2.2-.4.1-.4-.1-.1-.6-.4-.7-.7 0-.6-.3-.9-.3-1.2 0 0-.1-.8-.4-1-.4-.4-1.2-.8-2.8-.3-.6.1-.5.4-1.8.6-1.6.3-2.4 2.7-2.3 3.7.1.8.8.4.8.7 0 .6-.1 1.5 1 1.8.3 0 1.8-.2 2.4 0 0 0 .5 0 .2.4-.1.2-1.6.9-2 1-1 .4-3.6 4.6-4.3 5-.7.3-1 .6-1.3.8-1 .6-2.1 1.4-2.7 1.4-.7 0-1.4.3-1.9 1.5-.1.3 0 .5.3.5 0 .1.2.3.4.3.2.2.5 0 .7 0l1-.7c.5-.2 4-1.5 4.7-2.1 1.2-.6 3.5-3 4-3s1.6 1 1.2 3c0 .7-1 2-1.4 2.6-1 1.5-.2 2.7-.4 3-1 1.6-2.2 3.7-4.2 5.5-.8.6-2.4.7-3.3 1-2 .5-4.2 2-5.7 2.8-.5.3-1-.3-1.6-.4-.7 0-.7 1-1 1.8l-.7 1.5c-.1.4-.7 1.7-.1 2.2.4.7 1.5-1.4 2.4-2.3.4-.5.7-.7 1.7-1 .6 0 5.6-1.4 8.8-2.2h.3c4.2-1.1 8.6-6.6 8.7-6.6l7.2 3.5c.7.4.7 1.8 1.5 3.4a22 22 0 0 0 4.4 4.9c.4.4 0 1 .2 1.6.2.6.6.6 1.1.6l1.6-.2c.7 0 .5 0 1.3.2.4 0 1.3.1 2-.5 1-1.2-1.2-.6-2.3-1.1Z" data-v-d1ac2910=""></path>
34
+ <path d="M44 51.7a.4.4 0 0 0-.6-.2 22.2 22.2 0 0 1-21-.6.5.5 0 0 0-.6.2c-.1.2 0 .4.1.6a23 23 0 0 0 21.9.5c.2 0 .3-.3.2-.5Z" data-v-d1ac2910=""></path>
35
+ <path d="M12.9 22.9h-.2c-.2-.2-.3-.4-.2-.6 2-4.4 5.2-8 9.3-10.4.2-.1.5 0 .6.2.2.2 0 .4-.1.6-4 2.3-7.1 5.7-9 10l-.4.2Z" data-v-d1ac2910=""></path>
36
+ <path d="M50.3 17.3c-.1 0-.3 0-.3-.2A22 22 0 0 0 36.5 10c-.2 0-.4-.3-.3-.5 0-.3.2-.4.5-.4a23 23 0 0 1 14 7.5v.7h-.4Z" data-v-d1ac2910=""></path>
37
+ </svg>
38
+ <!--v-if-->
39
+ <div data-v-a3ce97de="" if="serviceTitle">
40
+ <div data-v-a3ce97de="" class="service-title"></div>
41
+ <!--v-if-->
42
+ </div>
43
+ </div>
44
+ </div>
45
+ <!--v-if-->
46
+ </div>
47
+ <!--v-if-->
48
+ </div>
49
+ </header>"
50
+ `;
@@ -0,0 +1,26 @@
1
+ import { it, describe, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import useHeaderResponsiveMode from '../useHeaderResponsiveMode'
4
+
5
+ describe('useHeaderResponsiveMode', () => {
6
+ it('should return the correct responsive mode', async () => {
7
+ // @ts-expect-error - Property 'happyDOM' does not exist on type 'Window & typeof globalThis'.
8
+ window.happyDOM.setInnerWidth(600)
9
+ const wrapper = mount({
10
+ template: `<div :class="{'is-desktop': isDesktop}"></div>`,
11
+ setup() {
12
+ const { isDesktop } = useHeaderResponsiveMode()
13
+ return { isDesktop }
14
+ },
15
+ })
16
+
17
+ expect(wrapper.find('.is-desktop').exists()).toBe(false)
18
+
19
+ // @ts-expect-error - Property 'happyDOM' does not exist on type 'Window & typeof globalThis'.
20
+ window.happyDOM.setInnerWidth(1200)
21
+ await wrapper.vm.$nextTick()
22
+ expect(wrapper.find('.is-desktop').exists()).toBe(true)
23
+
24
+ wrapper.unmount()
25
+ })
26
+ })
@@ -0,0 +1,34 @@
1
+ import { it, describe, expect, vi } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import useScrollDirection from '../useScrollDirection'
4
+
5
+ describe('useScrollDirection', () => {
6
+ it('should return the correct scroll direction', async () => {
7
+ const mockScrollTop = vi.spyOn(document.documentElement, 'scrollTop', 'get')
8
+
9
+ const wrapper = mount({
10
+ template: `<div :class="scrollDirection"></div>`,
11
+ setup() {
12
+ const { scrollDirection } = useScrollDirection()
13
+ return { scrollDirection }
14
+ },
15
+ })
16
+ mockScrollTop.mockReturnValue(0)
17
+ window.dispatchEvent(new CustomEvent('scroll'))
18
+
19
+ expect(wrapper.find('div').classes()).toEqual([])
20
+
21
+ mockScrollTop.mockReturnValue(100)
22
+ window.dispatchEvent(new CustomEvent('scroll'))
23
+ await wrapper.vm.$nextTick()
24
+ expect(wrapper.find('div').classes()).toEqual(['bottom'])
25
+
26
+ mockScrollTop.mockReturnValue(50)
27
+ window.dispatchEvent(new CustomEvent('scroll'))
28
+ await wrapper.vm.$nextTick()
29
+ expect(wrapper.find('div').classes()).toEqual(['top'])
30
+
31
+ vi.clearAllMocks()
32
+ wrapper.unmount()
33
+ })
34
+ })
@@ -0,0 +1,25 @@
1
+ import { ref, onMounted, onUnmounted } from 'vue'
2
+ import { headerBreakpoint } from './consts'
3
+
4
+ export default function useIsDesktop() {
5
+ const isDesktop = ref(false)
6
+
7
+ let mediaQuery: MediaQueryList
8
+
9
+ function handleChange(e: MediaQueryListEvent) {
10
+ isDesktop.value = e.matches
11
+ }
12
+
13
+ onMounted(() => {
14
+ mediaQuery = window.matchMedia(`(min-width: ${headerBreakpoint}px)`)
15
+ isDesktop.value = mediaQuery.matches
16
+
17
+ mediaQuery.addEventListener('change', handleChange)
18
+ })
19
+
20
+ onUnmounted(() => {
21
+ mediaQuery.removeEventListener('change', handleChange)
22
+ })
23
+
24
+ return { isDesktop }
25
+ }
@@ -0,0 +1,26 @@
1
+ import { onMounted, onUnmounted, ref } from 'vue'
2
+
3
+ export default function useScrollDirection() {
4
+ const lastYPos = ref<number | null>(null)
5
+ const scrollDirection = ref<'' | 'top' | 'bottom'>('')
6
+
7
+ function handleScroll() {
8
+ const yPos = document.documentElement.scrollTop
9
+ if (lastYPos.value === null) {
10
+ lastYPos.value = yPos
11
+ return ''
12
+ }
13
+ scrollDirection.value = yPos >= lastYPos.value ? 'bottom' : 'top'
14
+ lastYPos.value = yPos
15
+ }
16
+
17
+ onMounted(() => {
18
+ window.addEventListener('scroll', handleScroll)
19
+ })
20
+
21
+ onUnmounted(() => {
22
+ window.removeEventListener('scroll', handleScroll)
23
+ })
24
+
25
+ return { scrollDirection }
26
+ }
@@ -1,13 +1,11 @@
1
1
  <script setup lang="ts">
2
- import { ref, computed, getCurrentInstance, watch } from 'vue'
3
- import { mdiClose, mdiInformationOutline, mdiCheckCircleOutline, mdiAlertCircleOutline, mdiAlertOctagonOutline } from '@mdi/js'
4
- import { useDisplay } from 'vuetify'
5
- import { useNotificationService } from '@/services/NotificationService'
6
- import { type Notification } from './types'
7
- import { VSnackbar, VIcon, VBtn } from 'vuetify/components'
8
-
9
2
  import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
3
+ import { useNotificationService } from '@/services/NotificationService'
4
+ import { mdiAlertCircleOutline, mdiAlertOctagonOutline, mdiCheckCircleOutline, mdiClose, mdiInformationOutline } from '@mdi/js'
5
+ import { computed, getCurrentInstance, ref, watch } from 'vue'
6
+ import { useDisplay } from 'vuetify'
10
7
  import defaultOptions from './options'
8
+ import { type Notification } from './types'
11
9
 
12
10
  const props = withDefaults(defineProps<CustomizableOptions & {
13
11
  closeBtnText?: string
@@ -1,6 +1,5 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
- import { VSheet } from 'vuetify/components'
4
3
  import { useDisplay } from 'vuetify'
5
4
 
6
5
  const props = withDefaults(defineProps<{