Compare commits

..

46 Commits

Author SHA1 Message Date
Paul Valerie GOMA
299bae8d3d 1.0.27 -> 1.0.28 2025-08-11 19:35:46 +02:00
Paul Valerie GOMA
ef93944618 1.0.27 -> 1.0.28 2025-08-11 19:35:28 +02:00
Paul Valerie GOMA
cff17612de 📝 docs: Updated CHANGELOG file 2025-08-11 19:32:15 +02:00
Paul Valerie GOMA
6af37991a4 🎉 add README 2025-08-11 19:31:34 +02:00
Paul Valerie GOMA
6a0e9e566b 💄 Updating the style of the primevue datatable component 2025-08-11 19:28:00 +02:00
Paul Valerie GOMA
b06accb14f feature: Data table component updated 2025-08-11 19:27:21 +02:00
Paul Valerie GOMA
9e3067627c feature: Menu bar component updated 2025-08-11 18:07:18 +02:00
Paul Valerie GOMA
fd143ea7a5 feature: Select component improved 2025-08-11 17:32:09 +02:00
Paul Valerie GOMA
5f5c532d8e 🐛 fix: bugs fixed 2025-08-11 16:20:56 +02:00
Paul Valerie GOMA
e20626296e 1.0.26 -> 1.0.27 2025-08-07 15:30:13 +02:00
Paul Valerie GOMA
96fa281b11 1.0.26 -> 1.0.27 2025-08-07 15:29:55 +02:00
Paul Valerie GOMA
8d4bb9b762 📝 docs: README file updated 2025-08-07 15:25:30 +02:00
Paul Valerie GOMA
dcf54bf4b9 💄 Updating the style of the primevue fileupload component 2025-08-07 15:24:25 +02:00
Paul Valerie GOMA
6caad49ff8 💄 Updating the style of the primevue datatable component 2025-08-07 15:23:43 +02:00
Paul Valerie GOMA
24b1018f7a 1.0.25 -> 1.0.26 2025-08-05 15:30:26 +02:00
Paul Valerie GOMA
501e9cb3b8 1.0.25 -> 1.0.26 2025-08-05 15:30:06 +02:00
Paul Valerie GOMA
8071f3565c 🐛 fix: bug fixed 2025-08-05 15:21:02 +02:00
Paul Valerie GOMA
fff3f5e647 feature: Select component improved 2025-08-05 15:20:11 +02:00
Paul Valerie GOMA
1ca5f60449 📝 docs: README file updated 2025-08-05 15:13:11 +02:00
Paul Valerie GOMA
07aed8835e 📝 docs: Updated CHANGELOG file 2025-08-05 15:12:44 +02:00
Paul Valerie GOMA
3c3956da68 🔥 deleted files 2025-08-05 15:11:51 +02:00
Paul Valerie GOMA
65a2f9028a 💄 Updating the style of the primevue datatable component 2025-08-05 15:09:22 +02:00
Paul Valerie GOMA
2bd148274f 1.0.24 -> 1.0.25 2025-08-05 00:39:53 +02:00
Paul Valerie GOMA
ae4955f00c 📝 docs: Updated CHANGELOG file 2025-08-05 00:30:54 +02:00
Paul Valerie GOMA
8bfd244be2 📝 docs: README file updated 2025-08-05 00:29:59 +02:00
Paul Valerie GOMA
921667d7e9 1.0.24 -> 1.0.25 2025-08-05 00:29:27 +02:00
Paul Valerie GOMA
ce70f81113 1.0.24 -> 1.0.25 2025-08-05 00:29:12 +02:00
Paul Valerie GOMA
504118e92e Display datatable component 2025-08-05 00:27:37 +02:00
Paul Valerie GOMA
073ddc9d7c feature: Data table component updated 2025-08-05 00:26:53 +02:00
Paul Valerie GOMA
994237e98e feature: Select component improved 2025-08-05 00:26:21 +02:00
Paul Valerie GOMA
625189077b ♻️ refactor: component updated 2025-08-05 00:25:41 +02:00
Paul Valerie GOMA
dc288d42bb ♻️ refactor: useConfirmModal composable updated 2025-08-03 04:28:31 +02:00
Paul Valerie GOMA
ef57a78a03 ♻️ refactor: useAlert composable updated 2025-08-03 04:28:04 +02:00
Paul Valerie GOMA
38dfcfe794 Display confirm modal component 2025-08-03 04:27:34 +02:00
Paul Valerie GOMA
f8dfcfee1f 1.0.23 -> 1.0.24 2025-08-03 04:26:19 +02:00
Paul Valerie GOMA
34a00841b1 1.0.23 -> 1.0.24 2025-08-03 04:25:49 +02:00
Paul Valerie GOMA
8f77be03dc 🎉 add README 2025-08-03 04:24:51 +02:00
Paul Valerie GOMA
7749fdc8e2 📝 docs: Updated CHANGELOG file 2025-08-03 04:24:35 +02:00
Paul Valerie GOMA
1fb81ec69f 🔧 test environment setup updated 2025-08-02 23:24:38 +02:00
Paul Valerie GOMA
738f5ae2ad 1.0.22 -> 1.0.23 2025-08-02 23:24:03 +02:00
Paul Valerie GOMA
a5ef570311 1.0.22 -> 1.0.23 2025-08-02 23:23:48 +02:00
Paul Valerie GOMA
1964852b1c Display menu bar component 2025-08-02 22:52:56 +02:00
Paul Valerie GOMA
3c833bacb1 1.0.21 -> 1.0.22 2025-08-02 22:52:34 +02:00
Paul Valerie GOMA
e960c0d56d 1.0.22 -> 1.0.22 2025-08-02 22:52:06 +02:00
Paul Valerie GOMA
b78de670e9 📝 docs: README file updated 2025-08-02 22:50:43 +02:00
Paul Valerie GOMA
edeb8b105d 📝 docs: Updated CHANGELOG file 2025-08-02 22:50:20 +02:00
18 changed files with 848 additions and 802 deletions

