@pyreweb/fabric 1.2.6
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/README.md +119 -0
- package/dist/fabric.cjs.js +18109 -0
- package/dist/fabric.css +2180 -0
- package/dist/fabric.esm.js +18062 -0
- package/dist/fabric.min.js +18112 -0
- package/dist/types/components/atoms/FAvatar/FAvatar.test.d.ts +1 -0
- package/dist/types/components/atoms/FBadge/FBadge.test.d.ts +1 -0
- package/dist/types/components/atoms/FButton/FButton.test.d.ts +1 -0
- package/dist/types/components/atoms/FCheckbox/FCheckbox.test.d.ts +1 -0
- package/dist/types/components/atoms/FDivider/FDivider.test.d.ts +1 -0
- package/dist/types/components/atoms/FIcon/FIcon.test.d.ts +1 -0
- package/dist/types/components/atoms/FInput/FInput.test.d.ts +1 -0
- package/dist/types/components/atoms/FLoader/FLoader.test.d.ts +1 -0
- package/dist/types/components/atoms/FRadio/FRadio.test.d.ts +1 -0
- package/dist/types/components/atoms/FTextarea/FTextarea.test.d.ts +1 -0
- package/dist/types/components/atoms/FToggle/FToggle.test.d.ts +1 -0
- package/dist/types/components/atoms/FTypography/FTypography.test.d.ts +1 -0
- package/dist/types/components/atoms/index.d.ts +13 -0
- package/dist/types/components/molecules/FAccordionItem/FAccordionItem.test.d.ts +1 -0
- package/dist/types/components/molecules/FAlert/FAlert.test.d.ts +1 -0
- package/dist/types/components/molecules/FBreadcrumb/FBreadcrumb.test.d.ts +1 -0
- package/dist/types/components/molecules/FButtonGroup/FButtonGroup.test.d.ts +1 -0
- package/dist/types/components/molecules/FCard/FCard.test.d.ts +1 -0
- package/dist/types/components/molecules/FDatePicker/FDatePicker.test.d.ts +1 -0
- package/dist/types/components/molecules/FEmptyState/FEmptyState.test.d.ts +1 -0
- package/dist/types/components/molecules/FFilePreview/FFilePreview.test.d.ts +1 -0
- package/dist/types/components/molecules/FFormField/FFormField.test.d.ts +1 -0
- package/dist/types/components/molecules/FListItem/FListItem.test.d.ts +1 -0
- package/dist/types/components/molecules/FPagination/FPagination.test.d.ts +1 -0
- package/dist/types/components/molecules/FSearchBar/FSearchBar.test.d.ts +1 -0
- package/dist/types/components/molecules/FSelect/FSelect.test.d.ts +1 -0
- package/dist/types/components/molecules/FStatCard/FStatCard.test.d.ts +1 -0
- package/dist/types/components/molecules/FTabs/FTabs.test.d.ts +1 -0
- package/dist/types/components/molecules/FToast/FToast.test.d.ts +1 -0
- package/dist/types/components/molecules/index.d.ts +18 -0
- package/dist/types/components/organisms/FActivityFeed/FActivityFeed.test.d.ts +1 -0
- package/dist/types/components/organisms/FDataTable/FDataTable.test.d.ts +1 -0
- package/dist/types/components/organisms/FDrawer/FDrawer.test.d.ts +1 -0
- package/dist/types/components/organisms/FFileUpload/FFileUpload.test.d.ts +1 -0
- package/dist/types/components/organisms/FFilterSidebar/FFilterSidebar.test.d.ts +1 -0
- package/dist/types/components/organisms/FForm/FForm.test.d.ts +1 -0
- package/dist/types/components/organisms/FModal/FModal.test.d.ts +1 -0
- package/dist/types/components/organisms/FNavigationSidebar/FNavigationSidebar.test.d.ts +1 -0
- package/dist/types/components/organisms/FOnboardingStepper/FOnboardingStepper.test.d.ts +1 -0
- package/dist/types/components/organisms/FOnboardingStepper/FStepperProgress.test.d.ts +1 -0
- package/dist/types/components/organisms/FPageHeader/FPageHeader.test.d.ts +1 -0
- package/dist/types/components/organisms/FProfileSection/FProfileSection.test.d.ts +1 -0
- package/dist/types/components/organisms/FToastProvider/FToastProvider.test.d.ts +1 -0
- package/dist/types/components/organisms/FUserMenu/FUserMenu.test.d.ts +1 -0
- package/dist/types/components/organisms/index.d.ts +14 -0
- package/dist/types/components/utils/FThemeProvider.test.d.ts +1 -0
- package/dist/types/components/utils/index.d.ts +2 -0
- package/dist/types/components.d.ts +602 -0
- package/dist/types/composables/index.d.ts +12 -0
- package/dist/types/composables/useDataTableState.d.ts +106 -0
- package/dist/types/composables/useDataTableState.test.d.ts +1 -0
- package/dist/types/composables/useFormValidation.d.ts +49 -0
- package/dist/types/composables/useFormValidation.test.d.ts +1 -0
- package/dist/types/composables/useSidebarState.d.ts +65 -0
- package/dist/types/composables/useSidebarState.test.d.ts +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/types.d.ts +529 -0
- package/package.json +100 -0
- package/src/components/atoms/FAvatar/FAvatar.stories.js +100 -0
- package/src/components/atoms/FAvatar/FAvatar.test.ts +95 -0
- package/src/components/atoms/FAvatar/FAvatar.vue +190 -0
- package/src/components/atoms/FBadge/FBadge.stories.js +129 -0
- package/src/components/atoms/FBadge/FBadge.test.ts +93 -0
- package/src/components/atoms/FBadge/FBadge.vue +103 -0
- package/src/components/atoms/FButton/FButton.stories.js +122 -0
- package/src/components/atoms/FButton/FButton.test.ts +98 -0
- package/src/components/atoms/FButton/FButton.vue +147 -0
- package/src/components/atoms/FCheckbox/FCheckbox.stories.js +96 -0
- package/src/components/atoms/FCheckbox/FCheckbox.test.ts +64 -0
- package/src/components/atoms/FCheckbox/FCheckbox.vue +76 -0
- package/src/components/atoms/FDivider/FDivider.stories.js +104 -0
- package/src/components/atoms/FDivider/FDivider.test.ts +80 -0
- package/src/components/atoms/FDivider/FDivider.vue +117 -0
- package/src/components/atoms/FIcon/FIcon.stories.js +189 -0
- package/src/components/atoms/FIcon/FIcon.test.ts +99 -0
- package/src/components/atoms/FIcon/FIcon.vue +192 -0
- package/src/components/atoms/FInput/FInput.stories.js +119 -0
- package/src/components/atoms/FInput/FInput.test.ts +79 -0
- package/src/components/atoms/FInput/FInput.vue +88 -0
- package/src/components/atoms/FLoader/FLoader.stories.js +109 -0
- package/src/components/atoms/FLoader/FLoader.test.ts +66 -0
- package/src/components/atoms/FLoader/FLoader.vue +97 -0
- package/src/components/atoms/FRadio/FRadio.stories.js +105 -0
- package/src/components/atoms/FRadio/FRadio.test.ts +75 -0
- package/src/components/atoms/FRadio/FRadio.vue +119 -0
- package/src/components/atoms/FTextarea/FTextarea.stories.js +126 -0
- package/src/components/atoms/FTextarea/FTextarea.test.ts +94 -0
- package/src/components/atoms/FTextarea/FTextarea.vue +156 -0
- package/src/components/atoms/FToggle/FToggle.stories.js +108 -0
- package/src/components/atoms/FToggle/FToggle.test.ts +96 -0
- package/src/components/atoms/FToggle/FToggle.vue +123 -0
- package/src/components/atoms/FTypography/FTypography.stories.js +127 -0
- package/src/components/atoms/FTypography/FTypography.test.ts +93 -0
- package/src/components/atoms/FTypography/FTypography.vue +78 -0
- package/src/components/atoms/index.ts +27 -0
- package/src/components/molecules/FAccordionItem/FAccordionItem.stories.js +71 -0
- package/src/components/molecules/FAccordionItem/FAccordionItem.test.ts +61 -0
- package/src/components/molecules/FAccordionItem/FAccordionItem.vue +105 -0
- package/src/components/molecules/FAlert/FAlert.stories.js +87 -0
- package/src/components/molecules/FAlert/FAlert.test.ts +59 -0
- package/src/components/molecules/FAlert/FAlert.vue +108 -0
- package/src/components/molecules/FBreadcrumb/FBreadcrumb.stories.js +90 -0
- package/src/components/molecules/FBreadcrumb/FBreadcrumb.test.ts +76 -0
- package/src/components/molecules/FBreadcrumb/FBreadcrumb.vue +117 -0
- package/src/components/molecules/FButtonGroup/FButtonGroup.stories.js +82 -0
- package/src/components/molecules/FButtonGroup/FButtonGroup.test.ts +44 -0
- package/src/components/molecules/FButtonGroup/FButtonGroup.vue +31 -0
- package/src/components/molecules/FCard/FCard.stories.js +136 -0
- package/src/components/molecules/FCard/FCard.test.ts +87 -0
- package/src/components/molecules/FCard/FCard.vue +75 -0
- package/src/components/molecules/FDatePicker/FDatePicker.stories.js +305 -0
- package/src/components/molecules/FDatePicker/FDatePicker.test.ts +282 -0
- package/src/components/molecules/FDatePicker/FDatePicker.vue +750 -0
- package/src/components/molecules/FEmptyState/FEmptyState.stories.js +98 -0
- package/src/components/molecules/FEmptyState/FEmptyState.test.ts +82 -0
- package/src/components/molecules/FEmptyState/FEmptyState.vue +89 -0
- package/src/components/molecules/FFilePreview/FFilePreview.stories.js +130 -0
- package/src/components/molecules/FFilePreview/FFilePreview.test.ts +70 -0
- package/src/components/molecules/FFilePreview/FFilePreview.vue +125 -0
- package/src/components/molecules/FFormField/FFormField.stories.js +149 -0
- package/src/components/molecules/FFormField/FFormField.test.ts +85 -0
- package/src/components/molecules/FFormField/FFormField.vue +107 -0
- package/src/components/molecules/FListItem/FListItem.stories.js +158 -0
- package/src/components/molecules/FListItem/FListItem.test.ts +93 -0
- package/src/components/molecules/FListItem/FListItem.vue +113 -0
- package/src/components/molecules/FPagination/FPagination.stories.js +132 -0
- package/src/components/molecules/FPagination/FPagination.test.ts +79 -0
- package/src/components/molecules/FPagination/FPagination.vue +206 -0
- package/src/components/molecules/FSearchBar/FSearchBar.stories.js +129 -0
- package/src/components/molecules/FSearchBar/FSearchBar.test.ts +81 -0
- package/src/components/molecules/FSearchBar/FSearchBar.vue +180 -0
- package/src/components/molecules/FSelect/FSelect.stories.js +333 -0
- package/src/components/molecules/FSelect/FSelect.test.ts +478 -0
- package/src/components/molecules/FSelect/FSelect.vue +551 -0
- package/src/components/molecules/FStatCard/FStatCard.stories.js +144 -0
- package/src/components/molecules/FStatCard/FStatCard.test.ts +78 -0
- package/src/components/molecules/FStatCard/FStatCard.vue +106 -0
- package/src/components/molecules/FTabs/FTab.vue +63 -0
- package/src/components/molecules/FTabs/FTabs.stories.js +277 -0
- package/src/components/molecules/FTabs/FTabs.test.ts +264 -0
- package/src/components/molecules/FTabs/FTabs.vue +273 -0
- package/src/components/molecules/FToast/FToast.stories.js +150 -0
- package/src/components/molecules/FToast/FToast.test.ts +157 -0
- package/src/components/molecules/FToast/FToast.vue +283 -0
- package/src/components/molecules/index.ts +37 -0
- package/src/components/organisms/FActivityFeed/FActivityFeed.stories.js +217 -0
- package/src/components/organisms/FActivityFeed/FActivityFeed.test.ts +134 -0
- package/src/components/organisms/FActivityFeed/FActivityFeed.vue +589 -0
- package/src/components/organisms/FDataTable/FDataTable.stories.js +370 -0
- package/src/components/organisms/FDataTable/FDataTable.test.ts +248 -0
- package/src/components/organisms/FDataTable/FDataTable.vue +808 -0
- package/src/components/organisms/FDrawer/FDrawer.stories.js +296 -0
- package/src/components/organisms/FDrawer/FDrawer.test.ts +142 -0
- package/src/components/organisms/FDrawer/FDrawer.vue +303 -0
- package/src/components/organisms/FFileUpload/FFileUpload.stories.js +162 -0
- package/src/components/organisms/FFileUpload/FFileUpload.test.ts +103 -0
- package/src/components/organisms/FFileUpload/FFileUpload.vue +616 -0
- package/src/components/organisms/FFilterSidebar/FFilterSidebar.stories.js +161 -0
- package/src/components/organisms/FFilterSidebar/FFilterSidebar.test.ts +92 -0
- package/src/components/organisms/FFilterSidebar/FFilterSidebar.vue +458 -0
- package/src/components/organisms/FForm/FForm.stories.js +270 -0
- package/src/components/organisms/FForm/FForm.test.ts +63 -0
- package/src/components/organisms/FForm/FForm.vue +19 -0
- package/src/components/organisms/FModal/FModal.stories.js +227 -0
- package/src/components/organisms/FModal/FModal.test.ts +181 -0
- package/src/components/organisms/FModal/FModal.vue +319 -0
- package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.stories.js +176 -0
- package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.test.ts +95 -0
- package/src/components/organisms/FNavigationSidebar/FNavigationSidebar.vue +577 -0
- package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.stories.js +197 -0
- package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.test.ts +114 -0
- package/src/components/organisms/FOnboardingStepper/FOnboardingStepper.vue +212 -0
- package/src/components/organisms/FOnboardingStepper/FStepperProgress.stories.js +122 -0
- package/src/components/organisms/FOnboardingStepper/FStepperProgress.test.ts +130 -0
- package/src/components/organisms/FOnboardingStepper/FStepperProgress.vue +146 -0
- package/src/components/organisms/FPageHeader/FPageHeader.stories.js +142 -0
- package/src/components/organisms/FPageHeader/FPageHeader.test.ts +83 -0
- package/src/components/organisms/FPageHeader/FPageHeader.vue +241 -0
- package/src/components/organisms/FProfileSection/FProfileSection.stories.js +190 -0
- package/src/components/organisms/FProfileSection/FProfileSection.test.ts +85 -0
- package/src/components/organisms/FProfileSection/FProfileSection.vue +562 -0
- package/src/components/organisms/FToastProvider/FToastProvider.stories.js +290 -0
- package/src/components/organisms/FToastProvider/FToastProvider.test.ts +215 -0
- package/src/components/organisms/FToastProvider/FToastProvider.vue +214 -0
- package/src/components/organisms/FUserMenu/FUserMenu.stories.js +170 -0
- package/src/components/organisms/FUserMenu/FUserMenu.test.ts +102 -0
- package/src/components/organisms/FUserMenu/FUserMenu.vue +407 -0
- package/src/components/organisms/index.ts +29 -0
- package/src/components/utils/FThemeProvider.stories.js +236 -0
- package/src/components/utils/FThemeProvider.test.ts +244 -0
- package/src/components/utils/FThemeProvider.vue +191 -0
- package/src/components/utils/index.ts +3 -0
- package/src/components.d.ts +602 -0
- package/src/composables/README.md +233 -0
- package/src/composables/index.ts +25 -0
- package/src/composables/useDataTableState.test.ts +378 -0
- package/src/composables/useDataTableState.ts +361 -0
- package/src/composables/useFormValidation.test.ts +198 -0
- package/src/composables/useFormValidation.ts +178 -0
- package/src/composables/useSidebarState.test.ts +307 -0
- package/src/composables/useSidebarState.ts +201 -0
- package/src/env.d.ts +14 -0
- package/src/index.ts +167 -0
- package/src/styles/tailwind.css +173 -0
- package/src/types.ts +740 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
content?: string | number;
|
|
6
|
+
variant?: 'primary' | 'success' | 'warning' | 'error' | 'neutral';
|
|
7
|
+
shape?: 'pill' | 'circle' | 'rounded';
|
|
8
|
+
size?: 'sm' | 'md' | 'lg';
|
|
9
|
+
dot?: boolean;
|
|
10
|
+
outlined?: boolean;
|
|
11
|
+
tag?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
+
content: '',
|
|
16
|
+
variant: 'primary',
|
|
17
|
+
shape: 'pill',
|
|
18
|
+
size: 'md',
|
|
19
|
+
dot: false,
|
|
20
|
+
outlined: false,
|
|
21
|
+
tag: 'span'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const computedAriaLabel = computed(() => {
|
|
25
|
+
return props.dot ? `Status: ${props.variant}` : undefined;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const computedClasses = computed(() => {
|
|
29
|
+
const base =
|
|
30
|
+
'inline-flex items-center justify-center font-sans font-medium transition-colors duration-200';
|
|
31
|
+
|
|
32
|
+
const variantStyles = {
|
|
33
|
+
primary: props.outlined
|
|
34
|
+
? 'border border-primary-500 text-primary-500 bg-transparent'
|
|
35
|
+
: 'bg-primary-500 text-white',
|
|
36
|
+
success: props.outlined
|
|
37
|
+
? 'border border-success-500 text-success-500 bg-transparent'
|
|
38
|
+
: 'bg-success-500 text-white',
|
|
39
|
+
warning: props.outlined
|
|
40
|
+
? 'border border-warning-600 text-warning-600 bg-transparent'
|
|
41
|
+
: 'bg-warning-600 text-white',
|
|
42
|
+
error: props.outlined
|
|
43
|
+
? 'border border-danger-500 text-danger-500 bg-transparent'
|
|
44
|
+
: 'bg-danger-500 text-white',
|
|
45
|
+
neutral: props.outlined
|
|
46
|
+
? 'border border-neutral-500 text-neutral-500 bg-transparent'
|
|
47
|
+
: 'bg-neutral-500 text-white'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (props.dot) {
|
|
51
|
+
const dotSizes = {
|
|
52
|
+
sm: 'w-1.5 h-1.5',
|
|
53
|
+
md: 'w-2 h-2',
|
|
54
|
+
lg: 'w-3 h-3'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const dotColors = {
|
|
58
|
+
primary: 'bg-primary-500',
|
|
59
|
+
success: 'bg-success-500',
|
|
60
|
+
warning: 'bg-warning-600',
|
|
61
|
+
error: 'bg-danger-500',
|
|
62
|
+
neutral: 'bg-neutral-500'
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
base,
|
|
67
|
+
'rounded-full',
|
|
68
|
+
dotSizes[props.size],
|
|
69
|
+
dotColors[props.variant]
|
|
70
|
+
].join(' ');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const sizeStyles = {
|
|
74
|
+
sm:
|
|
75
|
+
props.shape === 'circle'
|
|
76
|
+
? 'w-4 h-4 text-[10px]'
|
|
77
|
+
: 'px-1.5 py-0.5 text-[10px]',
|
|
78
|
+
md: props.shape === 'circle' ? 'w-6 h-6 text-xs' : 'px-2.5 py-0.5 text-xs',
|
|
79
|
+
lg: props.shape === 'circle' ? 'w-8 h-8 text-sm' : 'px-3 py-1 text-sm'
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const shapeStyles = {
|
|
83
|
+
pill: 'rounded-full',
|
|
84
|
+
circle: 'rounded-full',
|
|
85
|
+
rounded: 'rounded-md'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return [
|
|
89
|
+
base,
|
|
90
|
+
variantStyles[props.variant],
|
|
91
|
+
sizeStyles[props.size],
|
|
92
|
+
shapeStyles[props.shape]
|
|
93
|
+
].join(' ');
|
|
94
|
+
});
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<template>
|
|
98
|
+
<component :is="tag" :class="computedClasses" :aria-label="computedAriaLabel">
|
|
99
|
+
<template v-if="!dot">
|
|
100
|
+
<slot>{{ content }}</slot>
|
|
101
|
+
</template>
|
|
102
|
+
</component>
|
|
103
|
+
</template>
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import FButton from './FButton.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Atoms/FButton',
|
|
5
|
+
component: FButton,
|
|
6
|
+
tags: ['autodocs'],
|
|
7
|
+
argTypes: {
|
|
8
|
+
variant: {
|
|
9
|
+
control: { type: 'select' },
|
|
10
|
+
options: ['primary', 'secondary', 'outline', 'ghost', 'danger', 'link'],
|
|
11
|
+
description: 'Variante visuelle du bouton'
|
|
12
|
+
},
|
|
13
|
+
size: {
|
|
14
|
+
control: { type: 'select' },
|
|
15
|
+
options: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
16
|
+
description: 'Taille du bouton'
|
|
17
|
+
},
|
|
18
|
+
type: {
|
|
19
|
+
control: { type: 'select' },
|
|
20
|
+
options: ['button', 'submit', 'reset'],
|
|
21
|
+
description: 'Type HTML du bouton'
|
|
22
|
+
},
|
|
23
|
+
disabled: {
|
|
24
|
+
control: 'boolean',
|
|
25
|
+
description: 'État désactivé du bouton'
|
|
26
|
+
},
|
|
27
|
+
loading: {
|
|
28
|
+
control: 'boolean',
|
|
29
|
+
description: 'Affiche un indicateur de chargement'
|
|
30
|
+
},
|
|
31
|
+
block: {
|
|
32
|
+
control: 'boolean',
|
|
33
|
+
description: 'Bouton en pleine largeur'
|
|
34
|
+
},
|
|
35
|
+
rounded: {
|
|
36
|
+
control: 'boolean',
|
|
37
|
+
description: 'Bouton arrondi (pill)'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const Template = (args, { argTypes }) => ({
|
|
43
|
+
components: { FButton },
|
|
44
|
+
props: Object.keys(argTypes),
|
|
45
|
+
template: '<FButton v-bind="$props">{{ label }}</FButton>',
|
|
46
|
+
data() {
|
|
47
|
+
return { label: args.label || 'Bouton' };
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const Primary = Template.bind({});
|
|
52
|
+
Primary.args = {
|
|
53
|
+
variant: 'primary',
|
|
54
|
+
label: 'Bouton primaire'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const Secondary = Template.bind({});
|
|
58
|
+
Secondary.args = {
|
|
59
|
+
variant: 'secondary',
|
|
60
|
+
label: 'Bouton secondaire'
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const Outline = Template.bind({});
|
|
64
|
+
Outline.args = {
|
|
65
|
+
variant: 'outline',
|
|
66
|
+
label: 'Bouton outline'
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const Ghost = Template.bind({});
|
|
70
|
+
Ghost.args = {
|
|
71
|
+
variant: 'ghost',
|
|
72
|
+
label: 'Bouton ghost'
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const Danger = Template.bind({});
|
|
76
|
+
Danger.args = {
|
|
77
|
+
variant: 'danger',
|
|
78
|
+
label: 'Bouton danger'
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const Link = Template.bind({});
|
|
82
|
+
Link.args = {
|
|
83
|
+
variant: 'link',
|
|
84
|
+
label: 'Bouton lien'
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const Sizes = () => ({
|
|
88
|
+
components: { FButton },
|
|
89
|
+
template: `
|
|
90
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
91
|
+
<FButton size="xs">Extra petit</FButton>
|
|
92
|
+
<FButton size="sm">Petit</FButton>
|
|
93
|
+
<FButton size="md">Moyen</FButton>
|
|
94
|
+
<FButton size="lg">Grand</FButton>
|
|
95
|
+
<FButton size="xl">Extra grand</FButton>
|
|
96
|
+
</div>
|
|
97
|
+
`
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export const Loading = Template.bind({});
|
|
101
|
+
Loading.args = {
|
|
102
|
+
loading: true,
|
|
103
|
+
label: 'Chargement...'
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const Disabled = Template.bind({});
|
|
107
|
+
Disabled.args = {
|
|
108
|
+
disabled: true,
|
|
109
|
+
label: 'Désactivé'
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const Block = Template.bind({});
|
|
113
|
+
Block.args = {
|
|
114
|
+
block: true,
|
|
115
|
+
label: 'Bouton pleine largeur'
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const Rounded = Template.bind({});
|
|
119
|
+
Rounded.args = {
|
|
120
|
+
rounded: true,
|
|
121
|
+
label: 'Bouton arrondi'
|
|
122
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import FButton from './FButton.vue';
|
|
4
|
+
|
|
5
|
+
describe('FButton', () => {
|
|
6
|
+
it('renders correctly with default props', () => {
|
|
7
|
+
const wrapper = mount(FButton, {
|
|
8
|
+
slots: {
|
|
9
|
+
default: 'Click me'
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
expect(wrapper.text()).toContain('Click me');
|
|
13
|
+
expect(wrapper.find('button').exists()).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('applies primary variant by default', () => {
|
|
17
|
+
const wrapper = mount(FButton);
|
|
18
|
+
const button = wrapper.find('button');
|
|
19
|
+
expect(button.classes().join(' ')).toContain('bg-primary-600');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('applies correct variant classes', () => {
|
|
23
|
+
const variants = [
|
|
24
|
+
'primary',
|
|
25
|
+
'secondary',
|
|
26
|
+
'outline',
|
|
27
|
+
'ghost',
|
|
28
|
+
'danger',
|
|
29
|
+
'link'
|
|
30
|
+
] as const;
|
|
31
|
+
variants.forEach((variant) => {
|
|
32
|
+
const wrapper = mount(FButton, {
|
|
33
|
+
propsData: { variant }
|
|
34
|
+
});
|
|
35
|
+
expect(wrapper.find('button').exists()).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('applies correct size classes', () => {
|
|
40
|
+
const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const;
|
|
41
|
+
sizes.forEach((size) => {
|
|
42
|
+
const wrapper = mount(FButton, {
|
|
43
|
+
propsData: { size }
|
|
44
|
+
});
|
|
45
|
+
expect(wrapper.find('button').exists()).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('emits click event when clicked', async () => {
|
|
50
|
+
const wrapper = mount(FButton);
|
|
51
|
+
await wrapper.find('button').trigger('click');
|
|
52
|
+
expect(wrapper.emitted('click')).toHaveLength(1);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('does not emit click when disabled', async () => {
|
|
56
|
+
const wrapper = mount(FButton, {
|
|
57
|
+
propsData: { disabled: true }
|
|
58
|
+
});
|
|
59
|
+
await wrapper.find('button').trigger('click');
|
|
60
|
+
expect(wrapper.emitted('click')).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('does not emit click when loading', async () => {
|
|
64
|
+
const wrapper = mount(FButton, {
|
|
65
|
+
propsData: { loading: true }
|
|
66
|
+
});
|
|
67
|
+
await wrapper.find('button').trigger('click');
|
|
68
|
+
expect(wrapper.emitted('click')).toBeUndefined();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('shows loader when loading', () => {
|
|
72
|
+
const wrapper = mount(FButton, {
|
|
73
|
+
propsData: { loading: true }
|
|
74
|
+
});
|
|
75
|
+
expect(wrapper.findComponent({ name: 'FLoader' }).exists()).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('applies block class when block prop is true', () => {
|
|
79
|
+
const wrapper = mount(FButton, {
|
|
80
|
+
propsData: { block: true }
|
|
81
|
+
});
|
|
82
|
+
expect(wrapper.find('button').classes()).toContain('w-full');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('applies rounded-full when rounded prop is true', () => {
|
|
86
|
+
const wrapper = mount(FButton, {
|
|
87
|
+
propsData: { rounded: true }
|
|
88
|
+
});
|
|
89
|
+
expect(wrapper.find('button').classes()).toContain('rounded-full');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('sets correct button type', () => {
|
|
93
|
+
const wrapper = mount(FButton, {
|
|
94
|
+
propsData: { type: 'submit' }
|
|
95
|
+
});
|
|
96
|
+
expect(wrapper.find('button').attributes('type')).toBe('submit');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import FLoader from '../FLoader/FLoader.vue';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'FButton',
|
|
7
|
+
components: {
|
|
8
|
+
FLoader
|
|
9
|
+
},
|
|
10
|
+
props: {
|
|
11
|
+
variant: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: 'primary',
|
|
14
|
+
validator: (value) =>
|
|
15
|
+
['primary', 'secondary', 'outline', 'ghost', 'danger', 'link'].includes(
|
|
16
|
+
value
|
|
17
|
+
)
|
|
18
|
+
},
|
|
19
|
+
size: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: 'md',
|
|
22
|
+
validator: (value) => ['xs', 'sm', 'md', 'lg', 'xl'].includes(value)
|
|
23
|
+
},
|
|
24
|
+
type: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: 'button',
|
|
27
|
+
validator: (value) => ['button', 'submit', 'reset'].includes(value)
|
|
28
|
+
},
|
|
29
|
+
disabled: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false
|
|
32
|
+
},
|
|
33
|
+
loading: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
},
|
|
37
|
+
block: {
|
|
38
|
+
type: Boolean,
|
|
39
|
+
default: false
|
|
40
|
+
},
|
|
41
|
+
rounded: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
default: false
|
|
44
|
+
},
|
|
45
|
+
iconLeft: {
|
|
46
|
+
type: String,
|
|
47
|
+
default: undefined
|
|
48
|
+
},
|
|
49
|
+
iconRight: {
|
|
50
|
+
type: String,
|
|
51
|
+
default: undefined
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
emits: ['click'],
|
|
55
|
+
setup(props, { emit }) {
|
|
56
|
+
const handleClick = (event) => {
|
|
57
|
+
if (props.disabled || props.loading) {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
emit('click', event);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const baseClasses =
|
|
65
|
+
'inline-flex items-center justify-center font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none';
|
|
66
|
+
|
|
67
|
+
const transitionClasses =
|
|
68
|
+
'transition-colors duration-[var(--transition-duration-base)] ease-[var(--transition-easing-standard)]';
|
|
69
|
+
|
|
70
|
+
const variantClasses = computed(() => {
|
|
71
|
+
const variants = {
|
|
72
|
+
primary:
|
|
73
|
+
'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500 border border-transparent',
|
|
74
|
+
secondary:
|
|
75
|
+
'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50 focus:ring-primary-500',
|
|
76
|
+
outline:
|
|
77
|
+
'bg-transparent text-primary-600 border border-primary-600 hover:bg-primary-50 focus:ring-primary-500',
|
|
78
|
+
ghost:
|
|
79
|
+
'bg-transparent text-gray-600 hover:bg-gray-100 hover:text-gray-900 focus:ring-gray-500',
|
|
80
|
+
danger:
|
|
81
|
+
'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500 border border-transparent',
|
|
82
|
+
link: 'text-primary-600 underline-offset-4 hover:underline p-0 h-auto focus:ring-0'
|
|
83
|
+
};
|
|
84
|
+
return variants[props.variant];
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const sizeClasses = computed(() => {
|
|
88
|
+
if (props.variant === 'link') return '';
|
|
89
|
+
|
|
90
|
+
const sizes = {
|
|
91
|
+
xs: 'text-xs px-2.5 py-1.5',
|
|
92
|
+
sm: 'text-sm px-3 py-2',
|
|
93
|
+
md: 'text-sm px-4 py-2',
|
|
94
|
+
lg: 'text-base px-4 py-2',
|
|
95
|
+
xl: 'text-base px-6 py-3'
|
|
96
|
+
};
|
|
97
|
+
return sizes[props.size];
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const classes = computed(() => [
|
|
101
|
+
baseClasses,
|
|
102
|
+
transitionClasses,
|
|
103
|
+
variantClasses.value,
|
|
104
|
+
sizeClasses.value,
|
|
105
|
+
props.block ? 'w-full flex' : '',
|
|
106
|
+
props.rounded ? 'rounded-full' : 'rounded-md',
|
|
107
|
+
props.loading
|
|
108
|
+
? 'cursor-wait relative text-transparent hover:text-transparent !transition-none'
|
|
109
|
+
: ''
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
handleClick,
|
|
114
|
+
classes
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<button
|
|
122
|
+
:type="type"
|
|
123
|
+
:class="classes"
|
|
124
|
+
:disabled="disabled || loading"
|
|
125
|
+
:aria-disabled="disabled || loading"
|
|
126
|
+
@click="handleClick"
|
|
127
|
+
>
|
|
128
|
+
<div
|
|
129
|
+
v-if="loading"
|
|
130
|
+
class="absolute inset-0 flex items-center justify-center text-current"
|
|
131
|
+
>
|
|
132
|
+
<FLoader size="sm" class="text-current opacity-100" />
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<span :class="{ 'opacity-0': loading }" class="flex items-center gap-2">
|
|
136
|
+
<slot name="prefix">
|
|
137
|
+
<span v-if="iconLeft" :class="iconLeft" aria-hidden="true" />
|
|
138
|
+
</slot>
|
|
139
|
+
|
|
140
|
+
<slot />
|
|
141
|
+
|
|
142
|
+
<slot name="suffix">
|
|
143
|
+
<span v-if="iconRight" :class="iconRight" aria-hidden="true" />
|
|
144
|
+
</slot>
|
|
145
|
+
</span>
|
|
146
|
+
</button>
|
|
147
|
+
</template>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import FCheckbox from './FCheckbox.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Atoms/FCheckbox',
|
|
5
|
+
component: FCheckbox,
|
|
6
|
+
tags: ['autodocs'],
|
|
7
|
+
argTypes: {
|
|
8
|
+
checked: {
|
|
9
|
+
control: 'boolean',
|
|
10
|
+
description: 'État coché de la checkbox'
|
|
11
|
+
},
|
|
12
|
+
label: {
|
|
13
|
+
control: 'text',
|
|
14
|
+
description: 'Libellé de la checkbox'
|
|
15
|
+
},
|
|
16
|
+
disabled: {
|
|
17
|
+
control: 'boolean',
|
|
18
|
+
description: 'État désactivé'
|
|
19
|
+
},
|
|
20
|
+
error: {
|
|
21
|
+
control: 'boolean',
|
|
22
|
+
description: "État d'erreur"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const Template = (args, { argTypes }) => ({
|
|
28
|
+
components: { FCheckbox },
|
|
29
|
+
props: Object.keys(argTypes),
|
|
30
|
+
data() {
|
|
31
|
+
return { isChecked: args.checked || false };
|
|
32
|
+
},
|
|
33
|
+
template: '<FCheckbox v-bind="$props" v-model="isChecked" />'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const Default = Template.bind({});
|
|
37
|
+
Default.args = {
|
|
38
|
+
label: "J'accepte les conditions"
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Checked = Template.bind({});
|
|
42
|
+
Checked.args = {
|
|
43
|
+
label: 'Option sélectionnée',
|
|
44
|
+
checked: true
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const Disabled = Template.bind({});
|
|
48
|
+
Disabled.args = {
|
|
49
|
+
label: 'Option désactivée',
|
|
50
|
+
disabled: true
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const DisabledChecked = Template.bind({});
|
|
54
|
+
DisabledChecked.args = {
|
|
55
|
+
label: 'Option désactivée et cochée',
|
|
56
|
+
disabled: true,
|
|
57
|
+
checked: true
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const WithError = Template.bind({});
|
|
61
|
+
WithError.args = {
|
|
62
|
+
label: 'Champ obligatoire',
|
|
63
|
+
error: true
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const States = () => ({
|
|
67
|
+
components: { FCheckbox },
|
|
68
|
+
data() {
|
|
69
|
+
return {
|
|
70
|
+
checked1: false,
|
|
71
|
+
checked2: true,
|
|
72
|
+
checked3: false,
|
|
73
|
+
checked4: false
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
template: `
|
|
77
|
+
<div class="flex flex-col gap-3">
|
|
78
|
+
<FCheckbox v-model="checked1" label="Normal" />
|
|
79
|
+
<FCheckbox v-model="checked2" label="Coché" />
|
|
80
|
+
<FCheckbox v-model="checked3" label="Désactivé" disabled />
|
|
81
|
+
<FCheckbox v-model="checked4" label="Erreur" error />
|
|
82
|
+
</div>
|
|
83
|
+
`
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const WithSlot = () => ({
|
|
87
|
+
components: { FCheckbox },
|
|
88
|
+
data() {
|
|
89
|
+
return { checked: false };
|
|
90
|
+
},
|
|
91
|
+
template: `
|
|
92
|
+
<FCheckbox v-model="checked">
|
|
93
|
+
J'accepte les <a href="#" class="text-primary-600 underline">conditions d'utilisation</a>
|
|
94
|
+
</FCheckbox>
|
|
95
|
+
`
|
|
96
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import FCheckbox from './FCheckbox.vue';
|
|
4
|
+
|
|
5
|
+
describe('FCheckbox', () => {
|
|
6
|
+
it('renders correctly with default props', () => {
|
|
7
|
+
const wrapper = mount(FCheckbox);
|
|
8
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('displays label when provided', () => {
|
|
12
|
+
const wrapper = mount(FCheckbox, {
|
|
13
|
+
propsData: { label: 'Accept terms' }
|
|
14
|
+
});
|
|
15
|
+
expect(wrapper.text()).toContain('Accept terms');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('displays slot content as label', () => {
|
|
19
|
+
const wrapper = mount(FCheckbox, {
|
|
20
|
+
slots: {
|
|
21
|
+
default: 'Custom label'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
expect(wrapper.text()).toContain('Custom label');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('is unchecked by default', () => {
|
|
28
|
+
const wrapper = mount(FCheckbox);
|
|
29
|
+
expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(
|
|
30
|
+
false
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('respects checked prop', () => {
|
|
35
|
+
const wrapper = mount(FCheckbox, {
|
|
36
|
+
propsData: { checked: true }
|
|
37
|
+
});
|
|
38
|
+
expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(
|
|
39
|
+
true
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('emits change event when toggled', async () => {
|
|
44
|
+
const wrapper = mount(FCheckbox);
|
|
45
|
+
const input = wrapper.find('input');
|
|
46
|
+
await input.setChecked(true);
|
|
47
|
+
expect(wrapper.emitted('change')).toBeTruthy();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('applies disabled styles', () => {
|
|
51
|
+
const wrapper = mount(FCheckbox, {
|
|
52
|
+
propsData: { disabled: true }
|
|
53
|
+
});
|
|
54
|
+
expect(wrapper.find('label').classes()).toContain('cursor-not-allowed');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('applies error styles', () => {
|
|
58
|
+
const wrapper = mount(FCheckbox, {
|
|
59
|
+
propsData: { error: true }
|
|
60
|
+
});
|
|
61
|
+
const input = wrapper.find('input');
|
|
62
|
+
expect(input.classes().join(' ')).toContain('border-danger');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<label :class="wrapperClasses">
|
|
3
|
+
<input
|
|
4
|
+
type="checkbox"
|
|
5
|
+
:class="inputClasses"
|
|
6
|
+
:checked="checked"
|
|
7
|
+
:disabled="disabled"
|
|
8
|
+
:aria-invalid="error"
|
|
9
|
+
@change="updateInput"
|
|
10
|
+
@focus="$emit('focus', $event)"
|
|
11
|
+
@blur="$emit('blur', $event)"
|
|
12
|
+
/>
|
|
13
|
+
<span v-if="label || $slots.default" :class="labelClasses">
|
|
14
|
+
<slot>{{ label }}</slot>
|
|
15
|
+
</span>
|
|
16
|
+
</label>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script>
|
|
20
|
+
export default {
|
|
21
|
+
name: 'FCheckbox',
|
|
22
|
+
model: {
|
|
23
|
+
prop: 'checked',
|
|
24
|
+
event: 'change'
|
|
25
|
+
},
|
|
26
|
+
props: {
|
|
27
|
+
checked: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
default: false
|
|
30
|
+
},
|
|
31
|
+
label: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: ''
|
|
34
|
+
},
|
|
35
|
+
disabled: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: false
|
|
38
|
+
},
|
|
39
|
+
error: {
|
|
40
|
+
type: Boolean,
|
|
41
|
+
default: false
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
computed: {
|
|
45
|
+
wrapperClasses() {
|
|
46
|
+
return [
|
|
47
|
+
'inline-flex items-center select-none',
|
|
48
|
+
this.disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
|
|
49
|
+
];
|
|
50
|
+
},
|
|
51
|
+
inputClasses() {
|
|
52
|
+
return [
|
|
53
|
+
'shrink-0 w-4 h-4 rounded border focus:outline-none focus:ring-2 focus:ring-offset-1',
|
|
54
|
+
'transition-colors duration-[var(--transition-duration-base)] ease-[var(--transition-easing-standard)]',
|
|
55
|
+
this.error
|
|
56
|
+
? 'border-danger-500 text-danger-500 focus:ring-danger-500/20'
|
|
57
|
+
: 'border-neutral-300 text-primary-600 focus:border-primary-600 focus:ring-primary-600/20',
|
|
58
|
+
this.disabled ? 'cursor-not-allowed' : 'cursor-pointer'
|
|
59
|
+
];
|
|
60
|
+
},
|
|
61
|
+
labelClasses() {
|
|
62
|
+
return [
|
|
63
|
+
'ml-2 font-sans text-sm',
|
|
64
|
+
this.error ? 'text-danger-500' : 'text-neutral-700'
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
methods: {
|
|
69
|
+
updateInput(event) {
|
|
70
|
+
if (!this.disabled) {
|
|
71
|
+
this.$emit('change', event.target.checked);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
</script>
|