@cnamts/synapse 1.0.6 → 1.0.7
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/{DateFilter-BlOpwEVq.js → DateFilter-CHDLz2EO.js} +1 -1
- package/dist/{NumberFilter-BPUXE4wY.js → NumberFilter-DXNQ4Uls.js} +1 -1
- package/dist/{PeriodFilter-B2yx329_.js → PeriodFilter-C8Qf3Jcn.js} +1 -1
- package/dist/{SelectFilter-CedKn1oV.js → SelectFilter-B2Ejs4Cb.js} +1 -1
- package/dist/{TextFilter-DkhJjRtR.js → TextFilter-CfR5_A1S.js} +1 -1
- package/dist/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.d.ts +116 -0
- package/dist/components/Amelipro/AmeliproAccordionGroup/types.d.ts +4 -0
- package/dist/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.d.ts +220 -0
- package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.d.ts +68 -0
- package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/AmeliproAccordionResultTemplate.d.ts +70 -0
- package/dist/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.d.ts +204 -0
- package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +26 -26
- package/dist/components/Amelipro/AmeliproBadge/AmeliproBadge.d.ts +59 -0
- package/dist/components/Amelipro/AmeliproBtn/AmeliproBtn.d.ts +3 -3
- package/dist/components/Amelipro/AmeliproCaptcha/AmeliproCaptcha.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproCarousel/AmeliproCarousel.d.ts +214 -0
- package/dist/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/AmeliproCarouselItem.d.ts +70 -0
- package/dist/components/Amelipro/AmeliproCarousel/types.d.ts +7 -0
- package/dist/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.d.ts +125 -0
- package/dist/components/Amelipro/AmeliproIconBtn/AmeliproIconBtn.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproIllustratedDataTile/AmeliproIllustratedDataTile.d.ts +2 -2
- package/dist/components/Amelipro/AmeliproResultList/AmeliproResultList.d.ts +164 -0
- package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +27 -27
- package/dist/components/Amelipro/AmeliproStateTile/AmeliproStateTile.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproTable/AmeliproTable.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +32 -32
- package/dist/components/Amelipro/AmeliproTextArea/AmeliproTextArea.d.ts +6 -6
- package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +7 -7
- package/dist/components/Amelipro/AmeliproTileBtn/AmeliproTileBtn.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproTooltips/AmeliproTooltips.d.ts +2 -2
- package/dist/components/ChipList/ChipList.d.ts +4 -0
- package/dist/components/ChipList/locales.d.ts +4 -2
- package/dist/components/CookiesSelection/CookiesInformation/CookiesInformation.d.ts +8 -8
- package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +445 -8
- package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +2 -0
- package/dist/components/Customs/SyTabs/SyTabs.d.ts +71 -0
- package/dist/components/Customs/SyTabs/config.d.ts +17 -0
- package/dist/components/Customs/SyTabs/types.d.ts +11 -0
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +9 -9
- package/dist/components/DataList/DataList.d.ts +1 -1
- package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +4811 -240
- package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +52 -33
- package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +23 -10
- package/dist/components/DatePicker/composables/useDateInputEditing.d.ts +1 -0
- package/dist/components/DatePicker/composables/useTodayButton.d.ts +1 -0
- package/dist/components/DialogBox/DialogBox.d.ts +219 -0
- package/dist/components/HeaderLoading/HeaderLoading.d.ts +27 -0
- package/dist/components/HeaderNavigationBar/HeaderNavigationBar.d.ts +110 -3
- package/dist/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.d.ts +19 -1
- package/dist/components/LangBtn/LangBtn.d.ts +2 -2
- package/dist/components/NirField/NirField.d.ts +18 -18
- package/dist/components/PeriodField/PeriodField.d.ts +10766 -1620
- package/dist/components/PhoneField/PhoneField.d.ts +1866 -2
- package/dist/components/PhoneField/indicatifs.d.ts +1 -0
- package/dist/components/PhoneField/locales.d.ts +1 -0
- package/dist/components/RangeField/RangeField.d.ts +1 -1
- package/dist/components/RangeField/RangeSlider/RangeSlider.d.ts +1 -1
- package/dist/components/SubHeader/SubHeader.d.ts +8 -0
- package/dist/components/SubHeader/locales.d.ts +1 -0
- package/dist/components/SyTextArea/SyTextArea.d.ts +6 -6
- package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +5 -4
- package/dist/components/Tables/SyTable/SyTable.d.ts +5 -4
- package/dist/components/Tables/common/SyTablePagination.d.ts +448 -7
- package/dist/components/Tables/common/organizeColumns/OrganizeColumns.d.ts +2 -2
- package/dist/components/Tables/common/types.d.ts +2 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/design-system-v3.js +173 -164
- package/dist/design-system-v3.umd.cjs +288 -261
- package/dist/{main-BXPFSAB4.js → main-C66C1BkG.js} +12984 -11291
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/assets/amelipro/icons.ts +38 -11
- package/src/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.mdx +20 -0
- package/src/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.stories.ts +135 -0
- package/src/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.vue +107 -0
- package/src/components/Amelipro/AmeliproAccordionGroup/__tests__/AmeliproAccordionGroup.spec.ts +37 -0
- package/src/components/Amelipro/AmeliproAccordionGroup/__tests__/__snapshots__/AmeliproAccordionGroup.spec.ts.snap +513 -0
- package/src/components/Amelipro/AmeliproAccordionGroup/types.d.ts +4 -0
- package/src/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.mdx +16 -0
- package/src/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.stories.ts +300 -0
- package/src/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.vue +288 -0
- package/src/components/Amelipro/AmeliproAccordionList/__tests__/AmeliproAccordionList.spec.ts +38 -0
- package/src/components/Amelipro/AmeliproAccordionList/__tests__/__snapshots__/AmeliproAccordionList.spec.ts.snap +1712 -0
- package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.mdx +19 -0
- package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.stories.ts +68 -0
- package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.vue +66 -0
- package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/AmeliproAccordionResultTemplate.vue +145 -0
- package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/__tests__/AmeliproAccordionResultTemplate.spec.ts +24 -0
- package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/__tests__/__snapshots__/AmeliproAccordionResultTemplate.spec.ts.snap +127 -0
- package/src/components/Amelipro/AmeliproAccordionResult/__tests__/AmeliproAccordionResult.spec.ts +24 -0
- package/src/components/Amelipro/AmeliproAccordionResult/__tests__/__snapshots__/AmeliproAccordionResult.spec.ts.snap +123 -0
- package/src/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.mdx +20 -0
- package/src/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.stories.ts +273 -0
- package/src/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.vue +275 -0
- package/src/components/Amelipro/AmeliproAccordionResultList/__tests__/AmeliproAccordionResultList.spec.ts +38 -0
- package/src/components/Amelipro/AmeliproAccordionResultList/__tests__/__snapshots__/AmeliproAccordionResultList.spec.ts.snap +1593 -0
- package/src/components/Amelipro/AmeliproBadge/AmeliproBadge.mdx +15 -0
- package/src/components/Amelipro/AmeliproBadge/AmeliproBadge.stories.ts +54 -0
- package/src/components/Amelipro/AmeliproBadge/AmeliproBadge.vue +76 -0
- package/src/components/Amelipro/AmeliproBadge/__tests__/AmeliproBadge.spec.ts +20 -0
- package/src/components/Amelipro/AmeliproBadge/__tests__/__snapshots__/AmeliproBadge.spec.ts.snap +19 -0
- package/src/components/Amelipro/AmeliproCarousel/AmeliproCarousel.mdx +15 -0
- package/src/components/Amelipro/AmeliproCarousel/AmeliproCarousel.stories.ts +191 -0
- package/src/components/Amelipro/AmeliproCarousel/AmeliproCarousel.vue +263 -0
- package/src/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/AmeliproCarouselItem.vue +93 -0
- package/src/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/__tests__/AmeliproCarouselItem.spec.ts +24 -0
- package/src/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/__tests__/__snapshots__/AmeliproCarouselItem.spec.ts.snap +43 -0
- package/src/components/Amelipro/AmeliproCarousel/__tests__/AmeliproCarousel.spec.ts +40 -0
- package/src/components/Amelipro/AmeliproCarousel/__tests__/__snapshots__/AmeliproCarousel.spec.ts.snap +342 -0
- package/src/components/Amelipro/AmeliproCarousel/types.d.ts +8 -0
- package/src/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.mdx +18 -0
- package/src/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.stories.ts +67 -0
- package/src/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.vue +233 -0
- package/src/components/Amelipro/AmeliproClickableTile/tests/AmeliproClickableTile.spec.ts +21 -0
- package/src/components/Amelipro/AmeliproClickableTile/tests/__snapshots__/AmeliproClickableTile.spec.ts.snap +140 -0
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeader.vue +7 -1
- package/src/components/Amelipro/AmeliproHeader/tests/__snapshots__/AmeliproHeader.spec.ts.snap +5 -4
- package/src/components/Amelipro/AmeliproIcon/iconList.ts +6 -0
- package/src/components/Amelipro/AmeliproPageLayout/tests/__snapshots__/AmeliproPageLayout.spec.ts.snap +5 -4
- package/src/components/Amelipro/AmeliproResultList/AmeliproResultList.mdx +15 -0
- package/src/components/Amelipro/AmeliproResultList/AmeliproResultList.stories.ts +264 -0
- package/src/components/Amelipro/AmeliproResultList/AmeliproResultList.vue +231 -0
- package/src/components/Amelipro/AmeliproResultList/__tests__/AmeliproResultList.spec.ts +37 -0
- package/src/components/Amelipro/AmeliproResultList/__tests__/__snapshots__/AmeliproResultList.spec.ts.snap +434 -0
- package/src/components/Amelipro/AmeliproTable/AmeliproTable.vue +6 -5
- package/src/components/Amelipro/AmeliproTable/__tests__/__snapshots__/AmeliproTable.spec.ts.snap +23 -26
- package/src/components/Amelipro/AmeliproTileBtn/AmeliproTileBtn.stories.ts +2 -2
- package/src/components/ChipList/Accessibilite.stories.ts +4 -0
- package/src/components/ChipList/ChipList.vue +185 -42
- package/src/components/ChipList/locales.ts +4 -2
- package/src/components/ChipList/tests/chipList.spec.ts +7 -4
- package/src/components/Customs/Selects/SelectOverview.mdx +18 -15
- package/src/components/Customs/Selects/SyBtnSelect/SyBtnSelect.stories.ts +10 -10
- package/src/components/Customs/Selects/SySelect/SySelect.stories.ts +13 -5
- package/src/components/Customs/Selects/SySelect/SySelect.vue +108 -37
- package/src/components/Customs/SyCheckbox/SyCheckbox.mdx +3 -1
- package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +165 -0
- package/src/components/Customs/SyCheckbox/SyCheckbox.vue +28 -9
- package/src/components/Customs/SyTabs/Accessibilite.mdx +309 -0
- package/src/components/Customs/SyTabs/SyTabs.mdx +117 -0
- package/src/components/Customs/SyTabs/SyTabs.stories.ts +354 -0
- package/src/components/Customs/SyTabs/SyTabs.vue +350 -0
- package/src/components/Customs/SyTabs/config.ts +17 -0
- package/src/components/Customs/SyTabs/tests/SyTabs.spec.ts +425 -0
- package/src/components/Customs/SyTabs/types.ts +12 -0
- package/src/components/Customs/SyTextField/SyTextField.mdx +3 -0
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +142 -1
- package/src/components/Customs/SyTextField/SyTextField.vue +19 -16
- package/src/components/DataList/DataList.vue +47 -49
- package/src/components/DataListGroup/DataListGroup.vue +1 -1
- package/src/components/DataListItem/DataListItem.vue +67 -63
- package/src/components/DataListItem/tests/DataListItem.spec.ts +2 -2
- package/src/components/DatePicker/CalendarMode/DatePicker.stories.ts +3 -3
- package/src/components/DatePicker/CalendarMode/DatePicker.vue +49 -13
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +412 -649
- package/src/components/DatePicker/DatePickerValidationExample/CalendarMode.stories.ts +215 -0
- package/src/components/DatePicker/DatePickerValidationExample/ComplexDatePicker.stories.ts +218 -0
- package/src/components/DatePicker/DatePickerValidationExample/DatePickerValidation.mdx +2 -0
- package/src/components/DatePicker/DatePickerValidationExample/DatePickerValidation.stories.ts +1 -1
- package/src/components/DatePicker/DatePickerValidationExample/DateTextInput.stories.ts +218 -0
- package/src/components/DatePicker/DatePickerValidationExample/MultiMode.stories.ts +281 -0
- package/src/components/DatePicker/DateTextInput/DateTextInput.events.spec.ts +17 -4
- package/src/components/DatePicker/DateTextInput/DateTextInput.range.spec.ts +111 -18
- package/src/components/DatePicker/DateTextInput/DateTextInput.spec.ts +238 -6
- package/src/components/DatePicker/DateTextInput/DateTextInput.vue +716 -757
- package/src/components/DatePicker/composables/tests/useDateInputEditing.spec.ts +4 -4
- package/src/components/DatePicker/composables/tests/useDisplayedDateString.spec.ts +17 -10
- package/src/components/DatePicker/composables/useDateInputEditing.ts +52 -22
- package/src/components/DatePicker/composables/useDisplayedDateString.ts +18 -4
- package/src/components/DatePicker/composables/useTodayButton.ts +13 -1
- package/src/components/DatePicker/utils/dateFormattingUtils.ts +79 -14
- package/src/components/DialogBox/DialogBox.stories.ts +12 -0
- package/src/components/DialogBox/DialogBox.vue +16 -11
- package/src/components/DialogBox/tests/DialogBox.spec.ts +22 -0
- package/src/components/HeaderLoading/Accessibilite.mdx +429 -8
- package/src/components/HeaderLoading/Accessibilite.stories.ts +4 -0
- package/src/components/HeaderLoading/HeaderLoading.vue +59 -0
- package/src/components/HeaderNavigationBar/HeaderNavigationBar.mdx +17 -2
- package/src/components/HeaderNavigationBar/HeaderNavigationBar.stories.ts +91 -2
- package/src/components/HeaderNavigationBar/HeaderNavigationBar.vue +37 -1
- package/src/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.vue +276 -21
- package/src/components/HeaderNavigationBar/tests/HeaderNavigationBar.spec.ts +2 -2
- package/src/components/NirField/NirField.mdx +3 -0
- package/src/components/NirField/NirField.vue +10 -1
- package/src/components/NirField/tests/NirField.spec.ts +81 -0
- package/src/components/PasswordField/PasswordField.mdx +3 -0
- package/src/components/PeriodField/PeriodField.mdx +2 -0
- package/src/components/PeriodField/PeriodField.stories.ts +195 -0
- package/src/components/PhoneField/Accessibilite.stories.ts +4 -0
- package/src/components/PhoneField/PhoneField.mdx +3 -1
- package/src/components/PhoneField/PhoneField.stories.ts +285 -1
- package/src/components/PhoneField/PhoneField.vue +228 -95
- package/src/components/PhoneField/indicatifs.ts +102 -102
- package/src/components/PhoneField/locales.ts +1 -0
- package/src/components/PhoneField/tests/PhoneField.spec.ts +429 -2
- package/src/components/SkipLink/SkipLink.vue +3 -31
- package/src/components/SkipLink/tests/skipLink.spec.ts +0 -21
- package/src/components/SubHeader/Accessibilite.stories.ts +8 -0
- package/src/components/SubHeader/SubHeader.mdx +1 -0
- package/src/components/SubHeader/SubHeader.stories.ts +179 -60
- package/src/components/SubHeader/SubHeader.vue +45 -15
- package/src/components/SubHeader/locales.ts +1 -0
- package/src/components/SyAlert/SyAlert.vue +6 -0
- package/src/components/Tables/SyServerTable/SyServerTable.mdx +3 -10
- package/src/components/Tables/SyServerTable/SyServerTable.stories.ts +242 -0
- package/src/components/Tables/SyServerTable/SyServerTable.vue +29 -10
- package/src/components/Tables/SyTable/SyTable.mdx +3 -10
- package/src/components/Tables/SyTable/SyTable.stories.ts +242 -0
- package/src/components/Tables/SyTable/SyTable.vue +2 -0
- package/src/components/Tables/common/SyTablePagination.vue +13 -6
- package/src/components/Tables/common/tests/SyTablePagination.spec.ts +157 -0
- package/src/components/Tables/common/types.ts +2 -0
- package/src/components/index.ts +9 -0
- package/src/composables/useFilterable/useFilterable.ts +10 -0
- package/src/designTokens/tokens/amelipro/apColors.ts +1 -1
- package/src/designTokens/tokens/cnam/cnamSemantic.ts +3 -3
- package/src/stories/Components/Components.stories.ts +1 -1
- package/src/stories/GuideDuDev/FormValidationGuide.mdx +342 -0
- package/src/stories/Templates/Templates.stories.ts +1 -1
- package/src/utils/functions/ameliproColors/ameliproColors.ts +1 -1
- package/dist/components/DataList/locales.d.ts +0 -3
- package/src/components/DataList/locales.ts +0 -3
- package/src/components/PhoneField/tests/PhoneField.additional.spec.ts +0 -266
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
|
|
3
|
+
import SyTabs from './SyTabs.vue'
|
|
4
|
+
import { ref } from 'vue'
|
|
5
|
+
|
|
6
|
+
// Plus d'informations sur la configuration de Storybook pour Vue:
|
|
7
|
+
// https://storybook.js.org/docs/vue/writing-stories/introduction
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `SyTabs` est un composant de navigation par onglets accessible et personnalisable.
|
|
11
|
+
* Il permet d'organiser le contenu en sections navigables facilement via une interface à onglets.
|
|
12
|
+
* Le composant implémente toutes les bonnes pratiques d'accessibilité ARIA et supporte la navigation complète au clavier.
|
|
13
|
+
*/
|
|
14
|
+
const meta = {
|
|
15
|
+
title: 'Composants/Navigation/SyTabs',
|
|
16
|
+
component: SyTabs,
|
|
17
|
+
parameters: {
|
|
18
|
+
layout: 'fullscreen',
|
|
19
|
+
controls: { exclude: ['confirmationMessage'] },
|
|
20
|
+
},
|
|
21
|
+
argTypes: {
|
|
22
|
+
items: {
|
|
23
|
+
description: 'Liste des éléments à afficher dans les onglets',
|
|
24
|
+
control: 'object',
|
|
25
|
+
},
|
|
26
|
+
modelValue: {
|
|
27
|
+
description: 'Index ou valeur de l\'onglet actuellement sélectionné (utilisé avec v-model)',
|
|
28
|
+
control: 'text',
|
|
29
|
+
},
|
|
30
|
+
confirmTabChange: {
|
|
31
|
+
description: 'Si activé, une confirmation sera demandée avant de changer d\'onglet',
|
|
32
|
+
control: 'boolean',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
args: {
|
|
36
|
+
items: [
|
|
37
|
+
{ label: 'Onglet 1', value: 'tab1', content: 'Contenu de l\'onglet 1' },
|
|
38
|
+
{ label: 'Onglet 2', value: 'tab2', content: 'Contenu de l\'onglet 2' },
|
|
39
|
+
{ label: 'Onglet 3', value: 'tab3', content: 'Contenu de l\'onglet 3' },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
} as Meta<typeof SyTabs>
|
|
43
|
+
|
|
44
|
+
export default meta
|
|
45
|
+
type Story = StoryObj<typeof meta>
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Exemple de base du composant SyTabs avec des onglets simples.
|
|
49
|
+
*/
|
|
50
|
+
export const Default: Story = {
|
|
51
|
+
args: {},
|
|
52
|
+
parameters: {
|
|
53
|
+
sourceCode: [
|
|
54
|
+
{
|
|
55
|
+
name: 'Template',
|
|
56
|
+
code: `<SyTabs :items="items" />`,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Script',
|
|
60
|
+
code: `
|
|
61
|
+
const items = [
|
|
62
|
+
{ label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
|
|
63
|
+
{ label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
|
|
64
|
+
{ label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" },
|
|
65
|
+
]
|
|
66
|
+
`,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Exemple avec v-model pour contrôler l'onglet actif de façon externe.
|
|
74
|
+
*/
|
|
75
|
+
export const WithVModel: Story = {
|
|
76
|
+
render: args => ({
|
|
77
|
+
components: { SyTabs },
|
|
78
|
+
setup() {
|
|
79
|
+
const activeTab = ref('tab2')
|
|
80
|
+
|
|
81
|
+
return { args, activeTab }
|
|
82
|
+
},
|
|
83
|
+
template: `
|
|
84
|
+
<div>
|
|
85
|
+
<div class="mb-4">
|
|
86
|
+
Onglet actif: {{ activeTab }}
|
|
87
|
+
<button
|
|
88
|
+
class="ml-4 px-2 py-1 bg-primary text-white rounded"
|
|
89
|
+
@click="activeTab = activeTab === 'tab1' ? 'tab2' : 'tab1'"
|
|
90
|
+
>
|
|
91
|
+
Changer d'onglet
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
<SyTabs v-model="activeTab" :items="args.items" />
|
|
95
|
+
</div>
|
|
96
|
+
`,
|
|
97
|
+
}),
|
|
98
|
+
parameters: {
|
|
99
|
+
sourceCode: [
|
|
100
|
+
{
|
|
101
|
+
name: 'Template',
|
|
102
|
+
code: `
|
|
103
|
+
<template>
|
|
104
|
+
<div>
|
|
105
|
+
<div class="mb-4">
|
|
106
|
+
Onglet actif: {{ activeTab }}
|
|
107
|
+
<button
|
|
108
|
+
class="ml-4 px-2 py-1 bg-primary text-white rounded"
|
|
109
|
+
@click="activeTab = activeTab === 'tab1' ? 'tab2' : 'tab1'"
|
|
110
|
+
>
|
|
111
|
+
Changer d'onglet
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
<SyTabs v-model="activeTab" :items="items" />
|
|
115
|
+
</div>
|
|
116
|
+
</template>`,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'Script',
|
|
120
|
+
code: `
|
|
121
|
+
<script setup>
|
|
122
|
+
import { ref } from 'vue'
|
|
123
|
+
|
|
124
|
+
const activeTab = ref('tab2')
|
|
125
|
+
const items = [
|
|
126
|
+
{ label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
|
|
127
|
+
{ label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
|
|
128
|
+
{ label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" }
|
|
129
|
+
]
|
|
130
|
+
</script>`,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Exemple avec slots personnalisés pour le contenu des onglets.
|
|
138
|
+
*/
|
|
139
|
+
export const WithCustomContent: Story = {
|
|
140
|
+
render: args => ({
|
|
141
|
+
components: { SyTabs },
|
|
142
|
+
setup() {
|
|
143
|
+
return { args }
|
|
144
|
+
},
|
|
145
|
+
template: `
|
|
146
|
+
<SyTabs :items="args.items">
|
|
147
|
+
<template #panel-0>
|
|
148
|
+
<div class="p-4 bg-info-light rounded">
|
|
149
|
+
<h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 1</h3>
|
|
150
|
+
<p>Vous pouvez utiliser des slots nommés <code>panel-{index}</code> pour personnaliser le contenu de chaque onglet.</p>
|
|
151
|
+
</div>
|
|
152
|
+
</template>
|
|
153
|
+
<template #panel-1>
|
|
154
|
+
<div class="p-4 bg-success-light rounded">
|
|
155
|
+
<h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 2</h3>
|
|
156
|
+
<p>Ce panneau utilise un style différent.</p>
|
|
157
|
+
</div>
|
|
158
|
+
</template>
|
|
159
|
+
</SyTabs>
|
|
160
|
+
`,
|
|
161
|
+
}),
|
|
162
|
+
parameters: {
|
|
163
|
+
sourceCode: [
|
|
164
|
+
{
|
|
165
|
+
name: 'Template',
|
|
166
|
+
code: `
|
|
167
|
+
<SyTabs :items="args.items">
|
|
168
|
+
<template #panel-0>
|
|
169
|
+
<div class="p-4 bg-info-light rounded">
|
|
170
|
+
<h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 1</h3>
|
|
171
|
+
<p>Vous pouvez utiliser des slots nommés <code>panel-{index}</code> pour personnaliser le contenu de chaque onglet.</p>
|
|
172
|
+
</div>
|
|
173
|
+
</template>
|
|
174
|
+
<template #panel-1>
|
|
175
|
+
<div class="p-4 bg-success-light rounded">
|
|
176
|
+
<h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 2</h3>
|
|
177
|
+
<p>Ce panneau utilise un style différent.</p>
|
|
178
|
+
</div>
|
|
179
|
+
</template>
|
|
180
|
+
</SyTabs>
|
|
181
|
+
`,
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'Script',
|
|
185
|
+
code: `
|
|
186
|
+
|
|
187
|
+
`,
|
|
188
|
+
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Exemple avec de nombreux onglets pour démontrer le comportement de scrolling.
|
|
196
|
+
*/
|
|
197
|
+
export const ManyTabs: Story = {
|
|
198
|
+
args: {
|
|
199
|
+
items: Array.from({ length: 10 }, (_, i) => ({
|
|
200
|
+
label: `Onglet ${i + 1}`,
|
|
201
|
+
value: `tab${i + 1}`,
|
|
202
|
+
content: `Contenu de l'onglet ${i + 1}`,
|
|
203
|
+
})),
|
|
204
|
+
},
|
|
205
|
+
parameters: {
|
|
206
|
+
sourceCode: [
|
|
207
|
+
{
|
|
208
|
+
name: 'Template',
|
|
209
|
+
code: `<SyTabs :items="items" />`,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'Script',
|
|
213
|
+
code: `
|
|
214
|
+
// Création d'un grand nombre d'onglets pour tester le comportement de défilement
|
|
215
|
+
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
216
|
+
label: \`Onglet \${i + 1}\`,
|
|
217
|
+
value: \`tab\${i + 1}\`,
|
|
218
|
+
content: \`Contenu de l'onglet \${i + 1}\`,
|
|
219
|
+
}))
|
|
220
|
+
`,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Exemple avec des options de personnalisation du thème.
|
|
228
|
+
*/
|
|
229
|
+
export const CustomTheme: Story = {
|
|
230
|
+
render: args => ({
|
|
231
|
+
components: { SyTabs },
|
|
232
|
+
setup() {
|
|
233
|
+
return { args }
|
|
234
|
+
},
|
|
235
|
+
template: `
|
|
236
|
+
<SyTabs
|
|
237
|
+
:items="args.items"
|
|
238
|
+
:vuetifyOptions="{
|
|
239
|
+
sheet: { theme: 'dark', color: '#0C419A' },
|
|
240
|
+
tab: { 'base-color': '#ffffff', 'active-color': '#ffffff', 'slider-color': '#42b983' },
|
|
241
|
+
tabs: { height: '60' }
|
|
242
|
+
}"
|
|
243
|
+
/>
|
|
244
|
+
`,
|
|
245
|
+
}),
|
|
246
|
+
parameters: {
|
|
247
|
+
sourceCode: [
|
|
248
|
+
{
|
|
249
|
+
name: 'Template',
|
|
250
|
+
code: `
|
|
251
|
+
<SyTabs
|
|
252
|
+
:items="items"
|
|
253
|
+
:vuetifyOptions="{
|
|
254
|
+
sheet: { theme: 'dark', color: '#0C419A' },
|
|
255
|
+
tab: { 'base-color': '#ffffff', 'active-color': '#ffffff', 'slider-color': '#42b983' },
|
|
256
|
+
tabs: { height: '60' }
|
|
257
|
+
}"
|
|
258
|
+
/>
|
|
259
|
+
`,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'Script',
|
|
263
|
+
code: `
|
|
264
|
+
<script setup>
|
|
265
|
+
const items = [
|
|
266
|
+
{ label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
|
|
267
|
+
{ label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
|
|
268
|
+
{ label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" },
|
|
269
|
+
]
|
|
270
|
+
</script>
|
|
271
|
+
`,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Exemple avec confirmation avant changement d'onglet.
|
|
279
|
+
* Démontre comment utiliser la propriété confirmTabChange et gérer l'événement confirm-tab-change.
|
|
280
|
+
*/
|
|
281
|
+
export const WithTabConfirmation: Story = {
|
|
282
|
+
render: args => ({
|
|
283
|
+
components: { SyTabs },
|
|
284
|
+
setup() {
|
|
285
|
+
return {
|
|
286
|
+
args,
|
|
287
|
+
showConfirmDialog: (message: string, callback: (confirmed: boolean) => void) => {
|
|
288
|
+
// Dans un cas réel, vous afficheriez une boîte de dialogue personnalisée
|
|
289
|
+
// Ici nous utilisons window.confirm pour la démonstration
|
|
290
|
+
const confirmed = window.confirm('Voulez-vous vraiment changer d\'onglet ?')
|
|
291
|
+
// Appelons le callback avec le résultat de la confirmation
|
|
292
|
+
callback(confirmed)
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
template: `
|
|
297
|
+
<div>
|
|
298
|
+
<div class="mb-4 pa-2 bg-warning-light">
|
|
299
|
+
<strong>Note :</strong> Essayez de changer d'onglet. Une boîte de dialogue de confirmation s'affichera.
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<SyTabs
|
|
303
|
+
:items="args.items"
|
|
304
|
+
:confirmTabChange="true"
|
|
305
|
+
@confirm-tab-change="showConfirmDialog"
|
|
306
|
+
/>
|
|
307
|
+
</div>
|
|
308
|
+
`,
|
|
309
|
+
}),
|
|
310
|
+
parameters: {
|
|
311
|
+
sourceCode: [
|
|
312
|
+
{
|
|
313
|
+
name: 'Template',
|
|
314
|
+
code: `
|
|
315
|
+
<template>
|
|
316
|
+
<div>
|
|
317
|
+
<div class="mb-4 pa-2 bg-warning-light">
|
|
318
|
+
<strong>Note :</strong> Essayez de changer d'onglet. Une boîte de dialogue de confirmation s'affichera.
|
|
319
|
+
</div>
|
|
320
|
+
<SyTabs
|
|
321
|
+
:items="items"
|
|
322
|
+
:confirmTabChange="true"
|
|
323
|
+
@confirm-tab-change="showConfirmDialog"
|
|
324
|
+
/>
|
|
325
|
+
</div>
|
|
326
|
+
</template>
|
|
327
|
+
`,
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: 'Script',
|
|
331
|
+
code: `
|
|
332
|
+
<script setup>
|
|
333
|
+
import { ref } from 'vue'
|
|
334
|
+
|
|
335
|
+
const items = [
|
|
336
|
+
{ label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
|
|
337
|
+
{ label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
|
|
338
|
+
{ label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" }
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
// Fonction pour afficher une boîte de dialogue de confirmation
|
|
342
|
+
function showConfirmDialog(message, callback) {
|
|
343
|
+
// Dans un cas réel, vous afficheriez une boîte de dialogue personnalisée
|
|
344
|
+
// Ici nous utilisons window.confirm pour la démonstration
|
|
345
|
+
const confirmed = window.confirm("Voulez-vous vraiment changer d'onglet ?")
|
|
346
|
+
// Appelons le callback avec le résultat de la confirmation
|
|
347
|
+
callback(confirmed)
|
|
348
|
+
}
|
|
349
|
+
</script>
|
|
350
|
+
`,
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TabItem } from './types'
|
|
3
|
+
import useCustomizableOptions from '@/composables/useCustomizableOptions'
|
|
4
|
+
import { config } from './config'
|
|
5
|
+
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
items: TabItem[]
|
|
9
|
+
modelValue?: number | string
|
|
10
|
+
/** Si activé, une confirmation sera demandée avant de changer d'onglet */
|
|
11
|
+
confirmTabChange?: boolean
|
|
12
|
+
/** Message affiché dans la boîte de dialogue de confirmation */
|
|
13
|
+
confirmationMessage?: boolean
|
|
14
|
+
/** Options personnalisées pour les composants Vuetify */
|
|
15
|
+
vuetifyOptions?: {
|
|
16
|
+
sheet?: {
|
|
17
|
+
theme?: string
|
|
18
|
+
dense?: boolean
|
|
19
|
+
color?: string
|
|
20
|
+
}
|
|
21
|
+
tabs?: {
|
|
22
|
+
height?: string
|
|
23
|
+
showArrows?: boolean
|
|
24
|
+
}
|
|
25
|
+
tab?: {
|
|
26
|
+
'base-color'?: string
|
|
27
|
+
'active-color'?: string
|
|
28
|
+
'slider-color'?: string
|
|
29
|
+
'ripple'?: boolean
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}>(), {
|
|
33
|
+
modelValue: undefined,
|
|
34
|
+
confirmTabChange: false,
|
|
35
|
+
confirmationMessage: false,
|
|
36
|
+
vuetifyOptions: () => ({}),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const emit = defineEmits(['update:modelValue', 'cancel-navigation', 'confirm-tab-change'])
|
|
40
|
+
|
|
41
|
+
defineSlots<{
|
|
42
|
+
'tabs-prepend': () => unknown
|
|
43
|
+
'tabs-append': () => unknown
|
|
44
|
+
'default': () => unknown
|
|
45
|
+
}>()
|
|
46
|
+
|
|
47
|
+
const options = useCustomizableOptions(config, { vuetifyOptions: props.vuetifyOptions })
|
|
48
|
+
|
|
49
|
+
// État pour suivre l'élément activement sélectionné
|
|
50
|
+
const activeItemIndex = ref<number>(0)
|
|
51
|
+
// Élément actuellement focusé (pour la navigation clavier)
|
|
52
|
+
const focusedItemIndex = ref<number>(-1)
|
|
53
|
+
|
|
54
|
+
// Émet un événement pour gérer la confirmation de changement d'onglet
|
|
55
|
+
async function handleTabChangeConfirmation(message: string): Promise<boolean> {
|
|
56
|
+
// Émettre l'événement avec le message et retourner une promesse
|
|
57
|
+
let resolver: (value: boolean) => void
|
|
58
|
+
const promise = new Promise<boolean>((resolve) => {
|
|
59
|
+
resolver = resolve
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Émettre l'événement avec le message et un callback pour résoudre la promesse
|
|
63
|
+
emit('confirm-tab-change', message, (confirmed: boolean) => {
|
|
64
|
+
resolver(confirmed)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return promise
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Fonction pour activer un élément au clic avec confirmation si nécessaire
|
|
71
|
+
async function setActiveItem(index: number) {
|
|
72
|
+
// Si l'index est déjà actif, ne rien faire
|
|
73
|
+
if (index === activeItemIndex.value) return
|
|
74
|
+
|
|
75
|
+
// Récupérer l'item pour la navigation potentielle
|
|
76
|
+
const item = props.items[index]
|
|
77
|
+
const hasHref = item && (item.href || item.to)
|
|
78
|
+
|
|
79
|
+
// Si la confirmation est activée, demander confirmation
|
|
80
|
+
if (props.confirmTabChange) {
|
|
81
|
+
const confirmMessage = props.confirmationMessage
|
|
82
|
+
const confirmed = await handleTabChangeConfirmation(confirmMessage.toString())
|
|
83
|
+
|
|
84
|
+
if (!confirmed) {
|
|
85
|
+
// L'utilisateur a annulé, émettre un événement d'annulation
|
|
86
|
+
emit('cancel-navigation')
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Si l'utilisateur a confirmé et qu'il y a un href, naviguer
|
|
91
|
+
if (hasHref) {
|
|
92
|
+
if (item.href) {
|
|
93
|
+
window.location.href = item.href
|
|
94
|
+
return // Arrêter ici car on navigue ailleurs
|
|
95
|
+
}
|
|
96
|
+
else if (item.to) {
|
|
97
|
+
// Pour les cas où vue-router est utilisé
|
|
98
|
+
// Notez que cela nécessiterait un accès au router,
|
|
99
|
+
// donc on se limite au href pour l'instant
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Sinon pas de confirmation nécessaire, naviguer directement si href présent
|
|
104
|
+
else if (hasHref && item.href) {
|
|
105
|
+
window.location.href = item.href
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Mettre à jour l'onglet actif
|
|
110
|
+
activeItemIndex.value = index
|
|
111
|
+
emit('update:modelValue', typeof props.modelValue === 'string' ? props.items[index].value : index)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Fonction pour gérer les touches Enter et Space
|
|
115
|
+
function handleKeyPress(event: KeyboardEvent, index: number) {
|
|
116
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
117
|
+
event.preventDefault()
|
|
118
|
+
void setActiveItem(index) // void pour ignorer la promesse
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Configuration des attributs ARIA et gestion du focus global
|
|
123
|
+
function setupAccessibilityFeatures() {
|
|
124
|
+
// Ajouter un écouteur global pour gérer l'escape key
|
|
125
|
+
const handleEscape = (event: KeyboardEvent) => {
|
|
126
|
+
if (event.key === 'Escape' && focusedItemIndex.value >= 0) {
|
|
127
|
+
// Retirer le focus des éléments du menu
|
|
128
|
+
focusedItemIndex.value = -1
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
window.addEventListener('keydown', handleEscape)
|
|
133
|
+
|
|
134
|
+
// Cleanup
|
|
135
|
+
onUnmounted(() => {
|
|
136
|
+
window.removeEventListener('keydown', handleEscape)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Navigation clavier entre les tabs (touches de direction)
|
|
141
|
+
function handleArrowNavigation(event: KeyboardEvent, currentIndex: number) {
|
|
142
|
+
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
|
|
143
|
+
event.preventDefault()
|
|
144
|
+
const itemCount = props.items?.length || 0
|
|
145
|
+
if (itemCount === 0) return
|
|
146
|
+
|
|
147
|
+
let newIndex = currentIndex
|
|
148
|
+
switch (event.key) {
|
|
149
|
+
case 'ArrowLeft':
|
|
150
|
+
newIndex = currentIndex <= 0 ? itemCount - 1 : currentIndex - 1
|
|
151
|
+
break
|
|
152
|
+
case 'ArrowRight':
|
|
153
|
+
newIndex = currentIndex >= itemCount - 1 ? 0 : currentIndex + 1
|
|
154
|
+
break
|
|
155
|
+
case 'Home':
|
|
156
|
+
newIndex = 0
|
|
157
|
+
break
|
|
158
|
+
case 'End':
|
|
159
|
+
newIndex = itemCount - 1
|
|
160
|
+
break
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Mettre à jour l'index de l'élément focusé et actif
|
|
164
|
+
focusedItemIndex.value = newIndex
|
|
165
|
+
|
|
166
|
+
// Focus sur le nouvel élément et l'activer
|
|
167
|
+
// Vérifier si l'élément existe et se focaliser sur le bouton
|
|
168
|
+
const tabButton = document.getElementById(`tab-${newIndex}`)
|
|
169
|
+
if (tabButton) {
|
|
170
|
+
tabButton.focus()
|
|
171
|
+
setActiveItem(newIndex)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Initialiser l'élément actif au montage
|
|
176
|
+
onMounted(() => {
|
|
177
|
+
// Configurer l'accessibilité
|
|
178
|
+
setupAccessibilityFeatures()
|
|
179
|
+
|
|
180
|
+
// Si les items ne sont pas un tableau ou vides, ne rien faire
|
|
181
|
+
if (!Array.isArray(props.items) || props.items.length === 0) return
|
|
182
|
+
|
|
183
|
+
// Si modelValue est défini, utiliser cette valeur pour déterminer l'index actif
|
|
184
|
+
if (props.modelValue !== undefined) {
|
|
185
|
+
if (typeof props.modelValue === 'number') {
|
|
186
|
+
activeItemIndex.value = props.modelValue
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
// Chercher l'index de l'item avec la valeur correspondante
|
|
190
|
+
const index = props.items.findIndex(item => item.value === props.modelValue)
|
|
191
|
+
if (index !== -1) {
|
|
192
|
+
activeItemIndex.value = index
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
// Par défaut, sélectionner le premier élément
|
|
198
|
+
activeItemIndex.value = 0
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// Observer les changements du modelValue pour mettre à jour l'élément actif
|
|
203
|
+
watch(() => props.modelValue, (newValue) => {
|
|
204
|
+
if (newValue !== undefined) {
|
|
205
|
+
if (typeof newValue === 'number') {
|
|
206
|
+
activeItemIndex.value = newValue
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const index = props.items.findIndex(item => item.value === newValue)
|
|
210
|
+
if (index !== -1) {
|
|
211
|
+
activeItemIndex.value = index
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
</script>
|
|
217
|
+
|
|
218
|
+
<template>
|
|
219
|
+
<VSheet
|
|
220
|
+
:theme="options.sheet.theme"
|
|
221
|
+
:color="options.sheet.color"
|
|
222
|
+
:class="{ 'v-sheet--dense': options.sheet.dense }"
|
|
223
|
+
>
|
|
224
|
+
<div class="sy-tabs px-xl-0 px-4">
|
|
225
|
+
<slot name="tabs-prepend" />
|
|
226
|
+
<slot>
|
|
227
|
+
<nav
|
|
228
|
+
role="tablist"
|
|
229
|
+
aria-label="Onglets de navigation"
|
|
230
|
+
class="sy-tabs__nav"
|
|
231
|
+
>
|
|
232
|
+
<ul
|
|
233
|
+
class="sy-tabs__list"
|
|
234
|
+
>
|
|
235
|
+
<li
|
|
236
|
+
v-for="(item, index) in Array.isArray(items) ? items : []"
|
|
237
|
+
:key="index"
|
|
238
|
+
class="sy-tabs__item"
|
|
239
|
+
role="presentation"
|
|
240
|
+
>
|
|
241
|
+
<a
|
|
242
|
+
:id="`tab-${index}`"
|
|
243
|
+
class="sy-tabs__button"
|
|
244
|
+
:class="{ 'sy-tabs__button--active': activeItemIndex === index }"
|
|
245
|
+
role="tab"
|
|
246
|
+
:aria-selected="activeItemIndex === index"
|
|
247
|
+
:aria-controls="`panel-${index}`"
|
|
248
|
+
tabindex="0"
|
|
249
|
+
@click="setActiveItem(index)"
|
|
250
|
+
@keydown="(event) => {
|
|
251
|
+
handleKeyPress(event, index);
|
|
252
|
+
handleArrowNavigation(event, index);
|
|
253
|
+
}"
|
|
254
|
+
>
|
|
255
|
+
{{ item.label.toUpperCase() }}
|
|
256
|
+
</a>
|
|
257
|
+
</li>
|
|
258
|
+
</ul>
|
|
259
|
+
</nav>
|
|
260
|
+
</slot>
|
|
261
|
+
<slot name="tabs-append" />
|
|
262
|
+
</div>
|
|
263
|
+
</VSheet>
|
|
264
|
+
|
|
265
|
+
<!-- Panneau de contenu des onglets -->
|
|
266
|
+
<div class="sy-tabs-panels">
|
|
267
|
+
<div
|
|
268
|
+
v-for="(item, index) in Array.isArray(items) ? items : []"
|
|
269
|
+
:id="`panel-${index}`"
|
|
270
|
+
:key="`panel-${index}`"
|
|
271
|
+
class="sy-tabs-panel"
|
|
272
|
+
role="tabpanel"
|
|
273
|
+
:aria-labelledby="`tab-${index}`"
|
|
274
|
+
:hidden="activeItemIndex !== index"
|
|
275
|
+
>
|
|
276
|
+
<slot
|
|
277
|
+
:name="`panel-${index}`"
|
|
278
|
+
:active="activeItemIndex === index"
|
|
279
|
+
>
|
|
280
|
+
{{ item.content || '' }}
|
|
281
|
+
</slot>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</template>
|
|
285
|
+
|
|
286
|
+
<style lang="scss" scoped>
|
|
287
|
+
@use '@/assets/tokens.scss' as *;
|
|
288
|
+
|
|
289
|
+
.sy-tabs {
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.sy-tabs__nav {
|
|
295
|
+
flex: 1 1 0;
|
|
296
|
+
width: 100%;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.sy-tabs__list {
|
|
300
|
+
display: flex;
|
|
301
|
+
list-style-type: none;
|
|
302
|
+
padding: 0;
|
|
303
|
+
margin: 0;
|
|
304
|
+
width: 100%;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.sy-tabs__item {
|
|
308
|
+
cursor: pointer;
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: stretch;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.sy-tabs__button {
|
|
314
|
+
display: flex;
|
|
315
|
+
align-items: center;
|
|
316
|
+
justify-content: center;
|
|
317
|
+
padding: 0 16px;
|
|
318
|
+
min-height: v-bind("options.tabs.height + 'px'");
|
|
319
|
+
font-size: 0.875rem;
|
|
320
|
+
font-weight: 700;
|
|
321
|
+
background: none;
|
|
322
|
+
border: none;
|
|
323
|
+
color: v-bind("options.tab['base-color']");
|
|
324
|
+
transition: color 0.2s ease;
|
|
325
|
+
|
|
326
|
+
&:hover {
|
|
327
|
+
color: v-bind("options.tab['active-color']");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
&:focus-visible {
|
|
331
|
+
outline: 3px solid v-bind("options.tab['active-color']");
|
|
332
|
+
outline-offset: -3px;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
&--active {
|
|
336
|
+
color: v-bind("options.tab['active-color']");
|
|
337
|
+
border-bottom: 3px solid v-bind("options.tab['slider-color']");
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.sy-tabs-panels {
|
|
342
|
+
padding: 16px;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.sy-tabs-panel {
|
|
346
|
+
&[hidden] {
|
|
347
|
+
display: none;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
</style>
|