View File

@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.28] - 2025-08-11
### fixed
- bug fixed
## [1.0.24] - 2025-08-02
### fixed
- Composable error fixed
## [1.0.22] - 2025-08-02
### fixed
- Fixed bugs related to dependencies
## [1.0.20] - 2025-07-31 ## [1.0.20] - 2025-07-31
### fixed ### fixed
- Composable error fixed - Composable error fixed

View File

@ -1,3 +1,3 @@
# visua-vue # visua-vue
**Current version: v1.0.20** **Current version: v1.0.28**

1293
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@cellule-financiere-pmo/visua-vue", "name": "@cellule-financiere-pmo/visua-vue",
"version": "1.0.21", "version": "1.0.28",
"type": "module", "type": "module",
"description": "Vue.js components of the Visua Design System.", "description": "Vue.js components of the Visua Design System.",
"main": "./dist/visua-vue.umd.cjs", "main": "./dist/visua-vue.umd.cjs",
@ -32,14 +32,13 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@cellule-financiere-pmo/visua": "1.1.3", "@cellule-financiere-pmo/visua": "1.1.3"
},
"peerDependencies": {
"primevue": "^4.3.6", "primevue": "^4.3.6",
"vue": "^3.5.17", "vue": "^3.5.17",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"peerDependencies": {
"vue": "^3.5.17"
},
"devDependencies": { "devDependencies": {
"@tsconfig/node22": "^22.0.2", "@tsconfig/node22": "^22.0.2",
"@types/node": "^22.15.32", "@types/node": "^22.15.32",
@ -51,6 +50,7 @@
"eslint": "^9.29.0", "eslint": "^9.29.0",
"eslint-plugin-vue": "~10.2.0", "eslint-plugin-vue": "~10.2.0",
"jiti": "^2.4.2", "jiti": "^2.4.2",
"jsdom": "^26.1.0",
"npm-run-all2": "^8.0.4", "npm-run-all2": "^8.0.4",
"rimraf": "^5.0.10", "rimraf": "^5.0.10",
"typescript": "~5.8.0", "typescript": "~5.8.0",
@ -58,6 +58,8 @@
"vite-plugin-inspect": "^11.3.0", "vite-plugin-inspect": "^11.3.0",
"vite-plugin-vue-devtools": "^7.7.7", "vite-plugin-vue-devtools": "^7.7.7",
"vitest": "^3.2.4", "vitest": "^3.2.4",
"vue": "^3.5.17",
"vue-router": "^4.5.1",
"vue-tsc": "^2.2.10" "vue-tsc": "^2.2.10"
}, },
"repository": { "repository": {

View File

@ -15,6 +15,7 @@
// import VConfirmModalView from '../template/VConfirmModalView.vue'; // import VConfirmModalView from '../template/VConfirmModalView.vue';
import VDataTableView from '../template/VDataTableView.vue'; import VDataTableView from '../template/VDataTableView.vue';
// import VMenuBarView from '../template/VMenuBarView.vue' // import VMenuBarView from '../template/VMenuBarView.vue'
// import VDataTable from './components/table/VDataTable.vue';
</script> </script>
@ -25,8 +26,8 @@ import VDataTableView from '../template/VDataTableView.vue';
<!-- <VAccordionView/> --> <!-- <VAccordionView/> -->
<!-- <VInputView/> --> <!-- <VInputView/> -->
<!-- <VCheckboxView/> --> <!-- <VCheckboxView/> -->
<!-- <VBadgeView/> <!-- <VBadgeView/>-->
<VSelectView/> --> <!-- <VSelectView/> -->
<!-- <VProgressBarView/> --> <!-- <VProgressBarView/> -->
<!-- <VMessageView/> --> <!-- <VMessageView/> -->
<!-- <VFileUploadView/> --> <!-- <VFileUploadView/> -->
@ -35,7 +36,8 @@ import VDataTableView from '../template/VDataTableView.vue';
<!-- <VConfirmModalView/> --> <!-- <VConfirmModalView/> -->
<VDataTableView/> <VDataTableView/>
<!-- <RouterView/> --> <!-- <RouterView/> -->
<VMenuBarView/> <!-- <VMenuBarView/> -->
<!-- <VDataTable/> -->
</template> </template>
<style lang="css" scoped> <style lang="css" scoped>
*{ *{

View File

@ -53,21 +53,21 @@
--p-datatable-footer-border-color: var(--p-datatable-border-color); --p-datatable-footer-border-color: var(--p-datatable-border-color);
--p-datatable-footer-color: var(--p-content-color); --p-datatable-footer-color: var(--p-content-color);
--p-datatable-footer-border-width: 0 0 1px 0; --p-datatable-footer-border-width: 0 0 1px 0;
--p-datatable-footer-padding: 1rem; --p-datatable-footer-padding: 0.75rem;
--p-datatable-footer-lg-padding: 1.25rem; --p-datatable-footer-lg-padding: 1rem;
--p-datatable-footer-sm-padding: 0.5rem; --p-datatable-footer-sm-padding: 0.5rem;
--p-datatable-column-footer-font-weight: 600; --p-datatable-column-footer-font-weight: 600;
--p-datatable-footer-cell-background: var(--datatable-background); --p-datatable-footer-cell-background: var(--datatable-background);
--p-datatable-footer-cell-border-color: var(--p-datatable-border-color); --p-datatable-footer-cell-border-color: var(--p-datatable-border-color);
--p-datatable-footer-cell-color: var(--p-content-color); --p-datatable-footer-cell-color: var(--p-content-color);
--p-datatable-footer-cell-padding: 1rem; --p-datatable-footer-cell-padding: 0.75rem;
--p-datatable-footer-cell-lg-padding: 1.25rem; --p-datatable-footer-cell-lg-padding: 1rem;
--p-datatable-footer-cell-sm-padding: 0.5rem; --p-datatable-footer-cell-sm-padding: 0.5rem;
--p-datatable-body-cell-border-color: var(--p-datatable-border-color); --p-datatable-body-cell-border-color: var(--p-datatable-border-color);
--p-datatable-body-cell-padding: 1rem; --p-datatable-body-cell-padding: 0.75rem;
--p-datatable-body-cell-lg-padding: 1.25rem; --p-datatable-body-cell-lg-padding: 1rem;
--p-datatable-body-cell-sm-padding: 0.5rem; --p-datatable-body-cell-sm-padding: 0.5rem;
--p-datatable-row-background: var(--datatable-alt-background); --p-datatable-row-background: var(--datatable-background);
--p-datatable-row-hover-background: var(--datatable-hover-background); --p-datatable-row-hover-background: var(--datatable-hover-background);
--p-datatable-row-selected-background: var(--datatable-active-background); --p-datatable-row-selected-background: var(--datatable-active-background);
--p-datatable-row-color: var(--datatable-row-color); --p-datatable-row-color: var(--datatable-row-color);
@ -78,17 +78,17 @@
--p-datatable-row-focus-ring-color: var(--focus-color); --p-datatable-row-focus-ring-color: var(--focus-color);
--p-datatable-row-focus-ring-offset: -1px; --p-datatable-row-focus-ring-offset: -1px;
/* --p-datatable-row-focus-ring-shadow: var(--p-focus-ring-shadow); */ /* --p-datatable-row-focus-ring-shadow: var(--p-focus-ring-shadow); */
--p-datatable-column-title-font-weight: var(--text-body-SM-detail-text-Bold-weight); --p-datatable-column-title-font-weight: var(--text-body-SM-detail-text-Medium-weight);
--p-datatable-header-cell-background: var(--datatable-background); --p-datatable-header-cell-background: var(--primary-color-850-blue-france-default);
--p-datatable-header-cell-hover-background: var(--datatable-hover-background); --p-datatable-header-cell-hover-background: var(--primary-color-850-blue-france-hover);
--p-datatable-header-cell-selected-background: var(--datatable-active-background); --p-datatable-header-cell-selected-background: var(--primary-color-850-blue-france-active);
--p-datatable-header-cell-border-color: var(--border-plain-grey); --p-datatable-header-cell-border-color: var(--border-plain-grey);
--p-datatable-header-cell-color: var(--datatable-header-cell-color); --p-datatable-header-cell-color: var(--datatable-header-cell-color);
--p-datatable-header-cell-hover-color: var(--datatable-header-cell-color); --p-datatable-header-cell-hover-color: var(--datatable-header-cell-color);
--p-datatable-header-cell-selected-color: var(--datatable-header-cell-color); --p-datatable-header-cell-selected-color: var(--datatable-header-cell-color);
--p-datatable-header-cell-gap: 0.5rem; --p-datatable-header-cell-gap: 0.5rem;
--p-datatable-header-cell-padding: 1rem; --p-datatable-header-cell-padding: 0.75rem;
--p-datatable-header-cell-lg-padding: 1.25rem; --p-datatable-header-cell-lg-padding: 1rem;
--p-datatable-header-cell-sm-padding: 0.5rem; --p-datatable-header-cell-sm-padding: 0.5rem;
--p-datatable-header-cell-focus-ring-width: var(--focus-width); --p-datatable-header-cell-focus-ring-width: var(--focus-width);
--p-datatable-header-cell-focus-ring-style: var(--focus-style); --p-datatable-header-cell-focus-ring-style: var(--focus-style);
@ -96,11 +96,11 @@
--p-datatable-header-cell-focus-ring-offset: -1px; --p-datatable-header-cell-focus-ring-offset: -1px;
/* --p-datatable-header-cell-focus-ring-shadow: var(--p-focus-ring-shadow); */ /* --p-datatable-header-cell-focus-ring-shadow: var(--p-focus-ring-shadow); */
--p-datatable-header-background: var(--datatable-background); --p-datatable-header-background: var(--datatable-background);
--p-datatable-header-border-color: var(--border-default-grey); --p-datatable-header-border-color: transparent;
--p-datatable-header-color: var(--datatable-header-cell-color); --p-datatable-header-color: var(--datatable-header-cell-color);
--p-datatable-header-border-width: 0 0 1px 0; --p-datatable-header-border-width: 0 0 1px 0;
--p-datatable-header-padding: 1rem; --p-datatable-header-padding: 0.75rem;
--p-datatable-header-lg-padding: 1.25rem; --p-datatable-header-lg-padding: 1rem;
--p-datatable-header-sm-padding: 0.5rem; --p-datatable-header-sm-padding: 0.5rem;
/* --p-datatable-transition-duration: var(--p-transition-duration); */ /* --p-datatable-transition-duration: var(--p-transition-duration); */
--p-datatable-body-cell-selected-border-color: var(--datatable-active-background); --p-datatable-body-cell-selected-border-color: var(--datatable-active-background);

View File

@ -16,7 +16,7 @@
--p-fileupload-header-border-width: 0; --p-fileupload-header-border-width: 0;
--p-fileupload-header-border-radius: 0; --p-fileupload-header-border-radius: 0;
--p-fileupload-header-gap: 0.5rem; --p-fileupload-header-gap: 0.5rem;
--p-fileupload-background: var(--background-transparent); --p-fileupload-background: var(--background-default-grey);
--p-fileupload-border-color: var(--border-default-grey); --p-fileupload-border-color: var(--border-default-grey);
--p-fileupload-color: var(--text-default-grey); --p-fileupload-color: var(--text-default-grey);
--p-fileupload-border-radius: 0px; --p-fileupload-border-radius: 0px;

View File

@ -1,7 +1,9 @@
import { useToast } from "primevue/usetoast"; import { useToast } from "primevue/usetoast";
import type IVAlert from "@/components/alert/IVAlert.type.js"; import type IVAlert from "../alert/IVAlert.type.js";
export function useAlert() { export function useAlert() {
const toast = useToast();
const showAlert = ({ const showAlert = ({
title = '', title = '',
description = '', description = '',
@ -9,17 +11,14 @@ export function useAlert() {
closeable = true, closeable = true,
lifeTime, lifeTime,
}: IVAlert) => { }: IVAlert) => {
const toast = useToast();
toast.add({ toast.add({
severity: type, severity: type,
summary: title, summary: title,
detail: description, detail: description,
life: lifeTime, life: lifeTime,
closable: closeable, closable: closeable,
}); })
}; }
return { showAlert }; return { showAlert}
} }

View File

@ -3,6 +3,8 @@ import { useConfirm } from "primevue";
import VButton from "../button/VButton.vue"; import VButton from "../button/VButton.vue";
export function useConfirmModal() { export function useConfirmModal() {
const confirm = useConfirm();
const showConfirmModal = ({ const showConfirmModal = ({
acceptProps = VButton, acceptProps = VButton,
rejectProps = VButton, rejectProps = VButton,
@ -10,17 +12,15 @@ export function useConfirmModal() {
header = '', header = '',
message = '', message = '',
icon = '', icon = '',
accept = () => {}, accept = Function,
reject = () => {}, reject = Function,
onHide = () => {}, onHide = Function,
onShow = () => {}, onShow = Function,
modal = true, modal = true,
blockScroll = true, blockScroll = true,
position = 'center', position = 'center',
appendTo = 'body', appendTo = 'body',
}: ConfirmationOptions) => { }: ConfirmationOptions) => {
const confirm = useConfirm();
confirm.require({ confirm.require({
group, group,
header, header,
@ -36,9 +36,7 @@ export function useConfirmModal() {
blockScroll, blockScroll,
position, position,
appendTo, appendTo,
}); })
}; }
return {showConfirmModal}
return { showConfirmModal };
} }

View File

@ -15,7 +15,11 @@ import styles from '@visua/typography.module.css';
const fileUploadRef = ref(); const fileUploadRef = ref();
const fileProgressMap = ref<Record<string, number>>({}); const fileProgressMap = ref<Record<string, number>>({});
const hasActiveError = ref(false); const hasValidationError = ref(false);
const hasSystemError = ref(false);
const systemErrorMessage = ref('');
defineOptions({ defineOptions({
inheritAttrs: false, inheritAttrs: false,
@ -72,7 +76,6 @@ const handleSelect = (event: FileUploadSelectEvent) => {
const handleClear = () => { const handleClear = () => {
emit('clear'); emit('clear');
hasActiveError.value = false;
}; };
const handleUpload = (event: FileUploadUploadEvent) => { const handleUpload = (event: FileUploadUploadEvent) => {
@ -81,7 +84,6 @@ const handleUpload = (event: FileUploadUploadEvent) => {
const handleRemove = (event: FileUploadRemoveEvent) => { const handleRemove = (event: FileUploadRemoveEvent) => {
emit('remove', event); emit('remove', event);
hasActiveError.value =false;
}; };
const handleProgress = (event: FileUploadProgressEvent) => { const handleProgress = (event: FileUploadProgressEvent) => {
@ -95,11 +97,21 @@ const handleProgress = (event: FileUploadProgressEvent) => {
const handleError = (event: FileUploadErrorEvent) => { const handleError = (event: FileUploadErrorEvent) => {
emit('error', event); emit('error', event);
hasActiveError.value = true; hasSystemError.value = true;
const status = event.xhr?.status;
const statusText = event.xhr?.statusText || 'Erreur inconnue';
const fileNames = Array.isArray(event.files)
? event.files.map(f => f.name).join(', ')
: event.files.name;
systemErrorMessage.value = `Echec de téléversement du fichier ${fileNames} {Status : ${status} | Message : <${statusText}>}`;
if (!props.advanced && fileUploadRef.value) { if (!props.advanced && fileUploadRef.value) {
fileUploadRef.value.uploadedFiles = []; fileUploadRef.value.uploadedFiles = [];
} }
} };
const lastSelectedFile = computed(() => { const lastSelectedFile = computed(() => {
const files = fileUploadRef.value?.files || []; const files = fileUploadRef.value?.files || [];
@ -114,10 +126,10 @@ const padding = computed(() => props.advanced ? '1.125rem' : '0rem')
const borderColor = computed(() => props.advanced ? 'var(--border-default-grey)' : 'transparent'); const borderColor = computed(() => props.advanced ? 'var(--border-default-grey)' : 'transparent');
const labelState = computed(() => { const labelState = computed(() => {
if(!hasActiveError.value && !props.disabled) return 'default'; if ((hasValidationError.value || hasSystemError.value) && !props.disabled) return 'error';
else if(hasActiveError.value && !props.disabled) return 'error'; return 'default';
else return undefined });
})
type MessageType = 'alert' | 'warning' | 'success' | 'info'; type MessageType = 'alert' | 'warning' | 'success' | 'info';
@ -222,21 +234,21 @@ const globalStatusMessage = computed<{
title="parcourir les fichiers" title="parcourir les fichiers"
/> />
<span <span
v-if="(!slotProps.files || slotProps.files.length === 0) && (!slotProps.uploadedFiles || slotProps.uploadedFiles.length === 0) && !hasActiveError" v-if="(!slotProps.files || slotProps.files.length === 0) && (!slotProps.uploadedFiles || slotProps.uploadedFiles.length === 0) && !(hasSystemError || hasValidationError)"
:class="[styles['text-body-SM-detail-text-Regular']]" :class="[styles['text-body-SM-detail-text-Regular']]"
> >
Aucun fichier sélectionné Aucun fichier sélectionné
</span> </span>
</div> </div>
</template> </template>
<template #empty v-if="props.advanced && !hasActiveError"> <template #empty v-if="props.advanced && !(hasSystemError || hasValidationError)">
<div class="fileupload-empty" :class="[styles['text-body-SM-detail-text-Regular']]"> <div class="fileupload-empty" :class="[styles['text-body-SM-detail-text-Regular']]">
<i class="ri-upload-cloud-line upload-cloud-icon"></i> <i class="ri-upload-cloud-line upload-cloud-icon"></i>
<p>Glissez-déposez les fichiers ici pour les téléverser.</p> <p>Glissez-déposez les fichiers ici pour les téléverser.</p>
</div> </div>
</template> </template>
<template #content="slotProps"> <template #content="slotProps">
<div v-if="props.advanced && !hasActiveError" style="margin-top: 0.75rem;" class="file-content"> <div v-if="props.advanced && !(hasValidationError || hasSystemError)" style="margin-top: 0.75rem;" class="file-content">
<VMessage <VMessage
v-if="globalStatusMessage" v-if="globalStatusMessage"
:type="globalStatusMessage.type" :type="globalStatusMessage.type"
@ -271,7 +283,7 @@ const globalStatusMessage = computed<{
</div> </div>
</VScrollPanel> </VScrollPanel>
</div> </div>
<div v-if="!props.advanced && !hasActiveError"> <div v-if="!props.advanced && !(hasValidationError || hasSystemError)">
<VFile <VFile
v-if="lastSelectedFile" v-if="lastSelectedFile"
:file="lastSelectedFile" :file="lastSelectedFile"
@ -290,14 +302,17 @@ const globalStatusMessage = computed<{
aria-live="polite" aria-live="polite"
> >
<VHint <VHint
v-for="message of slotProps.messages" v-for="message of [...(slotProps.messages ?? []), ...(hasSystemError ? [systemErrorMessage] : [])]"
:key="message" :key="message"
:title="message" :title="message"
type="alert" type="alert"
icon icon
/> />
</div> </div>
<VLabelErrorProxy :hasError="(slotProps.messages ?? []).length > 0" @update:error="hasActiveError = $event" /> <VLabelErrorProxy
:hasError="(slotProps.messages ?? []).length > 0"
@update:error="hasValidationError = $event"
/>
</template> </template>
</FileUpload> </FileUpload>
</div> </div>

View File

@ -70,7 +70,7 @@ const labelState = computed(() => {
v-if="props.labelVisible" v-if="props.labelVisible"
:for="props.id" :for="props.id"
:label="props.label" :label="props.label"
:required="!props.disabled" :required="!props.disabled && props.required"
:disabled="props.disabled" :disabled="props.disabled"
:type="labelState" :type="labelState"
:hint="props.hint" :hint="props.hint"

View File

@ -14,6 +14,7 @@ const props = withDefaults(defineProps<IVLabel>(), {
<template> <template>
<label <label
:for="props.for" :for="props.for"
class="label-container"
:class="[styles['text-body-MD-standard-text-Regular'], { :class="[styles['text-body-MD-standard-text-Regular'], {
'label': props.type === 'default', 'label': props.type === 'default',
'success': props.type === 'success', 'success': props.type === 'success',
@ -23,12 +24,14 @@ const props = withDefaults(defineProps<IVLabel>(), {
:aria-label="props.label" :aria-label="props.label"
:aria-disabled="props.disabled" :aria-disabled="props.disabled"
> >
<span>
{{ props.label }} {{ props.label }}
<template v-if="props.required"> <template v-if="props.required">
<span v-if="props.required" :class="{ 'required': !props.disabled}"> <span v-if="props.required" :class="{ 'required': !props.disabled}">
<slot name="required-tip">*</slot> <slot name="required-tip">*</slot>
</span> </span>
</template> </template>
</span>
<VHint <VHint
v-if="props.hint" v-if="props.hint"
:title="props.hint" :title="props.hint"
@ -38,6 +41,18 @@ const props = withDefaults(defineProps<IVLabel>(), {
</template> </template>
<style lang="css" scoped> <style lang="css" scoped>
*{
padding: 0px;
margin: 0px;
}
.label-container{
display: flex;
flex-direction: column;
align-items: start;
gap: 0.25rem;
}
.label {color: var(--text-label-grey);} .label {color: var(--text-label-grey);}
.success {color: var(--text-default-success);} .success {color: var(--text-default-success);}
.error {color: var(--text-default-error);} .error {color: var(--text-default-error);}

View File

@ -2,6 +2,7 @@
import Menubar from 'primevue/menubar'; import Menubar from 'primevue/menubar';
import type IVMenuBar from './IVMenuBar.type.js'; import type IVMenuBar from './IVMenuBar.type.js';
import styles from '@visua/typography.module.css' import styles from '@visua/typography.module.css'
import { useRoute } from 'vue-router';
const props = withDefaults(defineProps<IVMenuBar>(), { const props = withDefaults(defineProps<IVMenuBar>(), {
searchbarId: 'searchbar-header', searchbarId: 'searchbar-header',
@ -14,6 +15,8 @@ const props = withDefaults(defineProps<IVMenuBar>(), {
menuLabel: undefined, menuLabel: undefined,
logoPath: '/home', logoPath: '/home',
}) })
const route = useRoute();
</script> </script>
<template> <template>
@ -50,7 +53,9 @@ const props = withDefaults(defineProps<IVMenuBar>(), {
:tabindex="0" :tabindex="0"
v-bind="props.action" v-bind="props.action"
class="item" class="item"
:class="[styles['text-body-LG-article-text-Regular']]" :class="[styles['text-body-LG-article-text-Regular'],
item.to === route.path ? 'active' : ''
]"
> >
<slot name="itemicon" :item="item" v-if="'icon' in item"> <slot name="itemicon" :item="item" v-if="'icon' in item">
<i :class="[item.icon]"></i> <i :class="[item.icon]"></i>
@ -105,7 +110,13 @@ a{
} }
.item:hover, .item:hover,
.item:active{ .item.active{
color: var(--menu-active-color); color: var(--menu-active-color);
} }
.item.active {
height: 100%;
font-weight: bold;
}
</style> </style>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import Select from 'primevue/select'; import Select, { type SelectSlots } from 'primevue/select';
import type IVSelect from './IVSelect.type.js'; import type IVSelect from './IVSelect.type.js';
import { useId, computed, watch, ref } from 'vue'; import { useId, computed, watch, ref } from 'vue';
import VLabel from '../label/VLabel.vue'; import VLabel from '../label/VLabel.vue';
@ -29,9 +29,7 @@ const props = withDefaults(defineProps<IVSelect>(), {
selectionMessage: 'Elements sélectionnés', selectionMessage: 'Elements sélectionnés',
emptySelectionMessage: 'Aucun élément sélectionné', emptySelectionMessage: 'Aucun élément sélectionné',
emptyFilterMessage: 'Aucun résultat trouvé', emptyFilterMessage: 'Aucun résultat trouvé',
emptyMessage: 'Aucune option disponible', emptyMessage: 'Aucune option disponible'
optionTemplate: false,
}) })
const emit = defineEmits([ const emit = defineEmits([
@ -78,6 +76,33 @@ const labelState = computed(() => {
else if(props.errorMessage && !props.successMessage && !props.disabled) return 'error'; else if(props.errorMessage && !props.successMessage && !props.disabled) return 'error';
else return undefined else return undefined
}) })
type VSelectSlots = SelectSlots & {
required?: (props: Record<string, unknown>) => unknown
};
const slots = defineSlots<VSelectSlots>();
const selectSlotKeys = [
'value',
'header',
'footer',
'option',
'optiongroup',
'emptyfilter',
'empty',
'content',
'loader',
'clearicon',
'dropdownicon',
'loadingicon',
'filtericon'
] as const;
const availableSlots = computed(() =>
selectSlotKeys.filter((key) => !!slots[key]).map((key) => [key, slots[key]])
);
</script> </script>
<template> <template>
@ -86,13 +111,13 @@ const labelState = computed(() => {
v-if="props.label" v-if="props.label"
:for="props.selectId" :for="props.selectId"
:label="props.label" :label="props.label"
:required="!props.disabled" :required="props.required && !props.disabled"
:disabled="props.disabled" :disabled="props.disabled"
:type="labelState" :type="labelState"
:hint="props.hint" :hint="props.hint"
> >
<template #required-type v-if="props.required"> <template #required-type v-if="props.required">
<slot name="required-type"/> <slot name="required"/>
</template> </template>
</VLabel> </VLabel>
<Select <Select
@ -141,8 +166,12 @@ const labelState = computed(() => {
} }
]" ]"
> >
<template v-if="props.optionTemplate" #option="{option, selected, index}"> <template
<slot name="option" :option="option" :selected="selected" :index="index"/> v-for="([name]) in availableSlots"
:key="name"
v-slot:[name]="slotProps"
>
<slot :name="name" v-bind="slotProps" />
</template> </template>
</Select> </Select>
<div <div
@ -174,6 +203,8 @@ const labelState = computed(() => {
--p-select-dropdown-color: var(--text-disabled-grey); --p-select-dropdown-color: var(--text-disabled-grey);
} }
.p-select.error, .p-select.success{border-width: var(--large-border-width);}
.p-select.error{ .p-select.error{
--p-select-border-color: var(--border-plain-error); --p-select-border-color: var(--border-plain-error);
--p-select-hover-border-color: var(--border-plain-error); --p-select-hover-border-color: var(--border-plain-error);

View File

@ -2,6 +2,7 @@
import DataTable from 'primevue/datatable'; import DataTable from 'primevue/datatable';
import type { DataTableProps, DataTableSlots } from 'primevue/datatable'; import type { DataTableProps, DataTableSlots } from 'primevue/datatable';
import { useId, ref, watch, computed } from 'vue'; import { useId, ref, watch, computed } from 'vue';
import styles from '@visua/typography.module.css';
export interface IVDataTable extends Partial<Omit<DataTableProps, 'pt' | 'dt' | 'ptOptions' | 'unstyled'>>{ export interface IVDataTable extends Partial<Omit<DataTableProps, 'pt' | 'dt' | 'ptOptions' | 'unstyled'>>{
id?: string id?: string
@ -415,11 +416,31 @@ watch(localEditingRows, (newVal) => {
<slot :name="name" v-bind="slotProps" /> <slot :name="name" v-bind="slotProps" />
</template> </template>
<slot></slot> <slot></slot>
<template #empty>
<div class="datatable-empty" :class="[styles['text-body-SM-detail-text-Regular']]">
<i class="ri-database-line database-icon"></i>
<span> Aucune donnée trouvée. </span>
</div>
</template>
</DataTable> </DataTable>
</template> </template>
<style lang="css" scoped> <style lang="css" scoped>
.p-datatable{ .datatable-empty{
height: fit-content;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box; box-sizing: border-box;
gap: 1.5rem;
margin-top: 1.5rem;
}
.database-icon{
padding: 0px;
margin: 0px;
display: block;
color: var(--border-contrast-grey);
font-size: 5rem;
} }
</style> </style>

View File

@ -1,34 +0,0 @@
import { describe, it, expect, vi } from 'vitest'
// 1⃣ Mock BEFORE importing useAlert
const addSpy = vi.fn()
vi.mock('primevue/usetoast', () => ({
useToast: () => ({
add: addSpy
})
}))
// 2⃣ Import le module après que le mock soit actif
import { useAlert } from '../src/components/composable/useAlert'
describe('useAlert', () => {
it('should call toast.add with correct options', () => {
const { showAlert } = useAlert()
showAlert({
title: 'Test title',
description: 'Test description',
type: 'warn',
closeable: true,
lifeTime: 3000,
})
expect(addSpy).toHaveBeenCalledWith({
severity: 'warn',
summary: 'Test title',
detail: 'Test description',
closable: true,
life: 3000,
})
})
})

View File

@ -1,34 +0,0 @@
// tests/useConfirmModal.spec.ts
import { describe, it, expect, vi } from 'vitest'
// ✅ Mock AVANT import de useConfirmModal
const requireSpy = vi.fn()
vi.mock('primevue', () => ({
useConfirm: () => ({
require: requireSpy
})
}))
import { useConfirmModal } from '../src/components/composable/useConfirmModal'
describe('useConfirmModal', () => {
it('should call confirm.require with the given options', () => {
const { showConfirmModal } = useConfirmModal()
const options = {
header: 'Confirm Header',
message: 'Are you sure?',
accept: vi.fn(),
reject: vi.fn(),
}
showConfirmModal(options)
expect(requireSpy).toHaveBeenCalledWith(expect.objectContaining({
header: 'Confirm Header',
message: 'Are you sure?',
accept: options.accept,
reject: options.reject,
}))
})
})

View File

@ -1,9 +1,9 @@
// vite.config.ts // vite.config.ts
import { fileURLToPath, URL } from 'node:url'; import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'; import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'; import vueDevTools from 'vite-plugin-vue-devtools'
import path from 'path'; import path from 'path'
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
@ -24,12 +24,15 @@ export default defineConfig({
formats: ['es', 'umd'], formats: ['es', 'umd'],
}, },
rollupOptions: { rollupOptions: {
external: ['vue'], external: ['vue', 'primevue', 'vue-router', 'primeicons'],
output: { output: {
globals: { globals: {
vue: 'Vue', vue: 'Vue',
primevue: 'PrimeVue',
'vue-router': 'VueRouter',
primeicons: 'primeicons'
}, },
}, },
}, },
}, },
}); })