Compare commits
No commits in common. "main" and "button-group" have entirely different histories.
main
...
button-gro
105
CHANGELOG.md
105
CHANGELOG.md
|
@ -5,110 +5,6 @@ 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
|
|
||||||
### fixed
|
|
||||||
- Composable error fixed
|
|
||||||
|
|
||||||
## [1.0.19] - 2025-07-31
|
|
||||||
### Added
|
|
||||||
- Style component added
|
|
||||||
|
|
||||||
## [1.0.18] - 2025-07-31
|
|
||||||
### fixed
|
|
||||||
- Package publication error fixed
|
|
||||||
- Component export file configuration
|
|
||||||
|
|
||||||
## [1.0.15] - 2025-07-30
|
|
||||||
### Added
|
|
||||||
- Menu bar compoenent
|
|
||||||
|
|
||||||
## [1.0.14] - 2025-07-29
|
|
||||||
### fixed
|
|
||||||
- Package publication error fixed
|
|
||||||
- Component export file configuration
|
|
||||||
|
|
||||||
## [1.0.13] - 2025-07-29
|
|
||||||
### Added
|
|
||||||
- Data table compoenent
|
|
||||||
- List style
|
|
||||||
|
|
||||||
## [1.0.12] - 2025-07-28
|
|
||||||
### Added
|
|
||||||
- Modal compoenent
|
|
||||||
- Confirm modal component
|
|
||||||
- useConfirmModal composable
|
|
||||||
|
|
||||||
## [1.0.11] - 2025-07-27
|
|
||||||
### Added
|
|
||||||
- Alert compoenent
|
|
||||||
- useAlert composable
|
|
||||||
|
|
||||||
## [1.0.10] - 2025-07-27
|
|
||||||
### Added
|
|
||||||
- File compoenent
|
|
||||||
- File upload component
|
|
||||||
- Scroll panel component
|
|
||||||
|
|
||||||
## [1.0.9] - 2025-07-24
|
|
||||||
### Added
|
|
||||||
- Message component
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Badge compoent icon
|
|
||||||
|
|
||||||
## [1.0.8] - 2025-07-23
|
|
||||||
### Added
|
|
||||||
- ProgressBar component
|
|
||||||
|
|
||||||
## [1.0.7] - 2025-07-23
|
|
||||||
### Added
|
|
||||||
- Select component
|
|
||||||
- Badge component
|
|
||||||
|
|
||||||
## [1.0.6] - 2025-07-23
|
|
||||||
### Added
|
|
||||||
- Checkbox component
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- VLabel `required-tip` slot
|
|
||||||
- VHint UI style
|
|
||||||
|
|
||||||
## [1.0.5] - 2025-07-22
|
|
||||||
### Added
|
|
||||||
- Input component
|
|
||||||
- Label component
|
|
||||||
- Group component
|
|
||||||
- Hint component
|
|
||||||
- Divider component
|
|
||||||
|
|
||||||
## [1.0.4] - 2025-07-21
|
|
||||||
### Added
|
|
||||||
- Accordion component
|
|
||||||
- Accordion component unit test
|
|
||||||
|
|
||||||
## [1.0.3] - 2025-07-21
|
|
||||||
### Added
|
|
||||||
- Link component
|
|
||||||
- Link component unit test
|
|
||||||
|
|
||||||
## [1.0.2] - 2025-07-21
|
|
||||||
### Added
|
|
||||||
- Button group component
|
|
||||||
- Test the onClick function when the button is clicked
|
|
||||||
- Button group component unit test
|
|
||||||
|
|
||||||
## [1.0.1] - 2025-07-20
|
## [1.0.1] - 2025-07-20
|
||||||
### Initial
|
### Initial
|
||||||
- First project version published.
|
- First project version published.
|
||||||
|
@ -116,7 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Added
|
### Added
|
||||||
- Component style configuration
|
- Component style configuration
|
||||||
- Button component
|
- Button component
|
||||||
- Button component unit test
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Correction of test deployment errors
|
- Correction of test deployment errors
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# visua-vue
|
# visua-vue
|
||||||
|
|
||||||
**Current version: v1.0.28**
|
**Current version: v1.0.1**
|
8
env.d.ts
vendored
8
env.d.ts
vendored
|
@ -1,9 +1 @@
|
||||||
|
|
||||||
|
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
declare module '*.vue' {
|
|
||||||
import { DefineComponent } from 'vue';
|
|
||||||
const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>;
|
|
||||||
export default component;
|
|
||||||
}
|
|
||||||
|
|
1362
package-lock.json
generated
1362
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
68
package.json
68
package.json
|
@ -1,43 +1,22 @@
|
||||||
{
|
{
|
||||||
"name": "@cellule-financiere-pmo/visua-vue",
|
"name": "@cellule-financiere-pmo/visua-vue",
|
||||||
"version": "1.0.28",
|
"version": "1.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Vue.js components of the Visua Design System.",
|
|
||||||
"main": "./dist/visua-vue.umd.cjs",
|
|
||||||
"module": "./dist/visua-vue.es.js",
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"require": "./dist/visua-vue.umd.cjs",
|
|
||||||
"import": "./dist/visua-vue.es.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sideEffects": false,
|
|
||||||
"files": [
|
|
||||||
"dist",
|
|
||||||
"README.md"
|
|
||||||
],
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf dist",
|
|
||||||
"build": "npm-run-all clean build:assets build:types",
|
|
||||||
"build:assets": "vite build",
|
|
||||||
"build:types": "vue-tsc --emitDeclarationOnly --outDir dist && tsc --project tsconfig.types.json",
|
|
||||||
"prepare": "npm run build",
|
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"build-only": "vite build",
|
||||||
"type-check": "vue-tsc --build",
|
"type-check": "vue-tsc --build",
|
||||||
"lint": "eslint . --fix",
|
"lint": "eslint . --fix",
|
||||||
"test": "vitest --config vitest.config.ts",
|
"test:unit": "vitest run --coverage"
|
||||||
"test:unit": "vitest run --coverage",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cellule-financiere-pmo/visua": "1.1.3"
|
"@cellule-financiere-pmo/visua": "1.1.3",
|
||||||
},
|
"jsdom": "^26.1.0",
|
||||||
"peerDependencies": {
|
|
||||||
"primevue": "^4.3.6",
|
"primevue": "^4.3.6",
|
||||||
"vue": "^3.5.17",
|
"vite-plugin-inspect": "^11.3.0",
|
||||||
"vue-router": "^4.5.1"
|
"vue": "^3.5.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/node22": "^22.0.2",
|
"@tsconfig/node22": "^22.0.2",
|
||||||
|
@ -50,37 +29,28 @@
|
||||||
"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",
|
|
||||||
"typescript": "~5.8.0",
|
"typescript": "~5.8.0",
|
||||||
"vite": "^7.0.0",
|
"vite": "^7.0.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"
|
||||||
},
|
},
|
||||||
|
"description": "**Current version: v0.0.0**",
|
||||||
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitlab.com/cellule-financiere-pmo/design-system/visua-vue.git"
|
"url": "git+https://gitlab.com/cellule-financiere-pmo/design-system/visua-vue.git"
|
||||||
},
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://gitlab.com/cellule-financiere-pmo/design-system/visua-vue/issues"
|
"url": "https://gitlab.com/cellule-financiere-pmo/design-system/visua-vue/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/cellule-financiere-pmo/design-system/visua-vue#readme",
|
"homepage": "https://gitlab.com/cellule-financiere-pmo/design-system/visua-vue#readme",
|
||||||
"keywords": [
|
"files": [
|
||||||
"vue",
|
"src/components/*",
|
||||||
"typescript",
|
"src/assets/style/*"
|
||||||
"design-system",
|
]
|
||||||
"components",
|
|
||||||
"vite",
|
|
||||||
"library"
|
|
||||||
],
|
|
||||||
"author": {
|
|
||||||
"name": "CGI",
|
|
||||||
"url": "https://www.cgi.com"
|
|
||||||
},
|
|
||||||
"license": "ISC"
|
|
||||||
}
|
}
|
||||||
|
|
42
src/App.vue
42
src/App.vue
|
@ -1,48 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import VButtonView from '../template/VButtonView.vue'
|
// import VButtonView from '../template/VButtonView.vue'
|
||||||
// import VButtonGroupView from '../template/VButtonGroupView.vue';
|
import VButtonGroupView from '../template/VButtonGroupView.vue';
|
||||||
// import VLinkView from '../template/VLinkView.vue';
|
|
||||||
// import VAccordionView from '../template/VAccordionView.vue';
|
|
||||||
// import VInputView from '../template/VInputView.vue';
|
|
||||||
// import VCheckboxView from '../template/VCheckboxView.vue';
|
|
||||||
// import VBadgeView from '../template/VBadgeView.vue';
|
|
||||||
// import VSelectView from '../template/VSelectView.vue';
|
|
||||||
// import VProgressBarView from '../template/VProgressBarView.vue';
|
|
||||||
// import VMessageView from '../template/VMessageView.vue';
|
|
||||||
// import VFileUploadView from '../template/VFileUploadView.vue';
|
|
||||||
// import VAlertView from '../template/VAlertView.vue';
|
|
||||||
// import VModalView from '../template/VModalView.vue';
|
|
||||||
// import VConfirmModalView from '../template/VConfirmModalView.vue';
|
|
||||||
import VDataTableView from '../template/VDataTableView.vue';
|
|
||||||
// import VMenuBarView from '../template/VMenuBarView.vue'
|
|
||||||
// import VDataTable from './components/table/VDataTable.vue';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- <VButtonView/> -->
|
<!-- <VButtonView/> -->
|
||||||
<!-- <VButtonGroupView/> -->
|
<VButtonGroupView/>
|
||||||
<!-- <VLinkView/> -->
|
|
||||||
<!-- <VAccordionView/> -->
|
|
||||||
<!-- <VInputView/> -->
|
|
||||||
<!-- <VCheckboxView/> -->
|
|
||||||
<!-- <VBadgeView/>-->
|
|
||||||
<!-- <VSelectView/> -->
|
|
||||||
<!-- <VProgressBarView/> -->
|
|
||||||
<!-- <VMessageView/> -->
|
|
||||||
<!-- <VFileUploadView/> -->
|
|
||||||
<!-- <VAlertView/> -->
|
|
||||||
<!-- <VModalView/> -->
|
|
||||||
<!-- <VConfirmModalView/> -->
|
|
||||||
<VDataTableView/>
|
|
||||||
<!-- <RouterView/> -->
|
|
||||||
<!-- <VMenuBarView/> -->
|
|
||||||
<!-- <VDataTable/> -->
|
|
||||||
</template>
|
</template>
|
||||||
<style lang="css" scoped>
|
|
||||||
*{
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -8,6 +8,4 @@ html {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-family: var(--font-family-primary);
|
font-family: var(--font-family-primary);
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
--input-padding-y: 0.5rem;
|
--input-padding-y: 0.5rem;
|
||||||
--input-border-raduis: 0.25rem 0.25rem 0px 0px;
|
--input-border-raduis: 0.25rem 0.25rem 0px 0px;
|
||||||
/* datatable */
|
/* datatable */
|
||||||
--datatable-background: var(--background-default-grey);
|
--datatable-background: var(--background-contrast-grey);
|
||||||
--datatable-alt-background: var(--background-alt-grey);
|
--datatable-alt-background: var(--background-alt-grey);
|
||||||
--datatable-hover-background: var(--background-alt-grey-hover);
|
--datatable-hover-background: var(--background-alt-grey-hover);
|
||||||
--datatable-active-background: var(--background-alt-grey-active);
|
--datatable-active-background: var(--background-alt-grey-active);
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
/* menu */
|
/* menu */
|
||||||
--menu-container-border-color: var(--border-default-grey);
|
--menu-container-border-color: var(--border-default-grey);
|
||||||
--menu-border-width: 0px 0px 0px var(--large-border-width);
|
--menu-border-width: 0px 0px 0px var(--large-border-width);
|
||||||
--menu-default-background: var(--background-default-grey);
|
--menu-default-background: var(--background-transparent);
|
||||||
--menu-default-color: var(--text-default-grey);
|
--menu-default-color: var(--text-default-grey);
|
||||||
--menu-active-color: var(--text-active-blue-france);
|
--menu-active-color: var(--text-active-blue-france);
|
||||||
--menu-hover-background: var(--background-transparent-active);
|
--menu-hover-background: var(--background-transparent-active);
|
||||||
|
@ -41,5 +41,4 @@
|
||||||
--menu-indent: 1rem;
|
--menu-indent: 1rem;
|
||||||
--menu-border-color: transparent;
|
--menu-border-color: transparent;
|
||||||
--menu-border-color-active: var(--border-active-blue-france);
|
--menu-border-color-active: var(--border-active-blue-france);
|
||||||
--menu-border-radius: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1 @@
|
||||||
@import './primevue-style/button.css';
|
@import './primevue-style/button.css';
|
||||||
@import './primevue-style/accordion.css';
|
|
||||||
@import './primevue-style/message.css';
|
|
||||||
@import './primevue-style/input.css';
|
|
||||||
@import './primevue-style/password.css';
|
|
||||||
@import './primevue-style/textarea.css';
|
|
||||||
@import './primevue-style/divider.css';
|
|
||||||
@import './primevue-style/various.css';
|
|
||||||
@import './primevue-style/form.css';
|
|
||||||
@import './primevue-style/checkbox.css';
|
|
||||||
@import './primevue-style/tag.css';
|
|
||||||
@import './primevue-style/select.css';
|
|
||||||
@import './primevue-style/overlay.css';
|
|
||||||
@import './primevue-style/iconfield.css';
|
|
||||||
@import './primevue-style/progressbar.css';
|
|
||||||
@import './primevue-style/fileupload.css';
|
|
||||||
@import './primevue-style/scrollpanel.css';
|
|
||||||
@import './primevue-style/toast.css';
|
|
||||||
@import './primevue-style/dialog.css';
|
|
||||||
@import './primevue-style/confirmdialog.css';
|
|
||||||
@import './primevue-style/datatable.css';
|
|
||||||
@import './primevue-style/paginator.css';
|
|
||||||
@import './primevue-style/list.css';
|
|
||||||
@import './primevue-style/menubar.css';
|
|
||||||
@import './primevue-style/navigation.css';
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
:root{
|
|
||||||
/* panel */
|
|
||||||
--p-accordion-transition-duration: 0.2s;
|
|
||||||
--p-accordion-panel-border-width: 0px;
|
|
||||||
--p-accordion-panel-border-color: none;
|
|
||||||
/* header */
|
|
||||||
--p-accordion-header-color: var(--text-action-high-blue-france);
|
|
||||||
--p-accordion-header-hover-color: var(--text-action-high-blue-france);
|
|
||||||
--p-accordion-header-active-color: var(--text-action-high-blue-france);
|
|
||||||
--p-accordion-header-active-hover-color: var(--text-action-high-blue-france);
|
|
||||||
--p-accordion-header-padding: 0.75rem 1rem;
|
|
||||||
--p-accordion-header-font-weight: var(--text-body-MD-standard-text-Medium-weight);
|
|
||||||
--p-accordion-header-border-radius: 0px;
|
|
||||||
--p-accordion-header-border-width: 1px;
|
|
||||||
--p-accordion-header-border-color: var(--border-default-grey);
|
|
||||||
--p-accordion-header-background: var(--background-transparent);
|
|
||||||
--p-accordion-header-hover-background: var(--background-transparent-hover);
|
|
||||||
--p-accordion-header-active-background: var(--background-open-blue-france);
|
|
||||||
--p-accordion-header-active-hover-background: var(--background-open-blue-france-hover);
|
|
||||||
/* header first top border */
|
|
||||||
--p-accordion-header-first-top-border-radius: 0px;
|
|
||||||
--p-accordion-header-first-border-width: 1px;
|
|
||||||
/* header last bottom border */
|
|
||||||
--p-accordion-header-last-bottom-border-radius: 0px;
|
|
||||||
--p-accordion-header-last-active-bottom-border-radius: 0px;
|
|
||||||
/* focus */
|
|
||||||
--p-accordion-header-focus-ring-width: var(--focus-width);
|
|
||||||
--p-accordion-header-focus-ring-style: var(--focus-style);
|
|
||||||
--p-accordion-header-focus-ring-color: var(--focus-color);
|
|
||||||
--p-accordion-header-focus-ring-offset: var(--focus-offset);
|
|
||||||
/* icon */
|
|
||||||
--p-accordion-header-toggle-icon-color: var(--text-action-high-blue-france);
|
|
||||||
--p-accordion-header-toggle-icon-hover-color: var(--text-action-high-blue-france);
|
|
||||||
--p-accordion-header-toggle-icon-active-color: var(--text-action-high-blue-france);
|
|
||||||
--p-accordion-header-toggle-icon-active-hover-color: var(--text-action-high-blue-france);
|
|
||||||
/* content */
|
|
||||||
--p-accordion-content-border-width: 0px;
|
|
||||||
--p-accordion-content-border-color: none;
|
|
||||||
--p-accordion-content-background: var(--background-transparent);
|
|
||||||
--p-accordion-content-color: var(--text-default-grey);
|
|
||||||
--p-accordion-content-padding: 0px;
|
|
||||||
}
|
|
|
@ -64,8 +64,4 @@
|
||||||
--p-button-primary-focus-ring-color: var(--focus-color);
|
--p-button-primary-focus-ring-color: var(--focus-color);
|
||||||
--p-button-secondary-focus-ring-color: var(--focus-color);
|
--p-button-secondary-focus-ring-color: var(--focus-color);
|
||||||
--p-button-danger-focus-ring-color: var(--focus-color);
|
--p-button-danger-focus-ring-color: var(--focus-color);
|
||||||
/* link */
|
|
||||||
--p-button-link-color: var(--text-action-high-blue-france);
|
|
||||||
--p-button-link-hover-color: var(--text-action-high-blue-france);
|
|
||||||
--p-button-link-active-color: var(--text-action-high-blue-france);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
:root{
|
|
||||||
/* size */
|
|
||||||
--p-checkbox-width: 1.5rem;
|
|
||||||
--p-checkbox-height: 1.5rem;
|
|
||||||
--p-checkbox-sm-width: 1rem;
|
|
||||||
--p-checkbox-sm-height: 1rem;
|
|
||||||
/* border */
|
|
||||||
--p-checkbox-border-radius: 0px;
|
|
||||||
--p-checkbox-border-color: var(--border-action-high-blue-france);
|
|
||||||
--p-checkbox-hover-border-color: var(--border-action-high-blue-france);
|
|
||||||
/* background */
|
|
||||||
--p-checkbox-background: var(--background-default-grey);
|
|
||||||
/* icon */
|
|
||||||
--p-checkbox-icon-size: 1rem;
|
|
||||||
--p-checkbox-icon-sm-size: 1rem;
|
|
||||||
--p-checkbox-icon-color: var(--background-default-grey);
|
|
||||||
/* focus */
|
|
||||||
--p-checkbox-checked-focus-border-color: var(--border-action-high-blue-france);
|
|
||||||
--p-checkbox-focus-border-color: var(--border-action-high-blue-france);
|
|
||||||
--p-checkbox-focus-ring-color: var(--focus-color);
|
|
||||||
--p-checkbox-focus-ring-width: var(--focus-width);
|
|
||||||
--p-checkbox-focus-ring-style: var(--focus-style);
|
|
||||||
--p-checkbox-focus-ring-offset: var(--focus-offset);
|
|
||||||
/* checked */
|
|
||||||
--p-checkbox-checked-border-color: var(--border-action-high-blue-france);
|
|
||||||
--p-checkbox-checked-background: var(--border-active-blue-france);
|
|
||||||
--p-checkbox-icon-checked-color: var(--background-default-grey);
|
|
||||||
/* checked:hover */
|
|
||||||
--p-checkbox-icon-checked-hover-color: var(--background-default-grey);
|
|
||||||
--p-checkbox-checked-hover-border-color: var(--border-action-high-blue-france);
|
|
||||||
--p-checkbox-checked-hover-background: var(--border-active-blue-france);
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
:root {
|
|
||||||
--p-confirmdialog-content-gap: 1rem;
|
|
||||||
--p-confirmdialog-icon-size: var(--titles-H6-XXS-size);
|
|
||||||
--p-confirmdialog-icon-color: var(--text-title-grey);
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-datatable-paginator-bottom-border-color: var(--p-datatable-border-color);
|
|
||||||
--p-datatable-paginator-bottom-border-width: 0 0 1px 0;
|
|
||||||
--p-datatable-paginator-top-border-color: var(--p-datatable-border-color);
|
|
||||||
--p-datatable-paginator-top-border-width: 0 0 1px 0;
|
|
||||||
--p-datatable-filter-inline-gap: 0.5rem;
|
|
||||||
--p-datatable-filter-constraint-focus-background: var(--p-list-option-focus-background);
|
|
||||||
--p-datatable-filter-constraint-selected-background: var(--p-list-option-selected-background);
|
|
||||||
--p-datatable-filter-constraint-selected-focus-background: var(--p-list-option-selected-focus-background);
|
|
||||||
--p-datatable-filter-constraint-color: var(--p-list-option-color);
|
|
||||||
--p-datatable-filter-constraint-focus-color: var(--p-list-option-focus-color);
|
|
||||||
--p-datatable-filter-constraint-selected-color: var(--p-list-option-selected-color);
|
|
||||||
--p-datatable-filter-constraint-selected-focus-color: var(--p-list-option-selected-focus-color);
|
|
||||||
--p-datatable-filter-constraint-padding: var(--p-list-option-padding);
|
|
||||||
--p-datatable-filter-constraint-border-radius: var(--p-list-option-border-radius);
|
|
||||||
--p-datatable-filter-constraint-separator-border-color: var(--p-content-border-color);
|
|
||||||
--p-datatable-filter-constraint-list-padding: var(--p-list-padding);
|
|
||||||
--p-datatable-filter-constraint-list-gap: var(--p-list-gap);
|
|
||||||
--p-datatable-filter-rule-border-color: var(--p-content-border-color);
|
|
||||||
--p-datatable-filter-overlay-popover-background: var(--p-overlay-popover-background);
|
|
||||||
--p-datatable-filter-overlay-popover-border-color: var(--p-overlay-popover-border-color);
|
|
||||||
--p-datatable-filter-overlay-popover-border-radius: var(--p-overlay-popover-border-radius);
|
|
||||||
--p-datatable-filter-overlay-popover-color: var(--p-overlay-popover-color);
|
|
||||||
--p-datatable-filter-overlay-popover-shadow: var(--p-overlay-popover-shadow);
|
|
||||||
--p-datatable-filter-overlay-popover-padding: var(--p-overlay-popover-padding);
|
|
||||||
--p-datatable-filter-overlay-popover-gap: 0.5rem;
|
|
||||||
--p-datatable-filter-overlay-select-background: var(--p-overlay-select-background);
|
|
||||||
--p-datatable-filter-overlay-select-border-color: var(--p-overlay-select-border-color);
|
|
||||||
--p-datatable-filter-overlay-select-border-radius: var(--p-overlay-select-border-radius);
|
|
||||||
--p-datatable-filter-overlay-select-color: var(--p-overlay-select-color);
|
|
||||||
--p-datatable-filter-overlay-select-shadow: var(--p-overlay-select-shadow);
|
|
||||||
--p-datatable-row-toggle-button-hover-background: var(--datatable-hover-background);
|
|
||||||
--p-datatable-row-toggle-button-selected-hover-background: var(--datatable-background);
|
|
||||||
--p-datatable-row-toggle-button-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-row-toggle-button-hover-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-row-toggle-button-selected-hover-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-row-toggle-button-size: 1.75rem;
|
|
||||||
--p-datatable-row-toggle-button-border-radius: 0px;
|
|
||||||
--p-datatable-row-toggle-button-focus-ring-width: var(--focus-width);
|
|
||||||
--p-datatable-row-toggle-button-focus-ring-style: var(--focus-style);
|
|
||||||
--p-datatable-row-toggle-button-focus-ring-color: var(--focus-color);
|
|
||||||
--p-datatable-row-toggle-button-focus-ring-offset: var(--focus-offset);
|
|
||||||
/* --p-datatable-row-toggle-button-focus-ring-shadow: var(--p-focus-ring-shadow); */
|
|
||||||
--p-datatable-loading-icon-size: 2rem;
|
|
||||||
--p-datatable-sort-icon-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-sort-icon-hover-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-sort-icon-size: 0.875rem;
|
|
||||||
--p-datatable-resize-indicator-width: 1px;
|
|
||||||
--p-datatable-resize-indicator-color: var(--p-primary-color);
|
|
||||||
--p-datatable-column-resizer-width: 0.5rem;
|
|
||||||
--p-datatable-drop-point-color: var(--p-primary-color);
|
|
||||||
--p-datatable-footer-background: var(--datatable-background);
|
|
||||||
--p-datatable-footer-border-color: var(--p-datatable-border-color);
|
|
||||||
--p-datatable-footer-color: var(--p-content-color);
|
|
||||||
--p-datatable-footer-border-width: 0 0 1px 0;
|
|
||||||
--p-datatable-footer-padding: 0.75rem;
|
|
||||||
--p-datatable-footer-lg-padding: 1rem;
|
|
||||||
--p-datatable-footer-sm-padding: 0.5rem;
|
|
||||||
--p-datatable-column-footer-font-weight: 600;
|
|
||||||
--p-datatable-footer-cell-background: var(--datatable-background);
|
|
||||||
--p-datatable-footer-cell-border-color: var(--p-datatable-border-color);
|
|
||||||
--p-datatable-footer-cell-color: var(--p-content-color);
|
|
||||||
--p-datatable-footer-cell-padding: 0.75rem;
|
|
||||||
--p-datatable-footer-cell-lg-padding: 1rem;
|
|
||||||
--p-datatable-footer-cell-sm-padding: 0.5rem;
|
|
||||||
--p-datatable-body-cell-border-color: var(--p-datatable-border-color);
|
|
||||||
--p-datatable-body-cell-padding: 0.75rem;
|
|
||||||
--p-datatable-body-cell-lg-padding: 1rem;
|
|
||||||
--p-datatable-body-cell-sm-padding: 0.5rem;
|
|
||||||
--p-datatable-row-background: var(--datatable-background);
|
|
||||||
--p-datatable-row-hover-background: var(--datatable-hover-background);
|
|
||||||
--p-datatable-row-selected-background: var(--datatable-active-background);
|
|
||||||
--p-datatable-row-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-row-hover-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-row-selected-color: var(--datatable-row-color);
|
|
||||||
--p-datatable-row-focus-ring-width: var(--focus-width);
|
|
||||||
--p-datatable-row-focus-ring-style: var(--focus-style);
|
|
||||||
--p-datatable-row-focus-ring-color: var(--focus-color);
|
|
||||||
--p-datatable-row-focus-ring-offset: -1px;
|
|
||||||
/* --p-datatable-row-focus-ring-shadow: var(--p-focus-ring-shadow); */
|
|
||||||
--p-datatable-column-title-font-weight: var(--text-body-SM-detail-text-Medium-weight);
|
|
||||||
--p-datatable-header-cell-background: var(--primary-color-850-blue-france-default);
|
|
||||||
--p-datatable-header-cell-hover-background: var(--primary-color-850-blue-france-hover);
|
|
||||||
--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-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-gap: 0.5rem;
|
|
||||||
--p-datatable-header-cell-padding: 0.75rem;
|
|
||||||
--p-datatable-header-cell-lg-padding: 1rem;
|
|
||||||
--p-datatable-header-cell-sm-padding: 0.5rem;
|
|
||||||
--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-color: var(--focus-color);
|
|
||||||
--p-datatable-header-cell-focus-ring-offset: -1px;
|
|
||||||
/* --p-datatable-header-cell-focus-ring-shadow: var(--p-focus-ring-shadow); */
|
|
||||||
--p-datatable-header-background: var(--datatable-background);
|
|
||||||
--p-datatable-header-border-color: transparent;
|
|
||||||
--p-datatable-header-color: var(--datatable-header-cell-color);
|
|
||||||
--p-datatable-header-border-width: 0 0 1px 0;
|
|
||||||
--p-datatable-header-padding: 0.75rem;
|
|
||||||
--p-datatable-header-lg-padding: 1rem;
|
|
||||||
--p-datatable-header-sm-padding: 0.5rem;
|
|
||||||
/* --p-datatable-transition-duration: var(--p-transition-duration); */
|
|
||||||
--p-datatable-body-cell-selected-border-color: var(--datatable-active-background);
|
|
||||||
--p-datatable-row-striped-background: var(--datatable-background);
|
|
||||||
--p-datatable-border-color: var(--p-content-border-color);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-dialog-background: var(--background-lifted-grey);
|
|
||||||
--p-dialog-border-color: var(--border-default-grey);
|
|
||||||
--p-dialog-color: var(--text-title-grey);
|
|
||||||
--p-dialog-border-radius: 0px;
|
|
||||||
--p-dialog-shadow: var(--shadow);
|
|
||||||
--p-dialog-header-padding: 1rem;
|
|
||||||
--p-dialog-header-gap: 0.5rem;
|
|
||||||
--p-dialog-title-font-size: var(--titles-H4-SM-size);
|
|
||||||
--p-dialog-title-font-weight: var( --titles-H4-SM-weight);
|
|
||||||
--p-dialog-content-padding: 0 1rem 1rem 1rem;
|
|
||||||
--p-dialog-footer-padding: 1rem;
|
|
||||||
--p-dialog-footer-gap: 1rem;
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-divider-vertical-margin: 0 1rem;
|
|
||||||
--p-divider-vertical-padding: 0.5rem 0;
|
|
||||||
--p-divider-vertical-content-padding: 0.5rem 0;
|
|
||||||
--p-divider-horizontal-margin: 1rem 0;
|
|
||||||
--p-divider-horizontal-padding: 0 1rem;
|
|
||||||
--p-divider-horizontal-content-padding: 0 0.5rem;
|
|
||||||
--p-divider-content-background: var(--background-default-grey);
|
|
||||||
--p-divider-content-color: var(--text-label-grey);
|
|
||||||
--p-divider-border-color: var(--border-default-grey);
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
:root {
|
|
||||||
--p-fileupload-basic-gap: 0.5rem;
|
|
||||||
--p-fileupload-progressbar-height: 0.25rem;
|
|
||||||
--p-fileupload-file-list-gap: 0.5rem;
|
|
||||||
--p-fileupload-file-padding: 1rem;
|
|
||||||
--p-fileupload-file-gap: 1rem;
|
|
||||||
--p-fileupload-file-border-color: var(--border-default-grey);
|
|
||||||
--p-fileupload-file-info-gap: 0.5rem;
|
|
||||||
--p-fileupload-content-highlight-border-color: var(--border-action-high-grey);
|
|
||||||
--p-fileupload-content-padding: 0rem;
|
|
||||||
--p-fileupload-content-gap: 1rem;
|
|
||||||
--p-fileupload-header-background: transparent;
|
|
||||||
--p-fileupload-header-color: var(--text-default-grey);
|
|
||||||
--p-fileupload-header-padding: 0rem;
|
|
||||||
--p-fileupload-header-border-color: unset;
|
|
||||||
--p-fileupload-header-border-width: 0;
|
|
||||||
--p-fileupload-header-border-radius: 0;
|
|
||||||
--p-fileupload-header-gap: 0.5rem;
|
|
||||||
--p-fileupload-background: var(--background-default-grey);
|
|
||||||
--p-fileupload-border-color: var(--border-default-grey);
|
|
||||||
--p-fileupload-color: var(--text-default-grey);
|
|
||||||
--p-fileupload-border-radius: 0px;
|
|
||||||
--p-fileupload-transition-duration: var(--transition-duration);
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
:root {
|
|
||||||
--p-form-field-background: var(--input-background);
|
|
||||||
--p-form-field-disabled-background: var(--input-disabled-background);
|
|
||||||
--p-form-field-border-color: var(--input-border-color);
|
|
||||||
--p-form-field-hover-border-color: var(--input-border-color);
|
|
||||||
--p-form-field-focus-border-color: var(--input-border-color);
|
|
||||||
--p-form-field-color: var(--input-color);
|
|
||||||
--p-form-field-disabled-color: var(--input-disabled-color);
|
|
||||||
--p-form-field-placeholder-color: var(--input-color);
|
|
||||||
--p-form-field-icon-color: var(--input-color);
|
|
||||||
--p-form-field-padding-x: var(--input-padding-x);
|
|
||||||
--p-form-field-padding-y: var(--input-padding-y);
|
|
||||||
--p-form-field-border-radius: var(--input-border-raduis);
|
|
||||||
--p-form-field-transition-duration: var(--transition-duration);
|
|
||||||
--p-form-field-focus-ring-width: 0;
|
|
||||||
--p-form-field-focus-ring-style: none;
|
|
||||||
--p-form-field-focus-ring-color: transparent;
|
|
||||||
--p-form-field-focus-ring-offset: 0;
|
|
||||||
--p-form-field-focus-ring-shadow: none;
|
|
||||||
--p-form-field-lg-font-size: 1.125rem;
|
|
||||||
--p-form-field-lg-padding-x: 0.875rem;
|
|
||||||
--p-form-field-lg-padding-y: 0.625rem;
|
|
||||||
--p-form-field-sm-font-size: 0.875rem;
|
|
||||||
--p-form-field-sm-padding-x: 0.625rem;
|
|
||||||
--p-form-field-sm-padding-y: 0.375rem;
|
|
||||||
--p-form-field-invalid-border-color: var(--border-plain-error);
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-iconfield-icon-color: var(--input-color);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-inputtext-background: var(--input-background);
|
|
||||||
--p-inputtext-disabled-background: var(--input-disabled-background);
|
|
||||||
--p-inputtext-border-color: var(--input-border-color);
|
|
||||||
--p-inputtext-hover-border-color: var(--input-border-color);
|
|
||||||
--p-inputtext-focus-border-color: var(--input-border-color);
|
|
||||||
--p-inputtext-color: var(--input-color);
|
|
||||||
--p-inputtext-disabled-color: var(--input-disabled-color);
|
|
||||||
--p-inputtext-placeholder-color: var(--input-color);
|
|
||||||
--p-inputtext-padding-x: var(--input-padding-x);
|
|
||||||
--p-inputtext-padding-y: var(--input-padding-y);
|
|
||||||
--p-inputtext-border-radius: var(--input-border-raduis);
|
|
||||||
--p-inputtext-focus-ring-color: var(--focus-color);
|
|
||||||
--p-inputtext-focus-ring-width: var(--focus-width);
|
|
||||||
--p-inputtext-focus-ring-style: var(--focus-style);
|
|
||||||
--p-inputtext-focus-ring-offset: var(--focus-offset);
|
|
||||||
--p-inputtext-invalid-border-color: var(--border-plain-error);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-list-option-group-padding: 0.25rem;
|
|
||||||
--p-list-option-group-font-weight: var(--text-body-MD-standard-text-Regular-weight);
|
|
||||||
--p-list-option-padding: 0.25rem;
|
|
||||||
--p-list-option-border-radius: 0px;
|
|
||||||
--p-list-option-group-background: var(--input-background);
|
|
||||||
--p-list-option-group-color: var(--input-color);
|
|
||||||
--p-list-option-focus-background: var(--background-transparent-active);
|
|
||||||
--p-list-option-selected-background: var(--background-active-blue-france);
|
|
||||||
--p-list-option-selected-focus-background: var(--background-active-blue-france);
|
|
||||||
--p-list-option-color: var(--input-color);
|
|
||||||
--p-list-option-focus-color: var(--input-color);
|
|
||||||
--p-list-option-selected-color: var(--text-inverted-blue-france);
|
|
||||||
--p-list-option-selected-focus-color: var(--text-inverted-blue-france);
|
|
||||||
--p-list-option-icon-color: var(--input-color);
|
|
||||||
--p-list-option-icon-focus-color: var(--input-color);
|
|
||||||
--p-list-padding: 0.25rem;
|
|
||||||
--p-list-gap: 0.125rem;
|
|
||||||
--p-list-header-padding: 0.5rem 1rem;
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
:root {
|
|
||||||
--p-menubar-mobile-button-border-radius: var(--menu-border-radius);
|
|
||||||
--p-menubar-mobile-button-size: 1.75rem;
|
|
||||||
--p-menubar-mobile-button-color: var(--text-active-blue-france);
|
|
||||||
--p-menubar-mobile-button-hover-color: var(--text-active-blue-france);
|
|
||||||
--p-menubar-mobile-button-hover-background: var(--background-transparent-active);
|
|
||||||
--p-menubar-mobile-button-focus-ring-width: var(--focus-width);
|
|
||||||
--p-menubar-mobile-button-focus-ring-style: var(--focus-style);
|
|
||||||
--p-menubar-mobile-button-focus-ring-color: var(--focus-color);
|
|
||||||
--p-menubar-mobile-button-focus-ring-offset: var(--focus-offset);
|
|
||||||
/* --p-menubar-mobile-button-focus-ring-shadow: */
|
|
||||||
--p-menubar-separator-border-color: var(--border-default-grey);
|
|
||||||
--p-menubar-submenu-padding: var(--p-navigation-list-padding);
|
|
||||||
--p-menubar-submenu-gap: var(--p-navigation-list-gap);
|
|
||||||
--p-menubar-submenu-background: var(--background-transparent);
|
|
||||||
--p-menubar-submenu-border-color: var(--border-default-grey);
|
|
||||||
--p-menubar-submenu-border-radius: var(--menu-border-radius);
|
|
||||||
--p-menubar-submenu-shadow: var(--shadow);
|
|
||||||
--p-menubar-submenu-mobile-indent: 1rem;
|
|
||||||
--p-menubar-submenu-icon-size: var(--p-navigation-submenu-icon-size);
|
|
||||||
--p-menubar-submenu-icon-color: var(--p-navigation-submenu-icon-color);
|
|
||||||
--p-menubar-submenu-icon-focus-color: var(--p-navigation-submenu-icon-focus-color);
|
|
||||||
--p-menubar-submenu-icon-active-color: var(--p-navigation-submenu-icon-active-color);
|
|
||||||
--p-menubar-item-focus-background: var(--p-navigation-item-focus-background);
|
|
||||||
--p-menubar-item-active-background: var(--p-navigation-item-active-background);
|
|
||||||
--p-menubar-item-color: var(--p-navigation-item-color);
|
|
||||||
--p-menubar-item-focus-color: var(--p-navigation-item-focus-color);
|
|
||||||
--p-menubar-item-active-color: var(--p-navigation-item-active-color);
|
|
||||||
--p-menubar-item-padding: var(--p-navigation-item-padding);
|
|
||||||
--p-menubar-item-border-radius: var(--p-navigation-item-border-radius);
|
|
||||||
--p-menubar-item-gap: var(--p-navigation-item-gap);
|
|
||||||
--p-menubar-item-icon-color: var(--p-navigation-item-icon-color);
|
|
||||||
--p-menubar-item-icon-focus-color: var(--p-navigation-item-icon-focus-color);
|
|
||||||
--p-menubar-item-icon-active-color: var(--p-navigation-item-icon-active-color);
|
|
||||||
--p-menubar-base-item-border-radius: var(--menu-border-radius);
|
|
||||||
--p-menubar-base-item-padding: var(--p-navigation-item-padding);
|
|
||||||
--p-menubar-background: var(--background-transparent);
|
|
||||||
--p-menubar-border-color: var(--border-default-grey);
|
|
||||||
--p-menubar-border-radius: var(--menu-border-radius);
|
|
||||||
--p-menubar-color: var(--menu-default-color);
|
|
||||||
--p-menubar-gap: 0.5rem;
|
|
||||||
--p-menubar-padding: var(--menu-padding);
|
|
||||||
--p-menubar-transition-duration: var(--transition-duration);
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
:root {
|
|
||||||
--p-message-simple-content-padding: 0;
|
|
||||||
--p-message-outlined-border-width: var(--border-width);
|
|
||||||
--p-message-close-icon-size: 1rem;
|
|
||||||
--p-message-close-icon-lg-size: 1.125rem;
|
|
||||||
--p-message-close-icon-sm-size: 0.875rem;
|
|
||||||
--p-message-close-button-width: 1.75rem;
|
|
||||||
--p-message-close-button-height: 1.75rem;
|
|
||||||
--p-message-close-button-border-radius: 0px;
|
|
||||||
--p-message-close-button-focus-ring-width: var(--focus-width);
|
|
||||||
--p-message-close-button-focus-ring-style: var(--focus-style);
|
|
||||||
--p-message-close-button-focus-ring-offset: var(--focus-offset);
|
|
||||||
--p-message-icon-size: calc(0.125rem + var(--text-body-MD-standard-text-Regular-size));
|
|
||||||
--p-message-icon-lg-size: calc(0.125rem + var(--text-body-MD-standard-text-Regular-size));
|
|
||||||
--p-message-icon-sm-size: calc(0.125rem + var(--text-body-XS-mention-text-Regular-size));
|
|
||||||
--p-message-text-font-size: var(--text-body-MD-standard-text-Regular-size);
|
|
||||||
--p-message-text-font-weight: var(--text-body-XS-mention-text-Regular-weight);
|
|
||||||
--p-message-text-lg-font-size: var(--text-body-MD-standard-text-Regular-size);
|
|
||||||
--p-message-text-sm-font-size: var(--text-body-XS-mention-text-Regular-size);
|
|
||||||
--p-message-content-padding: 0.5rem 0.75rem;
|
|
||||||
--p-message-content-gap: 0.5rem;
|
|
||||||
--p-message-content-lg-padding: 0.625rem 0.875rem;
|
|
||||||
--p-message-content-sm-padding: 0.375rem 0.625rem;
|
|
||||||
--p-message-border-radius: 0px;
|
|
||||||
--p-message-border-width: var(--border-width);
|
|
||||||
--p-message-transition-duration: var(--transition-duration);
|
|
||||||
/* --p-message-contrast-background:
|
|
||||||
--p-message-contrast-border-color:
|
|
||||||
--p-message-contrast-color:
|
|
||||||
--p-message-contrast-shadow: */
|
|
||||||
--p-message-contrast-simple-color: var(--text-action-high-blue-france);
|
|
||||||
/* --p-message-contrast-outlined-color:
|
|
||||||
--p-message-contrast-outlined-border-color:
|
|
||||||
--p-message-contrast-close-button-hover-background:
|
|
||||||
--p-message-contrast-close-button-focus-ring-color:
|
|
||||||
--p-message-contrast-close-button-focus-ring-shadow: */
|
|
||||||
--p-message-secondary-background: none;
|
|
||||||
--p-message-secondary-border-color: none;
|
|
||||||
--p-message-secondary-color: none;
|
|
||||||
--p-message-secondary-shadow: none;
|
|
||||||
--p-message-secondary-simple-color: var(--text-mention-grey);
|
|
||||||
--p-message-secondary-outlined-color: none;
|
|
||||||
--p-message-secondary-outlined-border-color: none;
|
|
||||||
--p-message-secondary-close-button-hover-background: none;
|
|
||||||
--p-message-secondary-close-button-focus-ring-color: none;
|
|
||||||
--p-message-secondary-close-button-focus-ring-shadow: none;
|
|
||||||
--p-message-error-background: color-mix(in srgb,var(--background-contrast-error),transparent 5%);
|
|
||||||
--p-message-error-border-color: var(--border-plain-error);
|
|
||||||
--p-message-error-color: var(--text-default-grey);
|
|
||||||
--p-message-error-shadow: none;
|
|
||||||
--p-message-error-simple-color: var(--text-default-error);
|
|
||||||
--p-message-error-outlined-color: none;
|
|
||||||
--p-message-error-outlined-border-color: none;
|
|
||||||
--p-message-error-close-button-hover-background: none;
|
|
||||||
--p-message-error-close-button-focus-ring-color: none;
|
|
||||||
--p-message-error-close-button-focus-ring-shadow: none;
|
|
||||||
--p-message-warn-background: color-mix(in srgb,var(--background-contrast-warning),transparent 5%);
|
|
||||||
--p-message-warn-border-color: var(--border-plain-warning);
|
|
||||||
--p-message-warn-color: var(--text-default-grey);
|
|
||||||
--p-message-warn-shadow: none;
|
|
||||||
--p-message-warn-simple-color: var(--text-default-warning);
|
|
||||||
--p-message-warn-outlined-color: none;
|
|
||||||
--p-message-warn-outlined-border-color: none;
|
|
||||||
--p-message-warn-close-button-hover-background: none;
|
|
||||||
--p-message-warn-close-button-focus-ring-color: none;
|
|
||||||
--p-message-warn-close-button-focus-ring-shadow: none;
|
|
||||||
--p-message-success-background: color-mix(in srgb,var(--background-contrast-success),transparent 5%);
|
|
||||||
--p-message-success-border-color: var(--border-plain-success);
|
|
||||||
--p-message-success-color: var(--text-default-grey);
|
|
||||||
--p-message-success-shadow: none;
|
|
||||||
--p-message-success-simple-color: var(--text-default-success);
|
|
||||||
--p-message-success-outlined-color: var(none);
|
|
||||||
--p-message-success-outlined-border-color: var(none);
|
|
||||||
--p-message-success-close-button-hover-background: none;
|
|
||||||
--p-message-success-close-button-focus-ring-color: none;
|
|
||||||
--p-message-success-close-button-focus-ring-shadow: none;
|
|
||||||
--p-message-info-background: color-mix(in srgb,var(--background-contrast-info),transparent 5%);
|
|
||||||
--p-message-info-border-color: var(--border-plain-info);
|
|
||||||
--p-message-info-color: var(--text-default-grey);
|
|
||||||
--p-message-info-shadow: none;
|
|
||||||
--p-message-info-simple-color: var(--text-default-info);
|
|
||||||
--p-message-info-outlined-color: none;
|
|
||||||
--p-message-info-outlined-border-color: none;
|
|
||||||
--p-message-info-close-button-hover-background: none;
|
|
||||||
--p-message-info-close-button-focus-ring-color: none;
|
|
||||||
--p-message-info-close-button-focus-ring-shadow: none;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-navigation-submenu-icon-size: 0.875rem;
|
|
||||||
--p-navigation-submenu-label-padding: var(--menu-padding);
|
|
||||||
--p-navigation-submenu-label-font-weight: var( --titles-H4-SM-weight);
|
|
||||||
--p-navigation-item-padding: calc(var(--menu-padding) * 3);
|
|
||||||
--p-navigation-item-border-radius: var(--p-border-radius-sm);
|
|
||||||
--p-navigation-item-gap: var(--menu-item-gap);
|
|
||||||
--p-navigation-list-padding: var(--menu-padding);
|
|
||||||
--p-navigation-list-gap: var(--menu-gap);
|
|
||||||
--p-navigation-submenu-icon-color: var(--menu-default-color);
|
|
||||||
--p-navigation-submenu-icon-focus-color: var(--menu-active-color);
|
|
||||||
--p-navigation-submenu-icon-active-color: var(--menu-active-color);
|
|
||||||
--p-navigation-submenu-label-background: var(--menu-default-background);
|
|
||||||
--p-navigation-submenu-label-color: var(--menu-default-color);
|
|
||||||
--p-navigation-item-focus-background: var(--menu-default-background);
|
|
||||||
--p-navigation-item-active-background: var(--menu-default-background);
|
|
||||||
--p-navigation-item-color: var(--menu-default-color);
|
|
||||||
--p-navigation-item-focus-color: var(--menu-active-color);
|
|
||||||
--p-navigation-item-active-color: var(--menu-active-color);
|
|
||||||
--p-navigation-item-icon-color: var(--menu-default-color);
|
|
||||||
--p-navigation-item-icon-focus-color: var(--menu-active-color);
|
|
||||||
--p-navigation-item-icon-active-color: var(--menu-active-color);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-overlay-modal-background: var(--background-lifted-grey);
|
|
||||||
--p-overlay-modal-border-color: var(--border-default-grey);
|
|
||||||
--p-overlay-modal-color: var(--text-default-grey);
|
|
||||||
--p-overlay-popover-background: var(--background-lifted-grey);
|
|
||||||
--p-overlay-popover-border-color: var(--border-default-grey);
|
|
||||||
--p-overlay-popover-color: var(--text-default-grey);
|
|
||||||
--p-overlay-navigation-shadow: var(--shadow);
|
|
||||||
--p-overlay-modal-border-radius: 0px;
|
|
||||||
--p-overlay-modal-padding: 1.25rem;
|
|
||||||
--p-overlay-modal-shadow: var(--shadow);
|
|
||||||
--p-overlay-popover-border-radius: 0px;
|
|
||||||
--p-overlay-popover-padding: 0.75rem;
|
|
||||||
--p-overlay-popover-shadow: var(--shadow);
|
|
||||||
--p-overlay-select-border-radius: 0px;
|
|
||||||
--p-overlay-select-shadow: var(--shadow);
|
|
||||||
--p-overlay-select-background: var(--input-background);
|
|
||||||
--p-overlay-select-border-color: var(--input-border-color);
|
|
||||||
--p-overlay-select-color: var(--input-color);
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-paginator-jump-to-page-input-max-width: 2.5rem;
|
|
||||||
--p-paginator-current-page-report-color: var(--text-action-high-grey);
|
|
||||||
--p-paginator-nav-button-background: transparent;
|
|
||||||
--p-paginator-nav-button-hover-background: var(--background-transparent-hover);
|
|
||||||
--p-paginator-nav-button-selected-background: var(--background-action-high-blue-france);
|
|
||||||
--p-paginator-nav-button-color: var(--text-action-high-grey);
|
|
||||||
--p-paginator-nav-button-hover-color: var(--text-action-high-grey);
|
|
||||||
--p-paginator-nav-button-selected-color: var(--text-inverted-blue-france);
|
|
||||||
--p-paginator-nav-button-width: 2rem;
|
|
||||||
--p-paginator-nav-button-height: 2rem;
|
|
||||||
--p-paginator-nav-button-border-radius: 0px;
|
|
||||||
--p-paginator-nav-button-focus-ring-width: var(--focus-width);
|
|
||||||
--p-paginator-nav-button-focus-ring-style: var(--focus-style);
|
|
||||||
--p-paginator-nav-button-focus-ring-color: var(--focus-color);
|
|
||||||
--p-paginator-nav-button-focus-ring-offset: var(--focus-offset);
|
|
||||||
/* --p-paginator-nav-button-focus-ring-shadow: var(--p-focus-ring-shadow); */
|
|
||||||
--p-paginator-padding: 0px;
|
|
||||||
--p-paginator-gap: 1rem;
|
|
||||||
--p-paginator-border-radius: 0px;
|
|
||||||
--p-paginator-background: transparent;
|
|
||||||
--p-paginator-color: var(--text-action-high-grey);
|
|
||||||
--p-paginator-transition-duration: var(--transition-duration);
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-password-meter-background: var(--background-contrast-grey);
|
|
||||||
--p-password-meter-border-radius: 0.75rem;
|
|
||||||
--p-password-meter-height: var(--input-padding-y);
|
|
||||||
--p-password-icon-color: var(--input-color);
|
|
||||||
--p-password-overlay-background: var(--input-background);
|
|
||||||
--p-password-overlay-border-color: var(--input-border-color);
|
|
||||||
--p-password-overlay-border-radius: 0px;
|
|
||||||
--p-password-overlay-color: var(--input-color);
|
|
||||||
--p-password-overlay-padding: 0.75rem;
|
|
||||||
--p-password-overlay-shadow: var(--shadow);
|
|
||||||
--p-password-content-gap: 0.125rem;
|
|
||||||
--p-password-strength-weak-background: var(--background-action-high-error);
|
|
||||||
--p-password-strength-medium-background: var(--background-action-high-warning);
|
|
||||||
--p-password-strength-strong-background: var(--background-action-high-success);
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-progressbar-background: var(--background-contrast-grey);
|
|
||||||
--p-progressbar-height: 1rem;
|
|
||||||
--p-progressbar-border-radius: 0.75rem;
|
|
||||||
--p-progressbar-value-background: var(--background-active-blue-france);
|
|
||||||
--p-progressbar-label-color: var(--text-inverted-grey);
|
|
||||||
--p-progressbar-label-font-size: var(--text-body-XS-mention-text-Medium-size);
|
|
||||||
--p-progressbar-label-font-weight: var(--text-body-XS-mention-text-Medium-weight);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
:root {
|
|
||||||
--p-scrollpanel-bar-size: 0.5rem;
|
|
||||||
--p-scrollpanel-bar-border-radius: 0.25rem;
|
|
||||||
--p-scrollpanel-bar-focus-ring-width: var(--focus-width);
|
|
||||||
--p-scrollpanel-bar-focus-ring-style: var(--focus-style);
|
|
||||||
--p-scrollpanel-bar-focus-ring-color: var(--focus-color);
|
|
||||||
--p-scrollpanel-bar-focus-ring-offset: var(--focus-offset);
|
|
||||||
--p-scrollpanel-bar-focus-ring-shadow: none;
|
|
||||||
--p-scrollpanel-transition-duration: var(--transition-duration);
|
|
||||||
--p-scrollpanel-bar-background: var(--background-overlay-grey);
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-select-background: var(--input-background);
|
|
||||||
--p-select-disabled-background: var(--input-disabled-background);
|
|
||||||
--p-select-border-color: var(--input-border-color);
|
|
||||||
--p-select-hover-border-color: var(--input-border-color);
|
|
||||||
--p-select-color: var(--input-color);
|
|
||||||
--p-select-disabled-color: var(--input-disabled-color);
|
|
||||||
--p-select-placeholder-color: var(--input-color);
|
|
||||||
--p-select-padding-x: var(--input-padding-x);
|
|
||||||
--p-select-padding-y: var(--input-padding-y);
|
|
||||||
--p-select-border-radius: var(--input-border-raduis);
|
|
||||||
--p-select-dropdown-width: 2rem;
|
|
||||||
--p-select-dropdown-color: var(--input-color);
|
|
||||||
--p-select-overlay-background: var(--input-background);
|
|
||||||
--p-select-overlay-border-color: var(--input-border-color);
|
|
||||||
--p-select-overlay-border-radius: 0px 0px 0.25rem 0.25rem;
|
|
||||||
--p-select-overlay-color: var(--input-color);
|
|
||||||
--p-select-overlay-shadow: var(--shadow);
|
|
||||||
--p-select-list-padding: 0.25rem;
|
|
||||||
--p-select-list-gap: 0.125rem;
|
|
||||||
--p-select-list-header-padding: 0.5rem 1rem;
|
|
||||||
/* options */
|
|
||||||
--p-select-option-selected-background: var(--background-active-blue-france);
|
|
||||||
--p-select-option-selected-focus-background: var(--background-active-blue-france);
|
|
||||||
--p-select-option-color: var(--input-color);
|
|
||||||
--p-select-option-focus-color: var(--input-color);
|
|
||||||
--p-select-option-selected-color: var(--text-inverted-blue-france);
|
|
||||||
--p-select-option-padding: 0.5rem 0.75rem;
|
|
||||||
--p-select-option-border-radius: 0px;
|
|
||||||
--p-select-option-group-background: var(--input-background);
|
|
||||||
--p-select-option-group-color: var(--input-color);
|
|
||||||
--p-select-option-group-font-weight: var(--text-body-MD-standard-text-Regular-weight);
|
|
||||||
--p-select-option-group-padding: 0.25rem;
|
|
||||||
--p-select-clear-icon-color: var(--input-color);
|
|
||||||
--p-select-checkmark-color: var(--input-color);
|
|
||||||
--p-select-checkmark-gutter-start: 0.125rem;
|
|
||||||
--p-select-checkmark-gutter-end: 0.125rem;
|
|
||||||
--p-select-empty-message-padding: 0.25rem;
|
|
||||||
/* focus */
|
|
||||||
--p-select-focus-border-color: var(--input-border-color);
|
|
||||||
--p-select-focus-ring-width: var(--focus-width);
|
|
||||||
--p-select-focus-ring-style: var(--focus-style);
|
|
||||||
--p-select-focus-ring-color: var(--focus-color);
|
|
||||||
--p-select-focus-ring-offset: var(--focus-offset);
|
|
||||||
--p-select-option-focus-background: var(--background-transparent-active);
|
|
||||||
--p-select-option-selected-focus-color: var(--text-inverted-blue-france);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
:root {
|
|
||||||
--p-tag-icon-size: var(--text-body-MD-standard-text-Regular-size);
|
|
||||||
--p-tag-font-size: var( --text-body-SM-detail-text-Maj-size);
|
|
||||||
--p-tag-font-weight: var( --text-body-SM-detail-text-Maj-weight);
|
|
||||||
--p-tag-padding: 0.25rem 0.5rem;
|
|
||||||
--p-tag-gap: 0.25rem;
|
|
||||||
--p-tag-border-radius: 0.25rem;
|
|
||||||
/* --p-tag-rounded-border-radius:
|
|
||||||
--p-tag-contrast-background: */
|
|
||||||
--p-tag-contrast-color: var(--p-surface-0);
|
|
||||||
--p-tag-danger-background: var(--background-contrast-error);
|
|
||||||
--p-tag-danger-color: var(--text-default-error);
|
|
||||||
--p-tag-warn-background: var(--background-contrast-warning);
|
|
||||||
--p-tag-warn-color: var(--text-default-warning);
|
|
||||||
--p-tag-info-background: var(--background-contrast-info);
|
|
||||||
--p-tag-info-color: var(--text-default-info);
|
|
||||||
--p-tag-success-background: var(--background-contrast-success);
|
|
||||||
--p-tag-success-color: var(--text-default-success);
|
|
||||||
--p-tag-secondary-background: var(--illustration-color-950-tournesol-default);
|
|
||||||
--p-tag-secondary-color: var(--illustration-color-sun-tournesol-default);
|
|
||||||
--p-tag-primary-background: var(--illustration-color-950-glycine-default);
|
|
||||||
--p-tag-primary-color: var(--illustration-color-sun-glycine-default);
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-textarea-background: var(--input-background);
|
|
||||||
--p-textarea-disabled-background: var(--input-disabled-background);
|
|
||||||
--p-textarea-border-color: var(--input-border-color);
|
|
||||||
--p-textarea-hover-border-color: var(--input-border-color);
|
|
||||||
--p-textarea-focus-border-color: var(--input-border-color);
|
|
||||||
--p-textarea-color: var(--input-color);
|
|
||||||
--p-textarea-disabled-color: var(--input-disabled-color);
|
|
||||||
--p-textarea-placeholder-color: var(--input-color);
|
|
||||||
--p-textarea-padding-x: var(--input-padding-x);
|
|
||||||
--p-textarea-padding-y: var(--input-padding-y);
|
|
||||||
--p-textarea-border-radius: var(--input-border-raduis);
|
|
||||||
--p-textarea-focus-ring-color: var(--focus-color);
|
|
||||||
--p-textarea-focus-ring-width: var(--focus-width);
|
|
||||||
--p-textarea-focus-ring-style: var(--focus-style);
|
|
||||||
--p-textarea-focus-ring-offset: var(--focus-offset);
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-toast-width: 25rem;
|
|
||||||
--p-toast-border-radius: 0px;
|
|
||||||
--p-toast-border-width: var(--large-border-width);
|
|
||||||
--p-toast-icon-size: 1.5rem;
|
|
||||||
--p-toast-content-padding: 0.25rem;
|
|
||||||
--p-toast-content-gap: 0.5rem;
|
|
||||||
--p-toast-text-gap: 0.5rem;
|
|
||||||
--p-toast-summary-font-weight: var( --text-body-MD-standard-text-Medium-weight);
|
|
||||||
--p-toast-summary-font-size: var(--text-body-MD-standard-text-Medium-size);
|
|
||||||
--p-toast-detail-font-weight: var( --text-body-MD-standard-text-Regular-weight);
|
|
||||||
--p-toast-detail-font-size: var(--text-body-MD-standard-text-Regular-size);
|
|
||||||
/* info */
|
|
||||||
--p-toast-info-background: var(--background-contrast-info);
|
|
||||||
--p-toast-info-border-color: var(--border-plain-info);
|
|
||||||
--p-toast-info-color: var(--text-default-info);
|
|
||||||
--p-toast-info-detail-color: var(--input-color);
|
|
||||||
--p-toast-info-shadow: var(--shadow);
|
|
||||||
/* success */
|
|
||||||
--p-toast-success-background: var(--background-contrast-success);
|
|
||||||
--p-toast-success-border-color: var(--border-plain-success);
|
|
||||||
--p-toast-success-color: var(--text-default-success);
|
|
||||||
--p-toast-success-detail-color: var(--input-color);
|
|
||||||
--p-toast-success-shadow: var(--shadow);
|
|
||||||
/* warn */
|
|
||||||
--p-toast-warn-background: var(--background-contrast-warning);
|
|
||||||
--p-toast-warn-border-color: var(--border-plain-warning);
|
|
||||||
--p-toast-warn-color: var(--text-default-warning);
|
|
||||||
--p-toast-warn-detail-color: var(--input-color);
|
|
||||||
--p-toast-warn-shadow: var(--shadow);
|
|
||||||
/* error */
|
|
||||||
--p-toast-error-background: var(--background-contrast-error);
|
|
||||||
--p-toast-error-border-color: var(--border-plain-error);
|
|
||||||
--p-toast-error-color: var(--text-default-error);
|
|
||||||
--p-toast-error-detail-color: var(--input-color);
|
|
||||||
--p-toast-error-shadow: var(--shadow);
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
:root{
|
|
||||||
--p-icon-size: 1rem;
|
|
||||||
--p-text-muted-color: var(--text-action-high-grey);
|
|
||||||
--p-transition-duration: var(--transition-duration);
|
|
||||||
--p-anchor-gutter: 2px;
|
|
||||||
--p-content-background: var(--input-background);
|
|
||||||
--p-content-hover-background: var(--background-transparent-active);
|
|
||||||
--p-content-border-color: var(--border-action-high-grey);
|
|
||||||
--p-content-color: var(--text-action-high-grey);
|
|
||||||
--p-content-hover-color: var(--text-action-high-grey);
|
|
||||||
--p-content-border-radius: 0px;
|
|
||||||
--p-primary-color: var(--background-action-high-blue-france);
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the configuration and state of an Accordion component.
|
|
||||||
*/
|
|
||||||
export default interface IVAccordion {
|
|
||||||
/**
|
|
||||||
* Optional unique identifier for the accordion instance.
|
|
||||||
*/
|
|
||||||
id?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the accordion is disabled.
|
|
||||||
* When true, user interaction is prevented.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current value(s) of the accordion.
|
|
||||||
* Can be a single value or an array of values, depending on the selection mode.
|
|
||||||
* Accepts string, number, or null.
|
|
||||||
*/
|
|
||||||
value?: null | string | number | string[] | number[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value associated with a specific panel within the accordion.
|
|
||||||
* Useful for identifying or controlling individual panels.
|
|
||||||
*/
|
|
||||||
panelValue?: undefined | string | number;
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Accordion from 'primevue/accordion';
|
|
||||||
import VAccordionChild from './VAccordionChild.vue';
|
|
||||||
import type IVAccordion from './IVAccordion.type.js';
|
|
||||||
import { ref, watch } from 'vue';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVAccordion>(), {
|
|
||||||
value: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Define the event emitter for v-model binding
|
|
||||||
const emit = defineEmits([
|
|
||||||
'update:value',
|
|
||||||
])
|
|
||||||
|
|
||||||
// Local reactive value to sync with the parent v-model
|
|
||||||
const localValue = ref(props.value);
|
|
||||||
|
|
||||||
// Watch for external changes to the prop and update localValue accordingly
|
|
||||||
watch(() => props.value, (newVal) => {
|
|
||||||
if(localValue.value !== newVal) {
|
|
||||||
localValue.value = newVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch for internal changes to localValue and emit them to the parent
|
|
||||||
watch(localValue, (newVal) => {
|
|
||||||
if(props.value !== newVal){
|
|
||||||
emit('update:value', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Accordion
|
|
||||||
v-model:value="localValue"
|
|
||||||
class="p-accordion"
|
|
||||||
style="width: 100%;"
|
|
||||||
@update:value="emit('update:value', $event)"
|
|
||||||
>
|
|
||||||
<slot>
|
|
||||||
<VAccordionChild/>
|
|
||||||
</slot>
|
|
||||||
</Accordion>
|
|
||||||
</template>
|
|
|
@ -1,62 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import AccordionPanel from 'primevue/accordionpanel';
|
|
||||||
import AccordionHeader from 'primevue/accordionheader';
|
|
||||||
import AccordionContent from 'primevue/accordioncontent';
|
|
||||||
import type IVAccordion from './IVAccordion.type.js';
|
|
||||||
import { useId } from 'vue';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVAccordion>(), {
|
|
||||||
id: () => useId(),
|
|
||||||
disabled: false,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<AccordionPanel
|
|
||||||
:id="props.id"
|
|
||||||
:value="panelValue"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:aria-disabled="props.disabled"
|
|
||||||
:class="['p-accordionpanel', {'disabled': props.disabled}]">
|
|
||||||
<AccordionHeader class="p-accordionheader" role="heading" :aria-level="panelValue">
|
|
||||||
<slot name="header"></slot>
|
|
||||||
</AccordionHeader>
|
|
||||||
<AccordionContent class="p-accordioncontent">
|
|
||||||
<slot name="content"></slot>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionPanel>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.p-accordionpanel.disabled {
|
|
||||||
/* header */
|
|
||||||
--p-accordion-header-color: var(--text-disabled-grey);
|
|
||||||
--p-accordion-header-hover-color: var(--text-disabled-grey);
|
|
||||||
--p-accordion-header-active-color: var(--text-disabled-grey);
|
|
||||||
--p-accordion-header-active-hover-color: var(--text-disabled-grey);
|
|
||||||
--p-accordion-header-border-color: var(--border-disabled-grey);
|
|
||||||
--p-accordion-content-border-color: var(--border-disabled-grey);
|
|
||||||
/* focus header */
|
|
||||||
--p-accordion-header-focus-ring-style: none;
|
|
||||||
--p-accordion-header-focus-ring-color: transparent;
|
|
||||||
--p-accordion-header-focus-ring-width: 0px;
|
|
||||||
/* background header */
|
|
||||||
--p-accordion-header-border-color: var(--border-default-grey);
|
|
||||||
--p-accordion-header-background: var(--background-disabled-grey);
|
|
||||||
--p-accordion-header-hover-background: var(--background-disabled-grey);
|
|
||||||
--p-accordion-header-active-background: var(--background-disabled-grey);
|
|
||||||
--p-accordion-header-active-hover-background: var(--background-disabled-grey);
|
|
||||||
/* icon */
|
|
||||||
--p-accordion-header-toggle-icon-color: var(--text-disabled-grey);
|
|
||||||
--p-accordion-header-toggle-icon-hover-color: var(--text-disabled-grey);
|
|
||||||
--p-accordion-header-toggle-icon-active-color: var(--text-disabled-grey);
|
|
||||||
--p-accordion-header-toggle-icon-active-hover-color: var(--text-disabled-grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-accordionheader{
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-accordioncontent{margin: 1rem;}
|
|
||||||
</style>
|
|
|
@ -1,62 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the properties of an Alert component.
|
|
||||||
*/
|
|
||||||
export default interface IVAlert {
|
|
||||||
/**
|
|
||||||
* Determines if the alert should be visible.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
alert?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the alert has been closed.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
closed?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies if the alert can be closed by the user.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
closeable?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unique identifier for the alert instance.
|
|
||||||
*/
|
|
||||||
id?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Title displayed at the top of the alert.
|
|
||||||
*/
|
|
||||||
title?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detailed description or message content of the alert.
|
|
||||||
*/
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if a smaller variant of the alert should be displayed.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
small?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of alert, affecting its visual style and icon.
|
|
||||||
* - `"success"`: Indicates a successful or positive action.
|
|
||||||
* - `"error"`: Indicates an error or critical issue.
|
|
||||||
* - `"info"`: Provides general information.
|
|
||||||
* - `"warn"`: Indicates a warning or potential issue.
|
|
||||||
*/
|
|
||||||
type?: "success" | "error" | "info" | "warn" | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Label for the close button, useful for accessibility.
|
|
||||||
*/
|
|
||||||
closeButtonLabel?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time in milliseconds after which the alert automatically disappears.
|
|
||||||
*/
|
|
||||||
lifeTime?: number;
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Toast from 'primevue/toast';
|
|
||||||
import VButton from '../button/VButton.vue';
|
|
||||||
import type { ToastProps } from 'primevue/toast';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ToastProps>(), {
|
|
||||||
group: undefined,
|
|
||||||
position: 'bottom-center',
|
|
||||||
breakpoints: undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'close',
|
|
||||||
'life-end'
|
|
||||||
]);
|
|
||||||
|
|
||||||
const getIconColor = (type?: string) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'error': return 'var(--text-default-error)';
|
|
||||||
case 'warn' : return 'var(--text-default-warning)';
|
|
||||||
case 'success' : return 'var(--text-default-success)';
|
|
||||||
case 'info': return 'var(--text-default-info)';
|
|
||||||
default:
|
|
||||||
return 'var(--text-default-success)';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getIconClass = (type?: string) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'error': return 'ri-spam-fill';
|
|
||||||
case 'warn' : return 'ri-alert-fill';
|
|
||||||
case 'success' : return 'ri-checkbox-circle-fill';
|
|
||||||
case 'info': return 'ri-information-fill';
|
|
||||||
default:
|
|
||||||
return 'ri-information-fill';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Toast
|
|
||||||
:position=props.position
|
|
||||||
:group="props.group"
|
|
||||||
:breakpoints="props.breakpoints"
|
|
||||||
class="p-toast"
|
|
||||||
role="alert"
|
|
||||||
aria-live="assertive"
|
|
||||||
aria-atomic="true"
|
|
||||||
@close="emit('close', $event)"
|
|
||||||
@life-end="emit('life-end', $event)"
|
|
||||||
>
|
|
||||||
<template #container="{message, closeCallback}">
|
|
||||||
<div class="header">
|
|
||||||
<i
|
|
||||||
style="font-size: var(--p-message-icon-lg-size);"
|
|
||||||
:class="getIconClass(message.severity)"
|
|
||||||
:style="{color: getIconColor(message.severity)}"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
:class="[styles['text-body-MD-standard-text-Medium']]"
|
|
||||||
:style="{color: getIconColor(message.severity)}"
|
|
||||||
style="width: 100%;"
|
|
||||||
>
|
|
||||||
{{ message.summary }}
|
|
||||||
</span>
|
|
||||||
<VButton
|
|
||||||
title="Fermer le message"
|
|
||||||
tertiary
|
|
||||||
no-outline
|
|
||||||
size="sm"
|
|
||||||
icon-only
|
|
||||||
aria-label="Fermer"
|
|
||||||
icon="ri-close-line"
|
|
||||||
type="button"
|
|
||||||
@click="closeCallback"
|
|
||||||
style="height: 2rem;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="!!message.detail"
|
|
||||||
:class="['content', styles['text-body-MD-standard-text-Regular']]"
|
|
||||||
>
|
|
||||||
<span>{{ message.detail }}</span>
|
|
||||||
<slot name="footer"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Toast>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.header{
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 0.25rem;
|
|
||||||
padding: var(--p-toast-text-gap);
|
|
||||||
color: var(--text-title-grey);
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content{
|
|
||||||
padding: 0rem 0.825rem 0.5rem 0.825rem;
|
|
||||||
color: var(--text-default-grey);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,38 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the properties of a Badge component.
|
|
||||||
*/
|
|
||||||
export default interface IVBadge {
|
|
||||||
/**
|
|
||||||
* The text label displayed inside the badge.
|
|
||||||
*/
|
|
||||||
label: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of badge, which determines its color and icon.
|
|
||||||
* - `success`: Indicates a positive or successful status.
|
|
||||||
* - `error`: Indicates an error or negative status.
|
|
||||||
* - `new`: Highlights something new or recently added.
|
|
||||||
* - `info`: Provides informational context.
|
|
||||||
* - `warning`: Warns the user about a potential issue.
|
|
||||||
* - **undefined**: Defaults to a neutral badge style.
|
|
||||||
*/
|
|
||||||
type?: 'success' | 'error' | 'new' | 'info' | 'warning' | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the badge will be displayed without an icon.
|
|
||||||
* Defaults to false.
|
|
||||||
*/
|
|
||||||
noIcon?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the badge will be rendered in a smaller size.
|
|
||||||
* Useful for compact UI elements.
|
|
||||||
*/
|
|
||||||
small?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional maximum width for the badge.
|
|
||||||
* Accepts any valid CSS width value (e.g., '100px', '10rem', '50%').
|
|
||||||
*/
|
|
||||||
maxWidth?: string;
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import type IVBadge from './IVBadge.type.js';
|
|
||||||
import Tag from 'primevue/tag';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVBadge>(), {
|
|
||||||
type: undefined,
|
|
||||||
noIcon: false,
|
|
||||||
small: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const severity = computed(() => {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'error': return 'danger';
|
|
||||||
case 'warning' : return 'warn';
|
|
||||||
case 'success' : return 'success';
|
|
||||||
case 'info': return 'info';
|
|
||||||
case 'new': return 'secondary';
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const icon = computed(() => {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'error': return 'ri-spam-fill';
|
|
||||||
case 'warning' : return 'ri-alert-fill';
|
|
||||||
case 'success' : return 'ri-checkbox-circle-fill';
|
|
||||||
case 'info': return 'ri-information-fill';
|
|
||||||
case 'new': return 'ri-flashlight-fill';
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const limit = computed(() => props.maxWidth);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Tag
|
|
||||||
role="alert"
|
|
||||||
:value="props.label"
|
|
||||||
:severity="severity"
|
|
||||||
:title="props.label"
|
|
||||||
class="p-tag"
|
|
||||||
:class="{'small': props.small}"
|
|
||||||
>
|
|
||||||
<i v-if="!props.noIcon || props.type === undefined" :class="icon" style="font-weight: 100;"></i>
|
|
||||||
<span :class="{'limit': props.maxWidth}">{{ props.label }}</span>
|
|
||||||
</Tag>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.p-tag{height: fit-content;}
|
|
||||||
.p-tag.small{
|
|
||||||
--p-tag-icon-size: var( --text-body-SM-detail-text-Maj-size);
|
|
||||||
--p-tag-font-size: var( --text-body-XS-mention-text-Maj-size);
|
|
||||||
--p-tag-font-weight: var( --text-body-XS-mention-text-Maj-weight);
|
|
||||||
--p-tag-padding: 0.125rem 0.325rem;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.limit{
|
|
||||||
max-width: v-bind(limit);
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
import type { RouteLocationRaw } from "vue-router";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface representing a navigational link component.
|
|
||||||
* This can be used for both internal routing (via `to`) and external links (via `href`).
|
|
||||||
*/
|
|
||||||
export default interface IVLink {
|
|
||||||
/**
|
|
||||||
* The text label displayed for the link.
|
|
||||||
*/
|
|
||||||
label: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal route destination using Vue Router's RouteLocationRaw.
|
|
||||||
* Optional – used for client-side navigation.
|
|
||||||
*/
|
|
||||||
to?: RouteLocationRaw;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* External URL for the link.
|
|
||||||
* Optional – used for standard anchor navigation.
|
|
||||||
*/
|
|
||||||
href?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional icon name or path to be displayed alongside the label.
|
|
||||||
*/
|
|
||||||
icon?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies where to open the linked document (e.g., "_blank", "_self").
|
|
||||||
* Optional – applies to external links.
|
|
||||||
*/
|
|
||||||
target?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the link is disabled and not clickable.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the icon is displayed on the right side of the label.
|
|
||||||
* Defaults to false (icon on the left).
|
|
||||||
*/
|
|
||||||
iconRight?: boolean;
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Button from 'primevue/button';
|
import Button from 'primevue/button';
|
||||||
import type IVButton from './IVButton.type.js';
|
import type IVButton from './IVButton.type';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import styles from '@visua/typography.module.css';
|
import styles from '@visua/typography.module.css';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VButton from './VButton.vue';
|
import VButton from './VButton.vue';
|
||||||
import type IVButtonGroup from './IVButton.type.js';
|
import type IVButtonGroup from './IVButton.type.ts';
|
||||||
import { computed, ref, onMounted } from 'vue';
|
import { computed, ref, onMounted } from 'vue';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVButtonGroup>(), {
|
const props = withDefaults(defineProps<IVButtonGroup>(), {
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Button from 'primevue/button';
|
|
||||||
import type IVLink from '@/components/button/IVLink.type.js';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
const props = defineProps<IVLink>();
|
|
||||||
|
|
||||||
// Icon position computed
|
|
||||||
const iconPos = computed<string>(() => {
|
|
||||||
return props.iconRight ? 'right': 'left'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Change the html tag of root element
|
|
||||||
const htmlTag = computed(() => { return props.to ? 'RouterLink' : props.href ? 'a' : 'button'})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
role="link"
|
|
||||||
:label="props.label"
|
|
||||||
:aria-label="props.label"
|
|
||||||
:icon="props.icon"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:aria-disabled="props.disabled"
|
|
||||||
:icon-pos="iconPos"
|
|
||||||
:as="htmlTag"
|
|
||||||
:href="props.href"
|
|
||||||
:target="props.target"
|
|
||||||
:to="props.to"
|
|
||||||
:tabindex="props.disabled ? -1 : 0"
|
|
||||||
@click="props.disabled && $event.preventDefault()"
|
|
||||||
class="p-button"
|
|
||||||
:class="[styles['text-body-MD-standard-text-Regular'], {'disabled': props.disabled}]"
|
|
||||||
:style="props.disabled ? { pointerEvents: 'none' } : {}"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
*{
|
|
||||||
text-decoration: none;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-button{
|
|
||||||
padding: 0px;
|
|
||||||
gap: calc(var(--p-button-gap)/2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-button.disabled{
|
|
||||||
--p-button-link-color: var(--text-disabled-grey);
|
|
||||||
--p-button-link-hover-color: var(--text-disabled-grey);
|
|
||||||
--p-button-link-active-color: var(--text-disabled-grey);
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,89 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the props for a CheckBox Vue component.
|
|
||||||
*/
|
|
||||||
export default interface IVCheckBox {
|
|
||||||
/**
|
|
||||||
* The unique identifier for the checkbox element.
|
|
||||||
*/
|
|
||||||
id?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name attribute for the checkbox input.
|
|
||||||
*/
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the checkbox is required in a form.
|
|
||||||
*/
|
|
||||||
required?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value associated with the checkbox.
|
|
||||||
*/
|
|
||||||
value?: unknown;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the checkbox is initially checked.
|
|
||||||
*/
|
|
||||||
checked?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bound value of the checkbox, can be a boolean or an array of values.
|
|
||||||
*/
|
|
||||||
modelValue: Array<unknown> | boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, renders the checkbox in a smaller size.
|
|
||||||
*/
|
|
||||||
small?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, displays the checkbox inline with other elements.
|
|
||||||
*/
|
|
||||||
inline?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the checkbox is read-only and cannot be changed by the user.
|
|
||||||
*/
|
|
||||||
readonly?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opacity level applied when the checkbox is read-only.
|
|
||||||
*/
|
|
||||||
readonlyOpacity?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The label text displayed next to the checkbox.
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The error message shown when validation fails.
|
|
||||||
*/
|
|
||||||
errorMessage?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The message shown when the checkbox input is valid.
|
|
||||||
*/
|
|
||||||
validMessage?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Additional hint or helper text displayed below the checkbox.
|
|
||||||
*/
|
|
||||||
hint?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, disables the checkbox input.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visual style of the checkbox, can be 'normal', 'error', or 'success'.
|
|
||||||
*/
|
|
||||||
type?: 'normal' | 'error' | 'success';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the checkbox operates in binary mode (boolean value).
|
|
||||||
*/
|
|
||||||
binary?: boolean;
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Checkbox from 'primevue/checkbox';
|
|
||||||
import VLabel from '../label/VLabel.vue';
|
|
||||||
import VHint from '../hint/VHint.vue';
|
|
||||||
import type IVCheckBox from './IVCheckbox.type.js';
|
|
||||||
import { useId, computed, watch, ref } from 'vue';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVCheckBox>(), {
|
|
||||||
id: () => useId(),
|
|
||||||
hint: '',
|
|
||||||
errorMessage: '',
|
|
||||||
validMessage: '',
|
|
||||||
label: '',
|
|
||||||
small: false,
|
|
||||||
modelValue: false,
|
|
||||||
readonlyOpacity: 0.75,
|
|
||||||
});
|
|
||||||
|
|
||||||
const size = computed(() => props.small ? 'small' : undefined)
|
|
||||||
const message = computed(() => props.errorMessage || props.validMessage)
|
|
||||||
|
|
||||||
const typeMessage = computed(() => {
|
|
||||||
if (props.errorMessage) return 'alert';
|
|
||||||
else if (props.validMessage) return 'success';
|
|
||||||
else return undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
const binary = computed(() => {
|
|
||||||
if (typeof props.binary === 'boolean') return props.binary;
|
|
||||||
return typeof props.modelValue === 'boolean';
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const labelState = computed(() => {
|
|
||||||
if((!!props.errorMessage || props.type === 'error') && !props.disabled) return 'error';
|
|
||||||
else if((!!props.validMessage || props.type === 'success') && !props.disabled) return 'success';
|
|
||||||
else return 'default';
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'update:modelValue',
|
|
||||||
'value-change',
|
|
||||||
'change',
|
|
||||||
'focus',
|
|
||||||
'blur'
|
|
||||||
])
|
|
||||||
|
|
||||||
const localModelValue = ref(props.modelValue);
|
|
||||||
watch(() => props.modelValue, (newVal) => {
|
|
||||||
if(localModelValue.value !== newVal){
|
|
||||||
localModelValue.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localModelValue, (newVal) => {
|
|
||||||
if(props.modelValue !== newVal){
|
|
||||||
emit('update:modelValue', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<div class="header">
|
|
||||||
<Checkbox
|
|
||||||
:id="props.id"
|
|
||||||
:name="name"
|
|
||||||
:value="value"
|
|
||||||
:size="size"
|
|
||||||
:required="required"
|
|
||||||
:readonly="readonly"
|
|
||||||
:data-testid="`input-checkbox-${id}`"
|
|
||||||
:data-test="`input-checkbox-${id}`"
|
|
||||||
:tabindex="readonly ? -1 : undefined"
|
|
||||||
v-bind="$attrs"
|
|
||||||
:disabled="disabled"
|
|
||||||
:label="props.label"
|
|
||||||
:binary="binary"
|
|
||||||
:aria-label="props.label"
|
|
||||||
:class="['p-checkbox', {
|
|
||||||
'checked-disabled': props.modelValue && disabled,
|
|
||||||
'unchecked-disabled': !props.modelValue && disabled,
|
|
||||||
'error': (!!props.errorMessage || type === 'error') && !disabled,
|
|
||||||
'success': (!!props.validMessage || type === 'success') && !disabled,
|
|
||||||
}]"
|
|
||||||
v-model:model-value="localModelValue"
|
|
||||||
@update:model-value="emit('update:modelValue', $event)"
|
|
||||||
@change="emit('change', $event)"
|
|
||||||
@blur="emit('blur', $event)"
|
|
||||||
@focus="emit('focus', $event)"
|
|
||||||
@value-change="emit('value-change', $event)"
|
|
||||||
/>
|
|
||||||
<VLabel
|
|
||||||
:for="props.id"
|
|
||||||
:label="props.label"
|
|
||||||
:required="props.required"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:hint="props.hint"
|
|
||||||
:type="labelState"
|
|
||||||
class="label-container"
|
|
||||||
>
|
|
||||||
<template #required-tip v-if="props.required">
|
|
||||||
<slot name="required-tip"/>
|
|
||||||
</template>
|
|
||||||
</VLabel>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="message && !disabled"
|
|
||||||
aria-live="assertive"
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
<VHint :title="message" :type="typeMessage" icon/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.container.readonly {
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: v-bind('readonlyOpacity');
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
gap: 0.5rem;
|
|
||||||
align-self: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: start;
|
|
||||||
gap: 0.5rem;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-checkbox{margin-top: 0.5rem;}
|
|
||||||
|
|
||||||
.required {color: var(--minor-red-marianne);}
|
|
||||||
|
|
||||||
.p-checkbox.checked-disabled {
|
|
||||||
--p-checkbox-disabled-background: var(--background-disabled-grey);
|
|
||||||
--p-checkbox-checked-disabled-border-color: var(--border-disabled-grey);
|
|
||||||
--p-checkbox-icon-disabled-color: var(--text-disabled-grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-checkbox.unchecked-disabled {
|
|
||||||
--p-checkbox-disabled-background: var(--background-default-grey);
|
|
||||||
--p-checkbox-checked-disabled-border-color: var(--border-disabled-grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-checkbox.success {
|
|
||||||
--p-checkbox-border-color: var(--border-plain-success);
|
|
||||||
--p-checkbox-hover-border-color: var(--border-plain-success);
|
|
||||||
--p-checkbox-focus-border-color: var(--border-plain-success);
|
|
||||||
--p-checkbox-checked-border-color: var(--border-plain-success);
|
|
||||||
--p-checkbox-checked-hover-border-color: var(--border-plain-success);
|
|
||||||
outline: var(--border-width) solid var(--p-checkbox-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-checkbox.error {
|
|
||||||
--p-checkbox-border-color: var(--border-plain-error);
|
|
||||||
--p-checkbox-hover-border-color: var(--border-plain-error);
|
|
||||||
--p-checkbox-focus-border-color: var(--border-plain-error);
|
|
||||||
--p-checkbox-checked-border-color: var(--border-plain-error);
|
|
||||||
--p-checkbox-checked-hover-border-color: var(--border-plain-error);
|
|
||||||
outline: var(--border-width) solid var(--p-checkbox-border-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,2 +0,0 @@
|
||||||
export * from './useAlert.js';
|
|
||||||
export * from './useConfirmModal.js';
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { useToast } from "primevue/usetoast";
|
|
||||||
import type IVAlert from "../alert/IVAlert.type.js";
|
|
||||||
|
|
||||||
export function useAlert() {
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const showAlert = ({
|
|
||||||
title = '',
|
|
||||||
description = '',
|
|
||||||
type = 'info',
|
|
||||||
closeable = true,
|
|
||||||
lifeTime,
|
|
||||||
}: IVAlert) => {
|
|
||||||
toast.add({
|
|
||||||
severity: type,
|
|
||||||
summary: title,
|
|
||||||
detail: description,
|
|
||||||
life: lifeTime,
|
|
||||||
closable: closeable,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return { showAlert}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
import type { ConfirmationOptions } from "primevue/confirmationoptions";
|
|
||||||
import { useConfirm } from "primevue";
|
|
||||||
import VButton from "../button/VButton.vue";
|
|
||||||
|
|
||||||
export function useConfirmModal() {
|
|
||||||
const confirm = useConfirm();
|
|
||||||
|
|
||||||
const showConfirmModal = ({
|
|
||||||
acceptProps = VButton,
|
|
||||||
rejectProps = VButton,
|
|
||||||
group = '',
|
|
||||||
header = '',
|
|
||||||
message = '',
|
|
||||||
icon = '',
|
|
||||||
accept = Function,
|
|
||||||
reject = Function,
|
|
||||||
onHide = Function,
|
|
||||||
onShow = Function,
|
|
||||||
modal = true,
|
|
||||||
blockScroll = true,
|
|
||||||
position = 'center',
|
|
||||||
appendTo = 'body',
|
|
||||||
}: ConfirmationOptions) => {
|
|
||||||
confirm.require({
|
|
||||||
group,
|
|
||||||
header,
|
|
||||||
message,
|
|
||||||
icon,
|
|
||||||
rejectProps,
|
|
||||||
acceptProps,
|
|
||||||
accept,
|
|
||||||
reject,
|
|
||||||
onHide,
|
|
||||||
onShow,
|
|
||||||
modal,
|
|
||||||
blockScroll,
|
|
||||||
position,
|
|
||||||
appendTo,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return {showConfirmModal}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Divider from 'primevue/divider';
|
|
||||||
import type { DividerProps } from 'primevue/divider';
|
|
||||||
|
|
||||||
export type IVDivider = Partial<Omit<DividerProps, 'dt' | 'pt' | 'ptOptions' | 'unstyled'>>
|
|
||||||
const props = withDefaults(defineProps<IVDivider>(), {
|
|
||||||
align: undefined,
|
|
||||||
layout: 'horizontal',
|
|
||||||
type: 'solid',
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Divider :align="props.align" :layout="props.layout" :type="props.type"/>
|
|
||||||
</template>
|
|
|
@ -1,198 +0,0 @@
|
||||||
import type { HintedString } from '@primevue/core';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface representing the properties of a FileUpload component.
|
|
||||||
*/
|
|
||||||
export interface IFile {
|
|
||||||
/**
|
|
||||||
* Optional unique identifier for the file input.
|
|
||||||
*/
|
|
||||||
id?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional label displayed above or beside the file input.
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the types of files that the input accepts.
|
|
||||||
* Can be a single MIME type string or an array of MIME types.
|
|
||||||
* Example: "image/*" or ["image/png", "application/pdf"]
|
|
||||||
*/
|
|
||||||
accept?: string | string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional helper text displayed below the input to guide the user.
|
|
||||||
*/
|
|
||||||
hint?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional error message displayed when validation fails.
|
|
||||||
*/
|
|
||||||
error?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional message displayed when the input is valid.
|
|
||||||
*/
|
|
||||||
validMessage?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, disables the file input.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current value of the file input, typically a file path or name.
|
|
||||||
*/
|
|
||||||
modelValue?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FileUploadProps {
|
|
||||||
/**
|
|
||||||
* Name of the request parameter to identify the files at backend.
|
|
||||||
*/
|
|
||||||
name?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Remote url to upload the files.
|
|
||||||
*/
|
|
||||||
url?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Defines the UI of the component, possible values are 'advanced' and 'basic'.
|
|
||||||
* @defaultValue advanced
|
|
||||||
*/
|
|
||||||
mode?: HintedString<'advanced' | 'basic'> | undefined;
|
|
||||||
/**
|
|
||||||
* Used to select multiple files at once from file dialog.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
multiple?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Pattern to restrict the allowed file types such as 'image/*'.
|
|
||||||
*/
|
|
||||||
accept?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Disables the upload functionality.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
disabled?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When enabled, upload begins automatically after selection is completed.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
auto?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Maximum file size allowed in bytes.
|
|
||||||
*/
|
|
||||||
maxFileSize?: number | undefined;
|
|
||||||
/**
|
|
||||||
* Message of the invalid fize size.
|
|
||||||
* @defaultValue {0}: Invalid file size, file size should be smaller than {1.}
|
|
||||||
*/
|
|
||||||
invalidFileSizeMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Message to display when number of files to be uploaded exceeeds the limit.
|
|
||||||
* @defaultValue Maximum number of files to be uploaded is {0.}
|
|
||||||
*/
|
|
||||||
invalidFileLimitMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Message of the invalid fize type.
|
|
||||||
* @defaultValue '{0}: Invalid file type.'
|
|
||||||
*/
|
|
||||||
invalidFileTypeMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Maximum number of files that can be uploaded.
|
|
||||||
*/
|
|
||||||
fileLimit?: number | undefined;
|
|
||||||
/**
|
|
||||||
* Cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
withCredentials?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Width of the image thumbnail in pixels.
|
|
||||||
* @defaultValue 50
|
|
||||||
*/
|
|
||||||
previewWidth?: number | undefined;
|
|
||||||
/**
|
|
||||||
* Label of the choose button. Defaults to PrimeVue Locale configuration.
|
|
||||||
*/
|
|
||||||
chooseLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Label of the upload button. Defaults to PrimeVue Locale configuration.
|
|
||||||
*/
|
|
||||||
uploadLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Label of the cancel button. Defaults to PrimeVue Locale configuration.
|
|
||||||
* @defaultValue Cancel
|
|
||||||
*/
|
|
||||||
cancelLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to use the default upload or a manual implementation defined in uploadHandler callback. Defaults to PrimeVue Locale configuration.
|
|
||||||
*/
|
|
||||||
customUpload?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to show the upload button.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
showUploadButton?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to show the cancel button.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
showCancelButton?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Icon of the choose button.
|
|
||||||
*/
|
|
||||||
chooseIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon of the upload button.
|
|
||||||
*/
|
|
||||||
uploadIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon of the cancel button.
|
|
||||||
*/
|
|
||||||
cancelIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the component.
|
|
||||||
*/
|
|
||||||
style?: unknown;
|
|
||||||
/**
|
|
||||||
* Style class of the component.
|
|
||||||
*/
|
|
||||||
class?: unknown;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the ButtonProps to the choose button inside the component.
|
|
||||||
* @type {ButtonProps}
|
|
||||||
* @defaultValue null
|
|
||||||
*/
|
|
||||||
chooseButtonProps?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the ButtonProps to the upload button inside the component.
|
|
||||||
* @type {ButtonProps}
|
|
||||||
* @defaultValue { severity: 'secondary' }
|
|
||||||
*/
|
|
||||||
uploadButtonProps?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the ButtonProps to the cancel button inside the component.
|
|
||||||
* @type {ButtonProps}
|
|
||||||
* @defaultValue { severity: 'secondary' }
|
|
||||||
*/
|
|
||||||
cancelButtonProps?: object | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extended interface for a customizable FileUpload component.
|
|
||||||
*
|
|
||||||
* Combines selected properties from IFile and FileUploadProps,
|
|
||||||
* while omitting and overriding specific ones for more control.
|
|
||||||
*/
|
|
||||||
export default interface IVFileUpload extends
|
|
||||||
Partial<Omit<IFile, 'accept' | 'error'>>,
|
|
||||||
Partial<Omit<FileUploadProps, 'auto' | 'mode' | 'multiple'>> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, enables the advanced mode of the file upload component,
|
|
||||||
* which may include features like drag-and-drop, file previews, etc.
|
|
||||||
*/
|
|
||||||
advanced?: boolean;
|
|
||||||
}
|
|
|
@ -1,229 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import VBadge from '../badge/VBadge.vue';
|
|
||||||
import VButton from '../button/VButton.vue';
|
|
||||||
import VProgressBar from '../progressbar/VProgressBar.vue';
|
|
||||||
import { usePrimeVue } from 'primevue';
|
|
||||||
import { ref, computed } from 'vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
export interface IVFile {
|
|
||||||
file: File
|
|
||||||
advanced?: boolean
|
|
||||||
index?: number
|
|
||||||
removeFileCallback?: (index: number) => void
|
|
||||||
status?: 'pending' | 'completed' | 'error'
|
|
||||||
progress?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVFile>(), {
|
|
||||||
advanced: false,
|
|
||||||
index: 0,
|
|
||||||
removeFileCallback: () => {},
|
|
||||||
status: 'pending',
|
|
||||||
progress: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
const badgeType = computed(() => {
|
|
||||||
switch (props.status) {
|
|
||||||
case 'pending': return 'warning'
|
|
||||||
case 'completed': return 'success'
|
|
||||||
case 'error': return 'error'
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const badgeLabel = computed(() => {
|
|
||||||
switch (props.status) {
|
|
||||||
case 'pending': return 'En attente'
|
|
||||||
case 'completed': return 'Terminé'
|
|
||||||
case 'error': return 'Annulé'
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const $primevue = usePrimeVue();
|
|
||||||
const totalSize = ref(0);
|
|
||||||
const totalSizePercent = ref(0);
|
|
||||||
|
|
||||||
const formatSize = (bytes: number) => {
|
|
||||||
const k = 1024;
|
|
||||||
const dm = 3;
|
|
||||||
const sizes = $primevue.config.locale?.fileSizeTypes ?? ['B', 'KB', 'MB', 'GB', 'TB'];;
|
|
||||||
|
|
||||||
if (bytes === 0) {
|
|
||||||
return `0 ${ sizes[0]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
||||||
const formattedSize = parseFloat((bytes / k ** i).toFixed(dm));
|
|
||||||
|
|
||||||
return `${formattedSize} ${sizes[i]}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRemoveTemplatingFile = (file: File, removeFileCallback: (index: number) => void, index: number) => {
|
|
||||||
removeFileCallback(index);
|
|
||||||
totalSize.value -= parseInt(formatSize(file.size));
|
|
||||||
totalSizePercent.value = totalSize.value / 10;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFileIconClass = (file: File) => {
|
|
||||||
const type = file?.name.toLowerCase();
|
|
||||||
if (type?.endsWith('.pdf')) return 'ri-file-pdf-2-line';
|
|
||||||
if (type?.endsWith('.doc') || type?.endsWith('.docx')) return 'ri-file-word-2-line';
|
|
||||||
if (type?.endsWith('.xls') || type?.endsWith('.xlsx')) return 'ri-file-excel-2-line';
|
|
||||||
if (type?.endsWith('.txt')) return 'ri-file-text-line';
|
|
||||||
if (type?.endsWith('.zip') || type?.endsWith('.rar')) return 'ri-file-zip-line';
|
|
||||||
return 'ri-file-line'; // icône générique
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFileIconColor = (file: File) => {
|
|
||||||
const type = file?.name.toLowerCase();
|
|
||||||
if (type?.endsWith('.pdf')) return 'var(--border-plain-error)'; // rouge
|
|
||||||
if (type?.endsWith('.doc') || type?.endsWith('.docx')) return 'var(--border-plain-blue-france)'; // bleu
|
|
||||||
if (type?.endsWith('.xls') || type?.endsWith('.xlsx')) return 'var(--border-plain-success)'; // vert
|
|
||||||
if (type?.endsWith('.txt')) return 'var(--border-plain-grey)'; // gris
|
|
||||||
if (type?.endsWith('.zip') || type?.endsWith('.rar')) return 'var(--illustration-color-850-tournesol-default)'; // jaune
|
|
||||||
return 'var(--border-plain-blue-france)'; // bleu marine
|
|
||||||
};
|
|
||||||
|
|
||||||
const borderColor = computed(() => {
|
|
||||||
switch (props.status) {
|
|
||||||
case 'pending': return '2px solid var(--system-color-950-warning-active)';
|
|
||||||
case 'completed': return '2px solid var(--system-color-950-success-active)';
|
|
||||||
case 'error': return '2px solid var(--system-color-950-success-active)';
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const getImagePreview = (file: File) => {
|
|
||||||
return URL.createObjectURL(file);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
v-if="props.advanced"
|
|
||||||
class="advanced-file"
|
|
||||||
:title="`Fichier : ${props.file.name}`"
|
|
||||||
:style="{border: borderColor}"
|
|
||||||
:class="[styles['text-body-XS-mention-text-Medium']]"
|
|
||||||
>
|
|
||||||
<VButton
|
|
||||||
tertiary
|
|
||||||
noOutline
|
|
||||||
size="sm"
|
|
||||||
icon="ri-close-line"
|
|
||||||
@click="onRemoveTemplatingFile(props.file, props.removeFileCallback, props.index)"
|
|
||||||
title='Spprimer le fichier'
|
|
||||||
ref="clearfile"
|
|
||||||
aria-controls="clear-file"
|
|
||||||
type="button"
|
|
||||||
class="button"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
v-if="file?.type.startsWith('image/')"
|
|
||||||
role="presentation"
|
|
||||||
:alt="props.file?.name"
|
|
||||||
:src="getImagePreview(props.file)"
|
|
||||||
/>
|
|
||||||
<i
|
|
||||||
v-else
|
|
||||||
:class="getFileIconClass(props.file)"
|
|
||||||
:style="{color: getFileIconColor(props.file), fontSize: '4rem', fontWeight: 500}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span :title="props.file?.name" class="file-name">
|
|
||||||
{{ props.file?.name }}
|
|
||||||
</span>
|
|
||||||
<span>{{ formatSize(props.file?.size ?? 0) }}</span>
|
|
||||||
<VBadge
|
|
||||||
v-if="advanced"
|
|
||||||
:label="badgeLabel"
|
|
||||||
:type="badgeType"
|
|
||||||
small
|
|
||||||
no-icon
|
|
||||||
/>
|
|
||||||
<VProgressBar
|
|
||||||
v-if="advanced && status === 'pending'"
|
|
||||||
:value="props.progress"
|
|
||||||
:show-value="false"
|
|
||||||
style="width: 6rem; margin-top: 0.5rem;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- Simple file -->
|
|
||||||
<div v-else class="simple-file" >
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
v-if="file?.type.startsWith('image/')"
|
|
||||||
role="presentation"
|
|
||||||
:alt="props.file.name"
|
|
||||||
:src="getImagePreview(props.file)"
|
|
||||||
/>
|
|
||||||
<i
|
|
||||||
v-else
|
|
||||||
:class="getFileIconClass(props.file)"
|
|
||||||
:style="{color: getFileIconColor(props.file), fontSize: '2.5rem', padding: '0px'}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="file-info" :class="[styles['text-body-SM-detail-text-Regular']]">
|
|
||||||
<span>{{ props.file?.name }}</span>
|
|
||||||
<span>{{ formatSize(props.file?.size ?? 0) }}</span>
|
|
||||||
</div>
|
|
||||||
<VBadge
|
|
||||||
:label="badgeLabel"
|
|
||||||
:type="badgeType"
|
|
||||||
small
|
|
||||||
no-icon
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
img{
|
|
||||||
width: 2.5rem;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button{
|
|
||||||
display: flex;
|
|
||||||
align-self: self-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.advanced-file{
|
|
||||||
border-radius: 3px;
|
|
||||||
width: fit-content;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.simple-file{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
gap: calc(var(--p-fileupload-content-gap) / 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-name{
|
|
||||||
width: 6rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
margin: 0rem 0.5rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-info{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: start;
|
|
||||||
gap: 0.025rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,377 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import FileUpload from 'primevue/fileupload';
|
|
||||||
import VHint from '../hint/VHint.vue';
|
|
||||||
import VLabel from '../label/VLabel.vue';
|
|
||||||
import VButton from '../button/VButton.vue';
|
|
||||||
import VButtonGroup from '../button/VButtonGroup.vue';
|
|
||||||
import VFile from './VFile.vue';
|
|
||||||
import VMessage from '../message/VMessage.vue';
|
|
||||||
import VScrollPanel from '../scrollpanel/VScrollPanel.vue';
|
|
||||||
import VLabelErrorProxy from './VLabelErrorProxy.vue';
|
|
||||||
import type IVFileUpload from './IVFileUpload.type.js';
|
|
||||||
import type { FileUploadErrorEvent, FileUploadProgressEvent, FileUploadRemoveEvent, FileUploadSelectEvent, FileUploadUploadEvent } from 'primevue/fileupload';
|
|
||||||
import { computed, useId, ref } from 'vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
const fileUploadRef = ref();
|
|
||||||
const fileProgressMap = ref<Record<string, number>>({});
|
|
||||||
const hasValidationError = ref(false);
|
|
||||||
const hasSystemError = ref(false);
|
|
||||||
const systemErrorMessage = ref('');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
inheritAttrs: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVFileUpload>(), {
|
|
||||||
id: () => useId(),
|
|
||||||
label: 'Ajouter un fichier',
|
|
||||||
validMessage: '',
|
|
||||||
hint: '',
|
|
||||||
disabled: false,
|
|
||||||
invalidFileSizeMessage: 'Taille de fichier {0} invalide, la taille du fichier doit être plus petite que {1}',
|
|
||||||
invalidFileLimitMessage: 'Nombre maximal des fichiers atteint, le nombre maximal est {0.}',
|
|
||||||
invalidFileTypeMessage: 'Type de fichier invalide, le type de fichier {0} n\'est pas pris en charge',
|
|
||||||
accept: undefined,
|
|
||||||
chooseLabel: 'Choisir',
|
|
||||||
name: undefined,
|
|
||||||
url: undefined,
|
|
||||||
maxFileSize: undefined,
|
|
||||||
fileLimit: undefined,
|
|
||||||
withCredentials: false,
|
|
||||||
previewWidth: 50,
|
|
||||||
uploadLabel: 'Téléverser',
|
|
||||||
cancelLabel: 'Annuler',
|
|
||||||
showCancelButton: true,
|
|
||||||
showUploadButton: true,
|
|
||||||
advanced: false
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'select',
|
|
||||||
'before-upload',
|
|
||||||
'progress',
|
|
||||||
'upload',
|
|
||||||
'error',
|
|
||||||
'before-send',
|
|
||||||
'clear',
|
|
||||||
'remove',
|
|
||||||
'removeUploadFile',
|
|
||||||
'uploader',
|
|
||||||
]);
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
upload: () => fileUploadRef.value.upload()
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleSelect = (event: FileUploadSelectEvent) => {
|
|
||||||
emit('select', event);
|
|
||||||
if (!props.advanced && event.files.length > 0 && fileUploadRef.value) {
|
|
||||||
fileUploadRef.value.files = [event.files.at(-1)];
|
|
||||||
fileUploadRef.value.uploadedFiles = [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClear = () => {
|
|
||||||
emit('clear');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpload = (event: FileUploadUploadEvent) => {
|
|
||||||
emit('upload', event);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemove = (event: FileUploadRemoveEvent) => {
|
|
||||||
emit('remove', event);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleProgress = (event: FileUploadProgressEvent) => {
|
|
||||||
emit('progress', event);
|
|
||||||
|
|
||||||
const files = fileUploadRef.value?.files || [];
|
|
||||||
files.forEach((file: File) => {
|
|
||||||
fileProgressMap.value[file.name] = event.progress;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleError = (event: FileUploadErrorEvent) => {
|
|
||||||
emit('error', event);
|
|
||||||
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) {
|
|
||||||
fileUploadRef.value.uploadedFiles = [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const lastSelectedFile = computed(() => {
|
|
||||||
const files = fileUploadRef.value?.files || [];
|
|
||||||
return files.at(-1) ?? null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const uploadEvent = (callback: () => void) => {
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
|
|
||||||
const padding = computed(() => props.advanced ? '1.125rem' : '0rem')
|
|
||||||
const borderColor = computed(() => props.advanced ? 'var(--border-default-grey)' : 'transparent');
|
|
||||||
|
|
||||||
const labelState = computed(() => {
|
|
||||||
if ((hasValidationError.value || hasSystemError.value) && !props.disabled) return 'error';
|
|
||||||
return 'default';
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
type MessageType = 'alert' | 'warning' | 'success' | 'info';
|
|
||||||
|
|
||||||
const globalStatusMessage = computed<{
|
|
||||||
type: MessageType;
|
|
||||||
title: string;
|
|
||||||
} | null>(() => {
|
|
||||||
const files = fileUploadRef.value?.files || [];
|
|
||||||
const uploaded = fileUploadRef.value?.uploadedFiles || [];
|
|
||||||
|
|
||||||
const hasError = files.some((f: { status: string; }) => f.status === 'error');
|
|
||||||
const hasPending = files.length > 0;
|
|
||||||
|
|
||||||
if (hasError) return { type: 'alert', title: 'Erreur lors du téléversement' };
|
|
||||||
if (hasPending) return { type: 'warning', title: 'En attente de téléversement' };
|
|
||||||
if (uploaded.length > 0) return { type: 'success', title: 'Tous les fichiers ont été téléversés' };
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="container" data-testid="file-label">
|
|
||||||
<VLabel
|
|
||||||
:for="`file-upload-${props.id}`"
|
|
||||||
:label="props.label"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:type="labelState"
|
|
||||||
:hint="props.hint"
|
|
||||||
/>
|
|
||||||
<FileUpload
|
|
||||||
ref="fileUploadRef"
|
|
||||||
:id="`file-upload-${props.id}`"
|
|
||||||
:multiple="props.advanced"
|
|
||||||
:invalid-file-limit-message="props.invalidFileLimitMessage"
|
|
||||||
:invalid-file-size-message="props.invalidFileSizeMessage"
|
|
||||||
:invalid-file-type-message="props.invalidFileTypeMessage"
|
|
||||||
mode="advanced"
|
|
||||||
:accept="props.accept"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
v-bind="$attrs"
|
|
||||||
:auto="!props.advanced"
|
|
||||||
:choose-label="props.chooseLabel"
|
|
||||||
:upload-label="props.uploadLabel"
|
|
||||||
:cancel-label="props.cancelLabel"
|
|
||||||
:name="props.name"
|
|
||||||
:url="props.url"
|
|
||||||
:max-file-size="props.maxFileSize"
|
|
||||||
:file-limit="props.fileLimit"
|
|
||||||
:with-credentials="props.withCredentials"
|
|
||||||
:preview-width="props.previewWidth"
|
|
||||||
:show-cancel-button="props.showCancelButton"
|
|
||||||
:show-upload-button="props.showUploadButton"
|
|
||||||
@select="handleSelect($event)"
|
|
||||||
@before-send="emit('before-send', $event)"
|
|
||||||
@progress="handleProgress($event)"
|
|
||||||
@before-upload="emit('before-upload', $event)"
|
|
||||||
@uploader="emit('uploader', $event)"
|
|
||||||
@upload="handleUpload($event)"
|
|
||||||
@error="handleError($event)"
|
|
||||||
@clear="handleClear()"
|
|
||||||
@remove="handleRemove($event)"
|
|
||||||
@remove-uploaded-file="emit('removeUploadFile', $event)"
|
|
||||||
class="p-fileupload"
|
|
||||||
>
|
|
||||||
<template #header="slotProps">
|
|
||||||
<VButtonGroup
|
|
||||||
v-if="props.advanced"
|
|
||||||
:buttons="[
|
|
||||||
{
|
|
||||||
label: 'Parcourir...',
|
|
||||||
onClick: slotProps.chooseCallback,
|
|
||||||
title: 'parcourir les fichiers',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Téléverser',
|
|
||||||
icon: 'ri-upload-cloud-line',
|
|
||||||
onClick: () => uploadEvent(slotProps.uploadCallback),
|
|
||||||
secondary: true,
|
|
||||||
disabled: !slotProps.files || slotProps.files.length === 0,
|
|
||||||
title: 'televerser les fichiers'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Supprimer',
|
|
||||||
tertiary: true,
|
|
||||||
onClick: slotProps.clearCallback,
|
|
||||||
disabled: !slotProps.files || slotProps.files.length === 0,
|
|
||||||
title: 'suprimer les fichiers'
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
size="sm"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
inlineLayoutWhen="always"
|
|
||||||
title="boutons de gestion des fichiers"
|
|
||||||
/>
|
|
||||||
<div v-else class="simple">
|
|
||||||
<VButton
|
|
||||||
label="Parcourir..."
|
|
||||||
:disabled="props.disabled"
|
|
||||||
size="sm"
|
|
||||||
@click="slotProps.chooseCallback"
|
|
||||||
title="parcourir les fichiers"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
v-if="(!slotProps.files || slotProps.files.length === 0) && (!slotProps.uploadedFiles || slotProps.uploadedFiles.length === 0) && !(hasSystemError || hasValidationError)"
|
|
||||||
:class="[styles['text-body-SM-detail-text-Regular']]"
|
|
||||||
>
|
|
||||||
Aucun fichier sélectionné
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #empty v-if="props.advanced && !(hasSystemError || hasValidationError)">
|
|
||||||
<div class="fileupload-empty" :class="[styles['text-body-SM-detail-text-Regular']]">
|
|
||||||
<i class="ri-upload-cloud-line upload-cloud-icon"></i>
|
|
||||||
<p>Glissez-déposez les fichiers ici pour les téléverser.</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #content="slotProps">
|
|
||||||
<div v-if="props.advanced && !(hasValidationError || hasSystemError)" style="margin-top: 0.75rem;" class="file-content">
|
|
||||||
<VMessage
|
|
||||||
v-if="globalStatusMessage"
|
|
||||||
:type="globalStatusMessage.type"
|
|
||||||
:title="globalStatusMessage.title"
|
|
||||||
/>
|
|
||||||
<VScrollPanel height="20rem">
|
|
||||||
<div class="file-area">
|
|
||||||
<div v-if="slotProps.files.length > 0" class="file-non-uploaded-area">
|
|
||||||
<VFile
|
|
||||||
v-for="(file, index) in slotProps.files"
|
|
||||||
:key="file.name + file.type + file.size"
|
|
||||||
:file="file"
|
|
||||||
:index="index"
|
|
||||||
:remove-file-callback="slotProps.removeFileCallback"
|
|
||||||
:progress="fileProgressMap[file.name] || 0"
|
|
||||||
status="pending"
|
|
||||||
advanced
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="slotProps.uploadedFiles.length > 0" class="file-uploaded-area">
|
|
||||||
<VFile
|
|
||||||
v-for="(file, index) in slotProps.uploadedFiles"
|
|
||||||
:key="file.name + file.type + file.size"
|
|
||||||
:file="file"
|
|
||||||
:index="index"
|
|
||||||
:remove-file-callback="slotProps.removeFileCallback"
|
|
||||||
:progress="100"
|
|
||||||
status="completed"
|
|
||||||
advanced
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VScrollPanel>
|
|
||||||
</div>
|
|
||||||
<div v-if="!props.advanced && !(hasValidationError || hasSystemError)">
|
|
||||||
<VFile
|
|
||||||
v-if="lastSelectedFile"
|
|
||||||
:file="lastSelectedFile"
|
|
||||||
:remove-file-callback="slotProps.removeFileCallback"
|
|
||||||
status="pending"
|
|
||||||
/>
|
|
||||||
<VFile
|
|
||||||
v-if="slotProps.uploadedFiles.length > 0 && slotProps.uploadedFiles.at(-1)"
|
|
||||||
:file="slotProps.uploadedFiles.at(-1)!"
|
|
||||||
:remove-file-callback="slotProps.removeFileCallback"
|
|
||||||
status="completed"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
role="alert"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
<VHint
|
|
||||||
v-for="message of [...(slotProps.messages ?? []), ...(hasSystemError ? [systemErrorMessage] : [])]"
|
|
||||||
:key="message"
|
|
||||||
:title="message"
|
|
||||||
type="alert"
|
|
||||||
icon
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<VLabelErrorProxy
|
|
||||||
:hasError="(slotProps.messages ?? []).length > 0"
|
|
||||||
@update:error="hasValidationError = $event"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</FileUpload>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
*{
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-fileupload{
|
|
||||||
width: 100%;
|
|
||||||
--p-fileupload-border-color: v-bind(borderColor);
|
|
||||||
padding: v-bind(padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
.simple{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--p-fileupload-file-gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileupload-empty{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 1rem;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-cloud-icon{
|
|
||||||
font-size: 5rem;
|
|
||||||
color: var(--border-contrast-grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-content,
|
|
||||||
.file-area{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-content{gap: 0.75rem;}
|
|
||||||
.file-area{gap: 0.5rem;}
|
|
||||||
|
|
||||||
.file-non-uploaded-area,
|
|
||||||
.file-uploaded-area {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
||||||
gap: 0.25rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,27 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, onUpdated, watch } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
hasError: boolean
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'update:error', value: boolean): void
|
|
||||||
}>();
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emit('update:error', props.hasError);
|
|
||||||
});
|
|
||||||
|
|
||||||
onUpdated(() => {
|
|
||||||
emit('update:error', props.hasError);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => props.hasError, (val) => {
|
|
||||||
emit('update:error', val);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<slot/>
|
|
||||||
</template>
|
|
|
@ -1,18 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the props for the Group component.
|
|
||||||
*/
|
|
||||||
export default interface IVGroup {
|
|
||||||
/**
|
|
||||||
* Defines the visual style or status of the group.
|
|
||||||
* - `'default'`: Standard appearance.
|
|
||||||
* - `'error'`: Indicates an error state.
|
|
||||||
* - `'success'`: Indicates a successful state.
|
|
||||||
* - `undefined`: No specific type applied.
|
|
||||||
*/
|
|
||||||
type: 'default' | 'error' | 'success' | undefined;
|
|
||||||
/**
|
|
||||||
* If true, disables the group component, making it non-interactive.
|
|
||||||
* Optional.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import type IVGroup from './IVGroup.type.js';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVGroup>(), {
|
|
||||||
type: 'default',
|
|
||||||
disabled: false,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div :class="['container', {
|
|
||||||
'error': type === 'error',
|
|
||||||
'success': type === 'success',
|
|
||||||
'disabled': props.disabled,
|
|
||||||
}]"
|
|
||||||
>
|
|
||||||
<slot/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
height: fit-content;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
padding: 0 0 0 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container.error {border-left: 2px solid var(--border-plain-error);}
|
|
||||||
.container.success {border-left: 2px solid var(--border-plain-success);}
|
|
||||||
.container.disabled {border: none;}
|
|
||||||
</style>
|
|
|
@ -1,28 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the properties of the Hint component.
|
|
||||||
*/
|
|
||||||
export default interface IVHint {
|
|
||||||
/**
|
|
||||||
* The title or main message displayed in the hint.
|
|
||||||
*/
|
|
||||||
title: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether an icon should be displayed alongside the hint.
|
|
||||||
* Optional. Defaults to false if not provided.
|
|
||||||
*/
|
|
||||||
icon?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the type of hint to display.
|
|
||||||
* Can be one of the following: 'info', 'warning', 'alert', 'success', 'active', or an empty string.
|
|
||||||
* Optional.
|
|
||||||
*/
|
|
||||||
type?: 'info' | 'warning' | 'alert' | 'success' | 'active' | '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the hint is disabled.
|
|
||||||
* Optional. When true, the hint may appear inactive or be hidden.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Message from 'primevue/message';
|
|
||||||
import type IVHint from './IVHint.type.js';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVHint>(), {
|
|
||||||
title: '',
|
|
||||||
icon: false,
|
|
||||||
type: '',
|
|
||||||
disabled: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'alert': return 'ri-spam-fill';
|
|
||||||
case 'warning' : return 'ri-alert-fill';
|
|
||||||
case 'success' : return 'ri-checkbox-circle-fill';
|
|
||||||
case 'info': return 'ri-information-fill';
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const severity = computed(() => {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'alert': return 'error';
|
|
||||||
case 'warning' : return 'warn';
|
|
||||||
case 'success' : return 'success';
|
|
||||||
case 'info' : return 'info';
|
|
||||||
case 'active': return 'contrast'
|
|
||||||
default:
|
|
||||||
return 'secondary';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Message
|
|
||||||
variant="simple"
|
|
||||||
:severity="severity"
|
|
||||||
class="p-message"
|
|
||||||
:class="[styles['text-body-XS-mention-text-Regular'], {'disabled': props.disabled }]"
|
|
||||||
:closable="false"
|
|
||||||
size="small"
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
{{ props.title }}
|
|
||||||
<template #icon v-if="props.icon">
|
|
||||||
<i :class="[iconClass]" style="font-size: var(--text-body-SM-detail-text-Regular-size);"></i>
|
|
||||||
</template>
|
|
||||||
</Message>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.p-message.disabled {color: var(--text-disabled-grey);}
|
|
||||||
|
|
||||||
.p-message {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: baseline;
|
|
||||||
padding: 0px;
|
|
||||||
gap: var(--p-message-content-gap);
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,274 +0,0 @@
|
||||||
import type { HintedString, Nullable} from '@primevue/core';
|
|
||||||
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface representing the props for the InputText component.
|
|
||||||
*/
|
|
||||||
export interface IInputText {
|
|
||||||
/**
|
|
||||||
* The unique identifier for the input element.
|
|
||||||
*/
|
|
||||||
id?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ID used to associate the input with a description element for accessibility.
|
|
||||||
*/
|
|
||||||
descriptionId?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional hint text displayed below the input.
|
|
||||||
*/
|
|
||||||
hint?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the input is in an invalid state.
|
|
||||||
*/
|
|
||||||
isInvalid?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the input is in a valid state.
|
|
||||||
*/
|
|
||||||
isValid?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, renders a <textarea> instead of a standard input.
|
|
||||||
*/
|
|
||||||
isTextarea?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, wraps the input in an additional wrapper element.
|
|
||||||
*/
|
|
||||||
isWithWrapper?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the visibility of the label. If false, the label is visually hidden.
|
|
||||||
*/
|
|
||||||
labelVisible?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text content of the label associated with the input.
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom CSS class applied to the label element.
|
|
||||||
*/
|
|
||||||
labelClass?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bound value of the input, supporting string or null.
|
|
||||||
*/
|
|
||||||
modelValue?: string | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom CSS class applied to the wrapper element.
|
|
||||||
*/
|
|
||||||
wrapperClass?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the input type is set to "password" to mask the input content.
|
|
||||||
*/
|
|
||||||
isPassword?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, disables the input field.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Placeholder text displayed inside the input when it's empty.
|
|
||||||
*/
|
|
||||||
placeholder?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error message(s) displayed when the input is invalid.
|
|
||||||
* Can be a single string or an array of strings.
|
|
||||||
*/
|
|
||||||
errorMessage?: string | string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success message(s) displayed when the input is valid.
|
|
||||||
* Can be a single string or an array of strings.
|
|
||||||
*/
|
|
||||||
validMessage?: string | string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines valid properties in Password component.
|
|
||||||
*/
|
|
||||||
export interface PasswordProps {
|
|
||||||
/**
|
|
||||||
* Value of the component.
|
|
||||||
*/
|
|
||||||
modelValue?: Nullable<string>;
|
|
||||||
/**
|
|
||||||
* The default value for the input when not controlled by `modelValue`.
|
|
||||||
*/
|
|
||||||
defaultValue?: Nullable<string>;
|
|
||||||
/**
|
|
||||||
* The name attribute for the element, typically used in form submissions.
|
|
||||||
*/
|
|
||||||
name?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to prompt password entry. Defaults to PrimeVue Locale configuration.
|
|
||||||
*/
|
|
||||||
promptLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Regex for a medium level password.
|
|
||||||
* @defaultValue ^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})
|
|
||||||
*/
|
|
||||||
mediumRegex?: string | RegExp | undefined;
|
|
||||||
/**
|
|
||||||
* Regex for a strong level password.
|
|
||||||
* @defaultValue ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})
|
|
||||||
*/
|
|
||||||
strongRegex?: string | RegExp | undefined;
|
|
||||||
/**
|
|
||||||
* Text for a weak password. Defaults to PrimeVue Locale configuration.
|
|
||||||
*/
|
|
||||||
weakLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text for a medium password. Defaults to PrimeVue Locale configuration.
|
|
||||||
*/
|
|
||||||
mediumLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text for a strong password. Defaults to PrimeVue Locale configuration.
|
|
||||||
*/
|
|
||||||
strongLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to show the strength indicator or not.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
feedback?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* A valid query selector or an HTMLElement to specify where the overlay gets attached.
|
|
||||||
* @defaultValue body
|
|
||||||
*/
|
|
||||||
appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
|
|
||||||
/**
|
|
||||||
* Whether to show an icon to display the password as plain text.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
toggleMask?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to hide displaying the password as plain text.
|
|
||||||
*/
|
|
||||||
maskIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to show displaying the password as plain text.
|
|
||||||
*/
|
|
||||||
unmaskIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Defines the size of the component.
|
|
||||||
*/
|
|
||||||
size?: HintedString<'small' | 'large'> | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that the component should have invalid state style.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
invalid?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that the component should be disabled.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
disabled?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Specifies the input variant of the component.
|
|
||||||
* @defaultValue null
|
|
||||||
*/
|
|
||||||
variant?: HintedString<'outlined' | 'filled'> | undefined | null;
|
|
||||||
/**
|
|
||||||
* Placeholder text for the input.
|
|
||||||
*/
|
|
||||||
placeholder?: string | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that an input field must be filled out before submitting the form.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
required?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Spans 100% width of the container when enabled.
|
|
||||||
* @defaultValue null
|
|
||||||
*/
|
|
||||||
fluid?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that an input element should automatically get focus when the page loads.
|
|
||||||
* @defaultValue null
|
|
||||||
*/
|
|
||||||
autofocus?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Identifier of the underlying input element.
|
|
||||||
*/
|
|
||||||
inputId?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the input field.
|
|
||||||
*/
|
|
||||||
inputStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the input field.
|
|
||||||
*/
|
|
||||||
inputClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the HTMLInputElement to the focusable input element inside the component.
|
|
||||||
*/
|
|
||||||
inputProps?: InputHTMLAttributes | undefined;
|
|
||||||
/**
|
|
||||||
* Identifier of the underlying overlay panel element.
|
|
||||||
*/
|
|
||||||
panelId?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the overlay panel.
|
|
||||||
*/
|
|
||||||
panelClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the overlay panel.
|
|
||||||
*/
|
|
||||||
panelStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the HTMLDivElement to the overlay panel inside the component.
|
|
||||||
*/
|
|
||||||
panelProps?: HTMLAttributes | undefined;
|
|
||||||
/**
|
|
||||||
* Identifier of the underlying overlay element.
|
|
||||||
*/
|
|
||||||
overlayId?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the overlay.
|
|
||||||
*/
|
|
||||||
overlayClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the overlay.
|
|
||||||
*/
|
|
||||||
overlayStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the HTMLDivElement to the overlay inside the component.
|
|
||||||
*/
|
|
||||||
overlayProps?: HTMLAttributes | undefined;
|
|
||||||
/**
|
|
||||||
* Establishes relationships between the component and label(s) where its value should be one or more element IDs.
|
|
||||||
*/
|
|
||||||
ariaLabelledby?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Establishes a string value that labels the component.
|
|
||||||
*/
|
|
||||||
ariaLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Form control object, typically used for handling validation and form state.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extended interface for the VInputText component.
|
|
||||||
* Combines selected properties from PasswordProps and IInputText,
|
|
||||||
* with additional support for password hints.
|
|
||||||
*/
|
|
||||||
export default interface IVInput
|
|
||||||
extends Partial<Omit<PasswordProps, 'modelValue'>>,
|
|
||||||
Partial<Omit<IInputText, 'wrapperClass' | 'labelClass'>> {
|
|
||||||
/**
|
|
||||||
* Hint(s) displayed to guide the user when entering a password.
|
|
||||||
* Can be a single string or an array of strings.
|
|
||||||
*/
|
|
||||||
passwordHint?: string | Array<string>;
|
|
||||||
}
|
|
|
@ -1,301 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import InputText from 'primevue/inputtext';
|
|
||||||
import Textarea from 'primevue/textarea';
|
|
||||||
import VHint from '../hint/VHint.vue';
|
|
||||||
import VDivider from '../divider/VDivider.vue';
|
|
||||||
import VLabel from '../label/VLabel.vue';
|
|
||||||
import type IVInput from './IVInput.type.js';
|
|
||||||
import { computed, useAttrs, useId, ref, watch } from 'vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
import Password from 'primevue/password';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVInput>(), {
|
|
||||||
id: () => useId(),
|
|
||||||
descriptionId: undefined,
|
|
||||||
hint: '',
|
|
||||||
label: '',
|
|
||||||
modelValue: '',
|
|
||||||
wrapperClass: '',
|
|
||||||
isInvalid: false,
|
|
||||||
isValid: false,
|
|
||||||
isTextarea: false,
|
|
||||||
labelVisible: false,
|
|
||||||
isPassword: false,
|
|
||||||
weakLabel: 'Faible',
|
|
||||||
mediumLabel: 'Moyen',
|
|
||||||
strongLabel: 'Fort',
|
|
||||||
promptLabel: 'Entrez un mot de passe',
|
|
||||||
toggleMask: false,
|
|
||||||
placeholder: '',
|
|
||||||
passwordHint: undefined,
|
|
||||||
disabled: false,
|
|
||||||
errorMessage: '',
|
|
||||||
validMessage: '',
|
|
||||||
feedback: false,
|
|
||||||
})
|
|
||||||
const attrs = useAttrs()
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'update:modelValue',
|
|
||||||
'value-change',
|
|
||||||
'change'
|
|
||||||
])
|
|
||||||
|
|
||||||
const localModelValue = ref(props.modelValue);
|
|
||||||
watch(() => props.modelValue, (newVal) => {
|
|
||||||
if(localModelValue.value !== newVal) {
|
|
||||||
localModelValue.value = newVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
watch(localModelValue, (newVal) => {
|
|
||||||
if(props.modelValue !== newVal){
|
|
||||||
emit('update:modelValue', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const labelState = computed(() => {
|
|
||||||
if(!props.isInvalid && !props.isValid && !props.disabled) return 'default';
|
|
||||||
else if(!props.isInvalid && props.isValid && !props.disabled) return 'success';
|
|
||||||
else if(props.isInvalid && !props.isValid && !props.disabled) return 'error';
|
|
||||||
else return undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="main-container">
|
|
||||||
<VLabel
|
|
||||||
v-if="props.labelVisible"
|
|
||||||
:for="props.id"
|
|
||||||
:label="props.label"
|
|
||||||
:required="!props.disabled && props.required"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:type="labelState"
|
|
||||||
:hint="props.hint"
|
|
||||||
>
|
|
||||||
<template #required-type v-if="props.required">
|
|
||||||
<slot name="required-type"/>
|
|
||||||
</template>
|
|
||||||
</VLabel>
|
|
||||||
<!-- Password -->
|
|
||||||
<Password
|
|
||||||
v-if="props.isPassword"
|
|
||||||
:id="props.id"
|
|
||||||
v-bind="attrs"
|
|
||||||
class="p-password"
|
|
||||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
|
||||||
'error': props.isInvalid && !props.isValid && !props.disabled || !!props.errorMessage,
|
|
||||||
'success': !props.isInvalid && props.isValid && !props.disabled && !props.errorMessage || !!props.validMessage,
|
|
||||||
'disabled': props.disabled,
|
|
||||||
}]"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:aria-disabled="props.disabled"
|
|
||||||
:aria-describedby="descriptionId || undefined"
|
|
||||||
:weakLabel="props.weakLabel"
|
|
||||||
:mediumLabel="props.mediumLabel"
|
|
||||||
:strongLabel="props.strongLabel"
|
|
||||||
:promptLabel="props.promptLabel"
|
|
||||||
:toggleMask="props.toggleMask && !props.disabled"
|
|
||||||
:placeholder="props.placeholder"
|
|
||||||
:feedback="props.feedback"
|
|
||||||
:fluid="true"
|
|
||||||
v-model:model-value="localModelValue"
|
|
||||||
@update:model-value="emit('update:modelValue', $event)"
|
|
||||||
@valueChange="emit('value-change', $event)"
|
|
||||||
@change="emit('change', $event)"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<div
|
|
||||||
:class="styles['text-body-MD-standard-text-Medium']"
|
|
||||||
style="padding-bottom: 0.5rem;"
|
|
||||||
>Niveau de complexité</div>
|
|
||||||
</template>
|
|
||||||
<template v-if="props.passwordHint !== undefined" #footer>
|
|
||||||
<VDivider/>
|
|
||||||
<VHint title="Votre mot de passe doit contenir au moins :"/>
|
|
||||||
<div
|
|
||||||
role="alert"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
<template v-if="Array.isArray(props.passwordHint)">
|
|
||||||
<VHint
|
|
||||||
v-for="message in props.passwordHint"
|
|
||||||
:key="`password-hint-${message}`"
|
|
||||||
:id="descriptionId"
|
|
||||||
:data-testid="descriptionId"
|
|
||||||
:title="message"
|
|
||||||
type="info"
|
|
||||||
icon
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<VHint
|
|
||||||
v-else-if="props.passwordHint"
|
|
||||||
:title="props.passwordHint"
|
|
||||||
:id="descriptionId"
|
|
||||||
:data-testid="descriptionId"
|
|
||||||
type="info"
|
|
||||||
icon
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Password>
|
|
||||||
|
|
||||||
<!-- Textarea -->
|
|
||||||
<Textarea
|
|
||||||
v-else-if="props.isTextarea"
|
|
||||||
:id="props.id"
|
|
||||||
v-bind="attrs"
|
|
||||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
|
||||||
'p-textarea': true,
|
|
||||||
'error': props.isInvalid && !props.isValid && !props.disabled || !!props.errorMessage,
|
|
||||||
'success': !props.isInvalid && props.isValid && !props.disabled && !props.errorMessage || !!props.validMessage,
|
|
||||||
}]"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:aria-disabled="props.disabled"
|
|
||||||
:aria-describedby="descriptionId || undefined"
|
|
||||||
:placeholder="props.placeholder"
|
|
||||||
@valueChange="emit('value-change', $event)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- InputText -->
|
|
||||||
<InputText
|
|
||||||
v-else
|
|
||||||
:id="props.id"
|
|
||||||
v-bind="attrs"
|
|
||||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
|
||||||
'p-inputtext': true,
|
|
||||||
'error': props.isInvalid && !props.isValid && !props.disabled || !!props.errorMessage,
|
|
||||||
'success': !props.isInvalid && props.isValid && !props.disabled && !props.errorMessage || !!props.validMessage,
|
|
||||||
}]"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:aria-disabled="props.disabled"
|
|
||||||
:aria-describedby="descriptionId || undefined"
|
|
||||||
:placeholder="props.placeholder"
|
|
||||||
v-model:model-value="localModelValue"
|
|
||||||
@update:model-value="emit('update:modelValue', $event)"
|
|
||||||
@valueChange="emit('value-change', $event)"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
role="alert"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
<template v-if="Array.isArray(props.errorMessage)">
|
|
||||||
<VHint
|
|
||||||
v-for="message in props.errorMessage"
|
|
||||||
:id="descriptionId"
|
|
||||||
:key="message"
|
|
||||||
:data-testid="descriptionId"
|
|
||||||
:title="message"
|
|
||||||
type="alert"
|
|
||||||
icon
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<VHint
|
|
||||||
v-else-if="errorMessage"
|
|
||||||
:id="descriptionId"
|
|
||||||
:key="`error-${errorMessage}`"
|
|
||||||
:data-testid="descriptionId"
|
|
||||||
:title="`${errorMessage}`"
|
|
||||||
type="alert"
|
|
||||||
icon
|
|
||||||
/>
|
|
||||||
<template v-if="Array.isArray(props.validMessage)">
|
|
||||||
<VHint
|
|
||||||
v-for="message in props.validMessage"
|
|
||||||
:id="descriptionId"
|
|
||||||
:key="message"
|
|
||||||
:data-testid="descriptionId"
|
|
||||||
:title="message"
|
|
||||||
type="success"
|
|
||||||
icon
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<VHint
|
|
||||||
v-else-if="validMessage"
|
|
||||||
:id="descriptionId"
|
|
||||||
:key="`error-${validMessage}`"
|
|
||||||
:data-testid="descriptionId"
|
|
||||||
:title="`${validMessage}`"
|
|
||||||
type="success"
|
|
||||||
icon
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
|
|
||||||
.main-container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-textarea {
|
|
||||||
min-height: 5.5rem;
|
|
||||||
max-height: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-password,
|
|
||||||
.p-inputtext { height: 2.5rem;}
|
|
||||||
|
|
||||||
.p-password,
|
|
||||||
.p-textarea,
|
|
||||||
.p-inputtext {width: inherit;}
|
|
||||||
|
|
||||||
|
|
||||||
.p-textarea.success, .p-textarea.success:focus-visible, .p-textarea.success:hover,
|
|
||||||
.p-inputtext.success, .p-inputtext.success:focus-visible, .p-inputtext.success:hover {
|
|
||||||
border: var(--large-border-width) solid var(--border-plain-success);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.p-password.success{
|
|
||||||
--p-inputtext-border-color: transparent;
|
|
||||||
--p-inputtext-hover-border-color: transparent;
|
|
||||||
--p-inputtext-focus-border-color: transparent;
|
|
||||||
border-radius: var(--input-border-raduis);
|
|
||||||
border: var(--large-border-width) solid var(--border-plain-success);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-password.error{
|
|
||||||
--p-inputtext-border-color: transparent;
|
|
||||||
--p-inputtext-hover-border-color: transparent;
|
|
||||||
--p-inputtext-focus-border-color: transparent;
|
|
||||||
border-radius: var(--input-border-raduis);
|
|
||||||
border: var(--large-border-width) solid var(--border-plain-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-textarea.error, .p-textarea.error:focus-visible, .p-textarea.error:hover,
|
|
||||||
.p-inputtext.error, .p-inputtext.error:focus-visible, .p-inputtext.error:hover {border: var(--large-border-width) solid var(--border-plain-error);}
|
|
||||||
|
|
||||||
|
|
||||||
.p-textarea:disabled,
|
|
||||||
.p-inputtext:disabled {
|
|
||||||
border: var(--border-width) solid var(--border-disabled-grey);
|
|
||||||
color: var(--text-disabled-grey);
|
|
||||||
background-color: var(--background-disabled-grey);
|
|
||||||
--p-inputtext-placeholder-color: var(--text-disabled-grey);
|
|
||||||
--p-form-field-placeholder-color: var(--text-disabled-grey);
|
|
||||||
--p-textarea-placeholder-color: var(--text-disabled-grey);
|
|
||||||
--p-form-field-border-color: var(--border-disabled-grey);
|
|
||||||
--p-inputtext-border-color: var(--border-disabled-grey);
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-password.disabled{
|
|
||||||
--p-inputtext-border-color: var(--border-disabled-grey);
|
|
||||||
--p-inputtext-hover-border-color: var(--border-disabled-grey);
|
|
||||||
--p-inputtext-focus-border-color: var(--border-disabled-grey);
|
|
||||||
--p-inputtext-placeholder-color: var(--text-disabled-grey);
|
|
||||||
--p-form-field-placeholder-color: var(--text-disabled-grey);
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,18 +0,0 @@
|
||||||
export type { default as IVAccordion } from '../accordion/IVAccordion.type.js';
|
|
||||||
export type { default as IVAlert } from '../alert/IVAlert.type.js';
|
|
||||||
export type { default as IVBadge } from '../badge/IVBadge.type.js';
|
|
||||||
export type { default as IVButton } from '../button/IVButton.type.js';
|
|
||||||
export type { default as IVButtonGroup } from '../button/IVButton.type.js';
|
|
||||||
export type { default as IVCheckbox } from '../checkbox/IVCheckbox.type.js';
|
|
||||||
export type { default as IVLink } from '../button/IVLink.type.js';
|
|
||||||
export type { default as IVFileUpload } from '../file/IVFileUpload.type.js';
|
|
||||||
export type { default as IVGroup } from '../group/IVGroup.type.js';
|
|
||||||
export type { default as IVHint } from '../hint/IVHint.type.js';
|
|
||||||
export type { default as IVInput } from '../input/IVInput.type.js';
|
|
||||||
export type { default as IVLabel } from '../label/IVLabel.type.js';
|
|
||||||
export type { default as IVMessage } from '../message/IVMessage.type.js';
|
|
||||||
export type { default as IVModal } from '../modal/IVModal.type.js';
|
|
||||||
export type { default as IVProgressBar } from '../progressbar/IVProgressBar.type.js';
|
|
||||||
export type { default as IVSelect } from '../select/IVSelect.type.js';
|
|
||||||
export type { default as IVMenuBar } from '../menu/IVMenuBar.type.js';
|
|
||||||
export type { default as IVMenuBarItem } from '../menu/IVMenuBar.type.js';
|
|
|
@ -1,46 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the props for the Label component.
|
|
||||||
*/
|
|
||||||
export default interface IVLabel {
|
|
||||||
/**
|
|
||||||
* The text content displayed as the label.
|
|
||||||
*/
|
|
||||||
label: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The visual style of the label, indicating its semantic meaning.
|
|
||||||
* - `default`: Standard label appearance.
|
|
||||||
* - `error`: Indicates an error state.
|
|
||||||
* - `success`: Indicates a successful or valid state.
|
|
||||||
*
|
|
||||||
* @default 'default'
|
|
||||||
*/
|
|
||||||
type?: 'default' | 'error' | 'success' | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the label is disabled. When true, the label appears inactive or grayed out.
|
|
||||||
*
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional hint text displayed alongside or below the label to provide additional context.
|
|
||||||
*/
|
|
||||||
hint?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the associated field is required.
|
|
||||||
*
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
required?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ID of the input element that this label is associated with.
|
|
||||||
* This helps improve accessibility by linking the label to its corresponding input.
|
|
||||||
*/
|
|
||||||
for: string;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import type IVLabel from './IVLabel.type.js';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
import VHint from '../hint/VHint.vue';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVLabel>(), {
|
|
||||||
type: 'default',
|
|
||||||
disabled: false,
|
|
||||||
required: false,
|
|
||||||
hint: undefined,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<label
|
|
||||||
:for="props.for"
|
|
||||||
class="label-container"
|
|
||||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
|
||||||
'label': props.type === 'default',
|
|
||||||
'success': props.type === 'success',
|
|
||||||
'error': props.type === 'error',
|
|
||||||
'disabled': props.disabled,
|
|
||||||
}]"
|
|
||||||
:aria-label="props.label"
|
|
||||||
:aria-disabled="props.disabled"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{{ props.label }}
|
|
||||||
<template v-if="props.required">
|
|
||||||
<span v-if="props.required" :class="{ 'required': !props.disabled}">
|
|
||||||
<slot name="required-tip">*</slot>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</span>
|
|
||||||
<VHint
|
|
||||||
v-if="props.hint"
|
|
||||||
:title="props.hint"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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);}
|
|
||||||
.success {color: var(--text-default-success);}
|
|
||||||
.error {color: var(--text-default-error);}
|
|
||||||
.disabled {color: var(--text-disabled-grey);}
|
|
||||||
.required {
|
|
||||||
color: var(--minor-red-marianne);
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,93 +0,0 @@
|
||||||
import type { HTMLAttributes } from "vue"
|
|
||||||
import type { RouteLocationRaw } from "vue-router"
|
|
||||||
import type { MenuItem } from "primevue/menuitem"
|
|
||||||
/**
|
|
||||||
* Describes a link or button item in a menu bar.
|
|
||||||
*/
|
|
||||||
export interface IVMenuBarLinks {
|
|
||||||
/** Whether the item should be rendered as a button */
|
|
||||||
button?: boolean;
|
|
||||||
|
|
||||||
/** Name of the icon to display on the left */
|
|
||||||
icon?: string;
|
|
||||||
|
|
||||||
/** Whether the icon appears on the right side */
|
|
||||||
iconRight?: boolean;
|
|
||||||
|
|
||||||
/** Text label for the item */
|
|
||||||
label?: string;
|
|
||||||
|
|
||||||
/** Target URL if rendered as an anchor (<a>) */
|
|
||||||
target?: string;
|
|
||||||
|
|
||||||
/** Callback executed when the item is clicked */
|
|
||||||
onClick?: ($event: MouseEvent) => void;
|
|
||||||
|
|
||||||
/** Navigation route */
|
|
||||||
to?: RouteLocationRaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extends PrimeVue's MenuItem with optional routing capabilities.
|
|
||||||
* Excludes 'style' and 'class' to avoid conflicts with Vue attributes.
|
|
||||||
*/
|
|
||||||
export interface IVMenuBarItem
|
|
||||||
extends Partial<Omit<MenuItem, 'style' | 'class'>>,
|
|
||||||
Partial<Pick<IVMenuBarLinks, 'to'>> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for configuring a customizable menu bar component.
|
|
||||||
*/
|
|
||||||
export default interface IVMenuBar {
|
|
||||||
/** HTML id for the searchbar input */
|
|
||||||
searchbarId?: string;
|
|
||||||
|
|
||||||
/** Title of the service or application */
|
|
||||||
serviceTitle?: string;
|
|
||||||
|
|
||||||
/** Description of the service */
|
|
||||||
serviceDescription?: string;
|
|
||||||
|
|
||||||
/** Text displayed beside the logo (can be multiline) */
|
|
||||||
logoText?: string | string[];
|
|
||||||
|
|
||||||
/** Whether to show the logo */
|
|
||||||
logo?: boolean;
|
|
||||||
|
|
||||||
/** Bound model value for the search input */
|
|
||||||
modelValue?: string;
|
|
||||||
|
|
||||||
/** Placeholder text for the search input */
|
|
||||||
placeholder?: string;
|
|
||||||
|
|
||||||
/** List of quick link items with additional HTML attributes */
|
|
||||||
quickLinks?: (IVMenuBarItem & HTMLAttributes)[];
|
|
||||||
|
|
||||||
/** Label for the search input (accessibility) */
|
|
||||||
searchLabel?: string;
|
|
||||||
|
|
||||||
/** ARIA label for the quick links section */
|
|
||||||
quickLinksAriaLabel?: string;
|
|
||||||
|
|
||||||
/** Whether to display the search bar */
|
|
||||||
showSearch?: boolean;
|
|
||||||
|
|
||||||
/** Label describing the search bar visibility */
|
|
||||||
showSearchLabel?: string;
|
|
||||||
|
|
||||||
/** Label for the main menu toggle button */
|
|
||||||
menuLabel?: string;
|
|
||||||
|
|
||||||
/** Label used in modal menus */
|
|
||||||
menuModalLabel?: string;
|
|
||||||
|
|
||||||
/** Label for closing the modal menu */
|
|
||||||
closeMenuModalLabel?: string;
|
|
||||||
|
|
||||||
/** Responsive breakpoints (e.g. Tailwind-style classes) */
|
|
||||||
breakpoints?: string;
|
|
||||||
|
|
||||||
logoPath?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Menubar from 'primevue/menubar';
|
|
||||||
import type IVMenuBar from './IVMenuBar.type.js';
|
|
||||||
import styles from '@visua/typography.module.css'
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVMenuBar>(), {
|
|
||||||
searchbarId: 'searchbar-header',
|
|
||||||
serviceTitle: undefined,
|
|
||||||
serviceDescription: undefined,
|
|
||||||
logo: false,
|
|
||||||
logoText: undefined,
|
|
||||||
breakpoints: '960px',
|
|
||||||
quickLinks: undefined,
|
|
||||||
menuLabel: undefined,
|
|
||||||
logoPath: '/home',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Menubar
|
|
||||||
role="banner"
|
|
||||||
:model="props.quickLinks"
|
|
||||||
:breakpoint="props.breakpoints"
|
|
||||||
class="p-menubar"
|
|
||||||
>
|
|
||||||
<template #start>
|
|
||||||
<RouterLink :to=props.logoPath class="logo-container">
|
|
||||||
<div v-if="$slots.logo" class="logo">
|
|
||||||
<slot name="logo"/>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
v-if="!!props.serviceTitle"
|
|
||||||
:class="[styles['titles-H6-XXS']]"
|
|
||||||
style="color: var(--text-title-grey);"
|
|
||||||
>
|
|
||||||
{{ props.serviceTitle }}
|
|
||||||
</span>
|
|
||||||
</RouterLink>
|
|
||||||
</template>
|
|
||||||
<template #end>
|
|
||||||
<slot name="end"/>
|
|
||||||
</template>
|
|
||||||
<template #item="{ item, props, hasSubmenu }">
|
|
||||||
<component
|
|
||||||
v-if="hasSubmenu || 'label' in item || 'icon' in item"
|
|
||||||
:is="item.to ? 'RouterLink' : item.url ? 'a' : 'div'"
|
|
||||||
:to="item.to"
|
|
||||||
:href="item.url"
|
|
||||||
:target="item.target"
|
|
||||||
:tabindex="0"
|
|
||||||
v-bind="props.action"
|
|
||||||
class="item"
|
|
||||||
:class="[styles['text-body-LG-article-text-Regular'],
|
|
||||||
item.to === route.path ? 'active' : ''
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<slot name="itemicon" :item="item" v-if="'icon' in item">
|
|
||||||
<i :class="[item.icon]"></i>
|
|
||||||
</slot>
|
|
||||||
<span v-if="'label' in item" style="width: 100%;">{{ item.label }}</span>
|
|
||||||
<slot name="submenuicon" v-if="hasSubmenu">
|
|
||||||
<i class="ri-arrow-down-s-line"/>
|
|
||||||
</slot>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
<template #button="{id, toggleCallback}">
|
|
||||||
<slot name="button" :id="id" :toggleCallback="toggleCallback"/>
|
|
||||||
</template>
|
|
||||||
<template #buttonicon>
|
|
||||||
<slot name="buttonicon"/>
|
|
||||||
</template>
|
|
||||||
</Menubar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
*{
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a{
|
|
||||||
color: var(--p-menubar-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.logo{
|
|
||||||
display: block;
|
|
||||||
width: 25%;
|
|
||||||
height: auto;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-container{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: fit-content;
|
|
||||||
gap: var(--p-menubar-gap);
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-menubar{
|
|
||||||
width: 100%;
|
|
||||||
height: 4.5rem;
|
|
||||||
border-width: 0px 0px var(--border-width) 0px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: var(--p-menubar-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:hover,
|
|
||||||
.item.active{
|
|
||||||
color: var(--menu-active-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item.active {
|
|
||||||
height: 100%;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,25 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the structure of a message notice component.
|
|
||||||
*/
|
|
||||||
export default interface IVMessage {
|
|
||||||
/**
|
|
||||||
* The title or main text of the notice.
|
|
||||||
*/
|
|
||||||
title: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of notice, which affects its visual style and semantics.
|
|
||||||
* - `info`: Informational message
|
|
||||||
* - `warning`: Warning message
|
|
||||||
* - `alert`: Error or critical alert
|
|
||||||
* - `success`: Confirmation or success message
|
|
||||||
* - undefined: No specific type
|
|
||||||
*/
|
|
||||||
type?: 'info' | 'warning' | 'alert' | 'success' | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the notice can be closed by the user.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
closable?: boolean;
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import type IVMessage from './IVMessage.type.js';
|
|
||||||
import Message from 'primevue/message';
|
|
||||||
import VButton from '../button/VButton.vue';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVMessage>(), {
|
|
||||||
type: undefined,
|
|
||||||
closable: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['close']);
|
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'alert': return 'ri-spam-fill';
|
|
||||||
case 'warning' : return 'ri-alert-fill';
|
|
||||||
case 'success' : return 'ri-checkbox-circle-fill';
|
|
||||||
case 'info': return 'ri-information-fill';
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const severity = computed(() => {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'alert': return 'error';
|
|
||||||
case 'warning' : return 'warn';
|
|
||||||
case 'success' : return 'success';
|
|
||||||
case 'info' : return 'info';
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const iconColor = computed(() => {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'alert': return 'var(--text-default-error)';
|
|
||||||
case 'warning' : return 'var(--text-default-warning)';
|
|
||||||
case 'success' : return 'var(--text-default-success)';
|
|
||||||
case 'info': return 'var(--text-default-info)';
|
|
||||||
default:
|
|
||||||
return 'var(--text-mention-grey)';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Message
|
|
||||||
role="alert"
|
|
||||||
:closable="props.closable"
|
|
||||||
:severity="severity"
|
|
||||||
size="large"
|
|
||||||
class="p-message"
|
|
||||||
@close="emit('close', $event)"
|
|
||||||
>
|
|
||||||
<template #container="slotProps">
|
|
||||||
<div class="container">
|
|
||||||
<div class="header">
|
|
||||||
<i :class="[iconClass]" :style="{color: iconColor}" style="font-size: var(--p-message-icon-lg-size);"></i>
|
|
||||||
<span class="title" :class="[styles['text-body-MD-standard-text-Regular']]" :style="{color: iconColor}">
|
|
||||||
{{ props.title }}
|
|
||||||
</span>
|
|
||||||
<VButton
|
|
||||||
v-if="props.closable"
|
|
||||||
title="Fermer le message"
|
|
||||||
tertiary
|
|
||||||
no-outline
|
|
||||||
size="sm"
|
|
||||||
icon-only
|
|
||||||
aria-label="Fermer"
|
|
||||||
icon="ri-close-line"
|
|
||||||
type="button"
|
|
||||||
@click="slotProps.closeCallback"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="$slots.content" class="content">
|
|
||||||
<slot name="content"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Message>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.p-message{
|
|
||||||
width: 100%;
|
|
||||||
height: fit-content;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container{
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--p-message-content-gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: var(--p-message-content-gap);
|
|
||||||
align-items: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title{
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content{
|
|
||||||
display: block;
|
|
||||||
padding: 0rem 0.825rem 0.5rem 0.825rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,205 +0,0 @@
|
||||||
import type { HTMLAttributes } from 'vue';
|
|
||||||
import type IVButton from '../button/IVButton.type.js';
|
|
||||||
import { type DialogBreakpoints } from 'primevue';
|
|
||||||
import type { HintedString } from '@primevue/core';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface representing the properties for a Modal component.
|
|
||||||
*/
|
|
||||||
export interface IModal {
|
|
||||||
/**
|
|
||||||
* Optional unique identifier for the modal element.
|
|
||||||
*/
|
|
||||||
modalId?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag indicating whether the modal is currently open.
|
|
||||||
*/
|
|
||||||
opened?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of actions (buttons) displayed in the modal.
|
|
||||||
*/
|
|
||||||
actions?: IVButton[]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if the modal should behave like an alert dialog.
|
|
||||||
*/
|
|
||||||
isAlert?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the originating element that triggered the modal,
|
|
||||||
* used to return focus when the modal is closed.
|
|
||||||
*/
|
|
||||||
origin?: { focus: () => void }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Title displayed at the top of the modal.
|
|
||||||
*/
|
|
||||||
title: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional icon name or path to render next to the title.
|
|
||||||
*/
|
|
||||||
icon?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the modal size: 'sm' = small, 'md' = medium, 'lg' = large.
|
|
||||||
*/
|
|
||||||
size?: 'sm' | 'md' | 'lg'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accessible label (aria-label) for the close button.
|
|
||||||
*/
|
|
||||||
closeButtonLabel?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tooltip (title attribute) shown when hovering over the close button.
|
|
||||||
*/
|
|
||||||
closeButtonTitle?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines valid properties in Dialog component.
|
|
||||||
*/
|
|
||||||
export interface DialogProps {
|
|
||||||
/**
|
|
||||||
* Title content of the dialog.
|
|
||||||
*/
|
|
||||||
header?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Footer content of the dialog.
|
|
||||||
*/
|
|
||||||
footer?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Specifies the visibility of the dialog.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
visible?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Defines if background should be blocked when dialog is displayed.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
modal?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Style of the content section.
|
|
||||||
*/
|
|
||||||
contentStyle?: unknown;
|
|
||||||
/**
|
|
||||||
* Style class of the content section.
|
|
||||||
*/
|
|
||||||
contentClass?: unknown;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the HTMLDivElement to the overlay Dialog inside the component.
|
|
||||||
*/
|
|
||||||
contentProps?: HTMLAttributes | undefined;
|
|
||||||
/**
|
|
||||||
* Adds a close icon to the header to hide the dialog.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
closable?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Specifies if clicking the modal background should hide the dialog.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
dismissableMask?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Specifies if pressing escape key should hide the dialog.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
closeOnEscape?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to show the header or not.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
showHeader?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether background scroll should be blocked when dialog is visible.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
blockScroll?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Base zIndex value to use in layering.
|
|
||||||
* @defaultValue 0
|
|
||||||
*/
|
|
||||||
baseZIndex?: number | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to automatically manage layering.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
autoZIndex?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Position of the dialog.
|
|
||||||
* @defaultValue center
|
|
||||||
*/
|
|
||||||
position?: HintedString<'center' | 'top' | 'bottom' | 'left' | 'right' | 'topleft' | 'topright' | 'bottomleft' | 'bottomright'> | undefined;
|
|
||||||
/**
|
|
||||||
* Whether the dialog can be displayed full screen.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
maximizable?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Object literal to define widths per screen size.
|
|
||||||
*/
|
|
||||||
breakpoints?: DialogBreakpoints;
|
|
||||||
/**
|
|
||||||
* Enables dragging to change the position using header.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
draggable?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Keeps dialog in the viewport when dragging.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
keepInViewport?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Minimum value for the left coordinate of dialog in dragging.
|
|
||||||
* @defaultValue 0.
|
|
||||||
*/
|
|
||||||
minX?: number | undefined;
|
|
||||||
/**
|
|
||||||
* Minimum value for the top coordinate of dialog in dragging.
|
|
||||||
* @defaultValue 0
|
|
||||||
*/
|
|
||||||
minY?: number | undefined;
|
|
||||||
/**
|
|
||||||
* A valid query selector or an HTMLElement to specify where the dialog gets attached.
|
|
||||||
* @defaultValue body
|
|
||||||
*/
|
|
||||||
appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
|
|
||||||
/**
|
|
||||||
* Icon to display in the dialog close button.
|
|
||||||
*/
|
|
||||||
closeIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in the dialog maximize button when dialog is not maximized.
|
|
||||||
*/
|
|
||||||
maximizeIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in the dialog maximize button when dialog is minimized.
|
|
||||||
*/
|
|
||||||
minimizeIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the ButtonProps to the Button component.
|
|
||||||
* @type {ButtonProps}
|
|
||||||
* @defaultValue { severity: 'secondary', rounded: true, text: true }
|
|
||||||
*/
|
|
||||||
closeButtonProps?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the ButtonProps to the Button component.
|
|
||||||
* @type {ButtonProps}
|
|
||||||
* @defaultValue { severity: 'secondary', rounded: true, text: true }
|
|
||||||
*/
|
|
||||||
maximizeButtonProps?: object | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extended Modal interface that includes configuration for responsive breakpoints.
|
|
||||||
*/
|
|
||||||
export default interface IVModal extends Partial<Omit<IModal, 'opened'>>, Partial<DialogProps> {
|
|
||||||
/**
|
|
||||||
* Breakpoints used to control responsive modal behavior.
|
|
||||||
* Compatible with PrimeVue Dialog breakpoints format.
|
|
||||||
*/
|
|
||||||
breakpoints?: DialogBreakpoints
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import ConfirmDialog from 'primevue/confirmdialog';
|
|
||||||
import type { ConfirmDialogProps } from 'primevue/confirmdialog';
|
|
||||||
import VButtonGroup from '../button/VButtonGroup.vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ConfirmDialogProps>(), {
|
|
||||||
group: '',
|
|
||||||
draggable: true,
|
|
||||||
breakpoints: undefined,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ConfirmDialog
|
|
||||||
:group="props.group"
|
|
||||||
:draggable="props.draggable"
|
|
||||||
:breakpoints="props.breakpoints"
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
<template #container="slotProps">
|
|
||||||
<div class="container">
|
|
||||||
<div
|
|
||||||
class='header'
|
|
||||||
:class="[styles['titles-H6-XXS']]"
|
|
||||||
:style="('danger' in slotProps.message.acceptProps) ? {color: 'var(--text-default-error)'} : {color: 'var(--text-default-warning)'}"
|
|
||||||
>
|
|
||||||
<i :class="slotProps.message.icon" style="font-weight:lighter;"></i>
|
|
||||||
<span style="width: 100%;">{{ slotProps.message.header }}</span>
|
|
||||||
</div>
|
|
||||||
<span>{{ slotProps.message.message }}</span>
|
|
||||||
<VButtonGroup
|
|
||||||
:title="slotProps.message.group"
|
|
||||||
inline-layout-when="always"
|
|
||||||
size="sm"
|
|
||||||
align="right"
|
|
||||||
:buttons="[
|
|
||||||
{
|
|
||||||
...slotProps.message.acceptProps,
|
|
||||||
onclick: slotProps.acceptCallback
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...slotProps.message.rejectProps,
|
|
||||||
onclick: slotProps.rejectCallback
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</ConfirmDialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.container{
|
|
||||||
padding: 1rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: var(--p-dialog-header-gap);
|
|
||||||
align-items: baseline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,137 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Dialog from 'primevue/dialog';
|
|
||||||
import type IVModal from './IVModal.type';
|
|
||||||
import { useId, computed, watch, ref } from 'vue';
|
|
||||||
import VButtonGroup from '../button/VButtonGroup.vue';
|
|
||||||
import VButton from '../button/VButton.vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVModal>(), {
|
|
||||||
modalId: () => useId(),
|
|
||||||
actions: () => [],
|
|
||||||
icon: undefined,
|
|
||||||
size: 'md',
|
|
||||||
closeButtonLabel: 'Fermer',
|
|
||||||
closeButtonTitle: 'Fermer la fenêtre modale',
|
|
||||||
title: undefined,
|
|
||||||
visible: false,
|
|
||||||
isAlert: false,
|
|
||||||
breakpoints: undefined,
|
|
||||||
modal: true,
|
|
||||||
dismissableMask: false,
|
|
||||||
blockScroll: true,
|
|
||||||
position: 'center',
|
|
||||||
maximizable: false,
|
|
||||||
draggable: true,
|
|
||||||
showHeader: true,
|
|
||||||
closeOnEscape: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const role = computed(() => {
|
|
||||||
return props.isAlert ? 'alertdialog' : 'dialog'
|
|
||||||
})
|
|
||||||
|
|
||||||
const modalSize = computed(() => {
|
|
||||||
switch (props.size) {
|
|
||||||
case 'lg':
|
|
||||||
return `width: 49.5rem;`
|
|
||||||
case 'md':
|
|
||||||
return `width: 36.75rem;`
|
|
||||||
case 'sm':
|
|
||||||
return `width: 24rem;`
|
|
||||||
default:
|
|
||||||
return `width: 36.75rem;`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'update:visible',
|
|
||||||
'hide',
|
|
||||||
'after-hide',
|
|
||||||
'show',
|
|
||||||
'maximize',
|
|
||||||
'unmaximize',
|
|
||||||
'dragstart',
|
|
||||||
'dragend'
|
|
||||||
]);
|
|
||||||
|
|
||||||
const localVisible = ref(props.visible);
|
|
||||||
watch(() => props.visible, (newVal) => {
|
|
||||||
if(localVisible.value !== newVal){
|
|
||||||
localVisible.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localVisible, (newVal) => {
|
|
||||||
if(props.visible !== newVal){
|
|
||||||
emit('update:visible', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Dialog
|
|
||||||
:header="props.title"
|
|
||||||
id="modal"
|
|
||||||
:modal="props.modal"
|
|
||||||
:dismissable-mask="props.dismissableMask"
|
|
||||||
:role="role"
|
|
||||||
:aria-model="true"
|
|
||||||
:aria-labelledby="`modal-${props.modalId}-dialog`"
|
|
||||||
ref="modal"
|
|
||||||
:style="modalSize"
|
|
||||||
:breakpoints="props.breakpoints"
|
|
||||||
:block-scroll="props.blockScroll"
|
|
||||||
:position="props.position"
|
|
||||||
:maximizable="props.maximizable"
|
|
||||||
:draggable="props.draggable"
|
|
||||||
:show-header="props.showHeader"
|
|
||||||
:close-on-escape="props.closeOnEscape"
|
|
||||||
v-model:visible="localVisible"
|
|
||||||
@update:visible="emit('update:visible', $event)"
|
|
||||||
@after-hide="emit('after-hide', $event)"
|
|
||||||
@dragend="emit('dragend', $event)"
|
|
||||||
@dragstart="emit('dragstart', $event)"
|
|
||||||
@hide="emit('hide', $event)"
|
|
||||||
@maximize="emit('maximize', $event)"
|
|
||||||
@unmaximize="emit('unmaximize', $event)"
|
|
||||||
@show="emit('show', $event)"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<slot name="header" v-if="props.icon !== undefined">
|
|
||||||
<span :class="[styles['titles-H6-XXS']]">
|
|
||||||
<i :class="props.icon"></i>
|
|
||||||
{{ props.title }}
|
|
||||||
</span>
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
<slot name="content"/>
|
|
||||||
<template #footer>
|
|
||||||
<slot name="footer">
|
|
||||||
<VButtonGroup
|
|
||||||
v-if="props.actions?.length"
|
|
||||||
inline-layout-when="always"
|
|
||||||
align="right"
|
|
||||||
:buttons="props.actions"
|
|
||||||
size="sm"
|
|
||||||
title="groupe de boutons"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
<template #closebutton="{closeCallback}">
|
|
||||||
<VButton
|
|
||||||
tertiary
|
|
||||||
noOutline
|
|
||||||
size="sm"
|
|
||||||
icon="ri-close-line"
|
|
||||||
iconRight
|
|
||||||
@click="closeCallback"
|
|
||||||
:label="closeButtonLabel"
|
|
||||||
:title="closeButtonTitle"
|
|
||||||
ref="closeBtn"
|
|
||||||
aria-controls="modal-1"
|
|
||||||
type="button"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
|
@ -1,30 +0,0 @@
|
||||||
/**
|
|
||||||
* Interface representing the properties of a ProgressBar component.
|
|
||||||
*/
|
|
||||||
export default interface IVProgressBar {
|
|
||||||
/**
|
|
||||||
* Whether to display the numeric value of the progress.
|
|
||||||
* If true, the value will be shown alongside the progress bar.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
showValue?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current progress value, typically between 0 and 100.
|
|
||||||
*/
|
|
||||||
value: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the progress bar will be in indeterminate mode,
|
|
||||||
* indicating ongoing activity without a specific completion percentage.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
indeterminate?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, renders a smaller version of the progress bar.
|
|
||||||
* Useful for compact UI layouts.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
small?: boolean;
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import ProgressBar from 'primevue/progressbar';
|
|
||||||
import type IVProgressBar from './IVProgressBar.type.js';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVProgressBar>(), {
|
|
||||||
value: 0,
|
|
||||||
showValue: false,
|
|
||||||
indeterminate: false,
|
|
||||||
small: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mode = computed(() => (props.indeterminate ? 'indeterminate' : 'determinate'));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ProgressBar
|
|
||||||
role="progressbar"
|
|
||||||
:mode="mode"
|
|
||||||
:value="props.value"
|
|
||||||
:show-value="props.showValue && !props.small"
|
|
||||||
:class="['p-progressbar', {small: props.small}]"
|
|
||||||
style="width: 100%;"
|
|
||||||
>
|
|
||||||
<slot/>
|
|
||||||
</ProgressBar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.p-progressbar.small {--p-progressbar-height: 0.5rem;}
|
|
||||||
</style>
|
|
|
@ -1,35 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import ScrollPanel from 'primevue/scrollpanel';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
|
||||||
step?: number
|
|
||||||
height: string
|
|
||||||
width?: string
|
|
||||||
}>(), {
|
|
||||||
step: 5,
|
|
||||||
width: '100%'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ScrollPanel
|
|
||||||
:step="props.step"
|
|
||||||
class="p-scrollpanel-content"
|
|
||||||
:style="{width: props.width, maxHeight: props.height}"
|
|
||||||
>
|
|
||||||
<slot/>
|
|
||||||
</ScrollPanel>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
*{
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-scrollpanel {
|
|
||||||
height: fit-content;
|
|
||||||
overflow-y: auto;
|
|
||||||
box-sizing: content-box;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,351 +0,0 @@
|
||||||
import type { HintedString } from '@primevue/core';
|
|
||||||
import type { VirtualScrollerProps } from 'primevue/virtualscroller';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the props for a custom VSelect component.
|
|
||||||
*/
|
|
||||||
export type option = string | number | null | undefined;
|
|
||||||
|
|
||||||
export interface ISelect {
|
|
||||||
/**
|
|
||||||
* Whether the select field is required.
|
|
||||||
*/
|
|
||||||
required?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the select field is disabled.
|
|
||||||
*/
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The unique ID for the select element.
|
|
||||||
*/
|
|
||||||
selectId?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name attribute for the select element.
|
|
||||||
*/
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional hint text displayed below the select field.
|
|
||||||
*/
|
|
||||||
hint?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The currently selected value.
|
|
||||||
*/
|
|
||||||
modelValue?: option;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The label displayed above the select field.
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of options to display in the dropdown.
|
|
||||||
* Can be an array of strings, numbers, or objects.
|
|
||||||
*/
|
|
||||||
options?: Array<
|
|
||||||
string | number | Record<string, unknown> | {
|
|
||||||
value: unknown;
|
|
||||||
text: string;
|
|
||||||
disabled: boolean;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines how to extract the label from an option.
|
|
||||||
* Can be a string key or a function.
|
|
||||||
*/
|
|
||||||
optionLabel?: string | ((option: unknown) => string);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines how to extract the value from an option.
|
|
||||||
* Can be a string key or a function.
|
|
||||||
*/
|
|
||||||
optionValue?: string | ((option: unknown) => unknown);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message displayed when the selection is valid.
|
|
||||||
*/
|
|
||||||
successMessage?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message displayed when the selection is invalid.
|
|
||||||
*/
|
|
||||||
errorMessage?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text displayed when no option is selected.
|
|
||||||
*/
|
|
||||||
defaultUnselectedText?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelectProps {
|
|
||||||
/**
|
|
||||||
* Value of the component.
|
|
||||||
*/
|
|
||||||
modelValue?: unknown;
|
|
||||||
/**
|
|
||||||
* The default value for the input when not controlled by `modelValue`.
|
|
||||||
*/
|
|
||||||
defaultValue?: unknown;
|
|
||||||
/**
|
|
||||||
* The name attribute for the element, typically used in form submissions.
|
|
||||||
*/
|
|
||||||
name?: string | undefined;
|
|
||||||
/**
|
|
||||||
* An array of select items to display as the available options.
|
|
||||||
*/
|
|
||||||
options?: unknown[];
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the label of an option.
|
|
||||||
*/
|
|
||||||
optionLabel?: string | ((data: unknown) => string) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the value of an option, defaults to the option itself when not defined.
|
|
||||||
*/
|
|
||||||
optionValue?: string | ((data: unknown) => unknown) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the disabled flag of an option, defaults to false when not defined.
|
|
||||||
*/
|
|
||||||
optionDisabled?: string | ((data: unknown) => boolean) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the label of an option group.
|
|
||||||
*/
|
|
||||||
optionGroupLabel?: string | ((data: unknown) => string) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function that refers to the children options of option group.
|
|
||||||
*/
|
|
||||||
optionGroupChildren?: string | ((data: unknown) => unknown[]) | undefined;
|
|
||||||
/**
|
|
||||||
* Height of the viewport, a scrollbar is defined if height of list exceeds this value.
|
|
||||||
* @defaultValue 14rem
|
|
||||||
*/
|
|
||||||
scrollHeight?: string | undefined;
|
|
||||||
/**
|
|
||||||
* When specified, displays a filter input at header.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
filter?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Placeholder text to show when filter input is empty.
|
|
||||||
*/
|
|
||||||
filterPlaceholder?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Locale to use in filtering. The default locale is the host environment's current locale.
|
|
||||||
*/
|
|
||||||
filterLocale?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Defines the filtering algorithm to use when searching the options.
|
|
||||||
* @defaultValue contains
|
|
||||||
*/
|
|
||||||
filterMatchMode?: HintedString<'contains' | 'startsWith' | 'endsWith'> | undefined;
|
|
||||||
/**
|
|
||||||
* Fields used when filtering the options, defaults to optionLabel.
|
|
||||||
*/
|
|
||||||
filterFields?: string[] | undefined;
|
|
||||||
/**
|
|
||||||
* When present, custom value instead of predefined options can be entered using the editable input field.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
editable?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Default text to display when no option is selected.
|
|
||||||
*/
|
|
||||||
placeholder?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Defines the size of the component.
|
|
||||||
*/
|
|
||||||
size?: HintedString<'small' | 'large'> | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that the component should have invalid state style.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
invalid?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that the component should be disabled.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
disabled?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Specifies the input variant of the component.
|
|
||||||
* @defaultValue null
|
|
||||||
*/
|
|
||||||
variant?: HintedString<'outlined' | 'filled'> | undefined | null;
|
|
||||||
/**
|
|
||||||
* A property to uniquely identify an option.
|
|
||||||
*/
|
|
||||||
dataKey?: string | undefined;
|
|
||||||
/**
|
|
||||||
* When enabled, a clear icon is displayed to clear the value.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
showClear?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Spans 100% width of the container when enabled.
|
|
||||||
* @defaultValue null
|
|
||||||
*/
|
|
||||||
fluid?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* @deprecated since v4.0. Use 'labelId' instead.
|
|
||||||
* Identifier of the underlying input element.
|
|
||||||
*/
|
|
||||||
inputId?: string | undefined;
|
|
||||||
/**
|
|
||||||
* @deprecated since v4.0. Use 'labelStyle' instead.
|
|
||||||
* Inline style of the input field.
|
|
||||||
*/
|
|
||||||
inputStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* @deprecated since v4.0. Use 'labelClass' instead.
|
|
||||||
* Style class of the input field.
|
|
||||||
*/
|
|
||||||
inputClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* Identifier of the underlying label element.
|
|
||||||
*/
|
|
||||||
labelId?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the label field.
|
|
||||||
*/
|
|
||||||
labelStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the label field.
|
|
||||||
*/
|
|
||||||
labelClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* @deprecated since v4.0. Use 'overlayStyle' instead.
|
|
||||||
* Inline style of the overlay panel.
|
|
||||||
*/
|
|
||||||
panelStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* @deprecated since v4.0. Use 'overlayClass' instead.
|
|
||||||
* Style class of the overlay panel.
|
|
||||||
*/
|
|
||||||
panelClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the overlay.
|
|
||||||
*/
|
|
||||||
overlayStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the overlay.
|
|
||||||
*/
|
|
||||||
overlayClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* A valid query selector or an HTMLElement to specify where the overlay gets attached.
|
|
||||||
* @defaultValue body
|
|
||||||
*/
|
|
||||||
appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
|
|
||||||
/**
|
|
||||||
* Whether the select is in loading state.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
loading?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in clear button.
|
|
||||||
*/
|
|
||||||
clearIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in the select.
|
|
||||||
*/
|
|
||||||
dropdownIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in filter input.
|
|
||||||
*/
|
|
||||||
filterIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in loading state.
|
|
||||||
*/
|
|
||||||
loadingIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Clears the filter value when hiding the select.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
resetFilterOnHide?: boolean;
|
|
||||||
/**
|
|
||||||
* Clears the filter value when clicking on the clear icon.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
resetFilterOnClear?: boolean;
|
|
||||||
/**
|
|
||||||
* Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it.
|
|
||||||
*/
|
|
||||||
virtualScrollerOptions?: VirtualScrollerProps;
|
|
||||||
/**
|
|
||||||
* Whether to focus on the first visible or selected element when the overlay panel is shown.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
autoOptionFocus?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to focus on the filter element when the overlay panel is shown.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
autoFilterFocus?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When enabled, the focused option is selected.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
selectOnFocus?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When enabled, the focus is placed on the hovered option.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
focusOnHover?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether the selected option will be add highlight class.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
highlightOnSelect?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether the selected option will be shown with a check mark.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
checkmark?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue '{0} results are available'
|
|
||||||
*/
|
|
||||||
filterMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue '{0} items selected'
|
|
||||||
*/
|
|
||||||
selectionMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue No selected item
|
|
||||||
*/
|
|
||||||
emptySelectionMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue No results found
|
|
||||||
*/
|
|
||||||
emptyFilterMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to display when there are no options available. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue No available options
|
|
||||||
*/
|
|
||||||
emptyMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Index of the element in tabbing order.
|
|
||||||
*/
|
|
||||||
tabindex?: number | string | undefined;
|
|
||||||
/**
|
|
||||||
* Defines a string value that labels an interactive element.
|
|
||||||
*/
|
|
||||||
ariaLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Identifier of the underlying input element.
|
|
||||||
*/
|
|
||||||
ariaLabelledby?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Form control object, typically used for handling validation and form state.
|
|
||||||
*/
|
|
||||||
formControl?: Record<string, unknown> | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default interface IVSelect extends Partial<Omit<SelectProps, 'modelValue' | 'options'>>, Partial<ISelect> {
|
|
||||||
optionTemplate?: boolean
|
|
||||||
}
|
|
|
@ -1,218 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import Select, { type SelectSlots } from 'primevue/select';
|
|
||||||
import type IVSelect from './IVSelect.type.js';
|
|
||||||
import { useId, computed, watch, ref } from 'vue';
|
|
||||||
import VLabel from '../label/VLabel.vue';
|
|
||||||
import VHint from '../hint/VHint.vue';
|
|
||||||
import styles from '@visua/typography.module.css';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
inheritAttrs: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVSelect>(), {
|
|
||||||
hint: '',
|
|
||||||
successMessage: '',
|
|
||||||
errorMessage: '',
|
|
||||||
selectId: () => `select-${useId()}`,
|
|
||||||
label: '',
|
|
||||||
required: false,
|
|
||||||
disabled: false,
|
|
||||||
options: undefined,
|
|
||||||
name: '',
|
|
||||||
defaultUnselectedText: 'Sélectionner une option',
|
|
||||||
filter: false,
|
|
||||||
editable: false,
|
|
||||||
optionLabel: undefined,
|
|
||||||
optionValue: undefined,
|
|
||||||
filterMessage: 'Les résultats sont disponibles',
|
|
||||||
selectionMessage: 'Elements sélectionnés',
|
|
||||||
emptySelectionMessage: 'Aucun élément sélectionné',
|
|
||||||
emptyFilterMessage: 'Aucun résultat trouvé',
|
|
||||||
emptyMessage: 'Aucune option disponible'
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'update:modelValue',
|
|
||||||
'value-change',
|
|
||||||
'change',
|
|
||||||
'focus',
|
|
||||||
'blur',
|
|
||||||
'before-show',
|
|
||||||
'before-hide',
|
|
||||||
'show',
|
|
||||||
'hide',
|
|
||||||
'filter',
|
|
||||||
])
|
|
||||||
|
|
||||||
const localModelValue = ref(props.modelValue);
|
|
||||||
watch(() => props.modelValue, (newVal) => {
|
|
||||||
if(localModelValue.value !== newVal){
|
|
||||||
localModelValue.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localModelValue, (newVal) => {
|
|
||||||
if(props.modelValue !== newVal){
|
|
||||||
emit('update:modelValue', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const message = computed(() => {
|
|
||||||
return props.errorMessage || props.successMessage
|
|
||||||
})
|
|
||||||
const messageState = computed(() => {
|
|
||||||
return props.errorMessage ? 'error' : 'valid'
|
|
||||||
})
|
|
||||||
|
|
||||||
const typeMessage = computed(() => {
|
|
||||||
if (props.errorMessage) return 'alert';
|
|
||||||
else if (props.successMessage) return 'success';
|
|
||||||
else return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
const labelState = computed(() => {
|
|
||||||
if(!props.errorMessage && !props.successMessage && !props.disabled) return 'default';
|
|
||||||
else if(!props.errorMessage && props.successMessage && !props.disabled) return 'success';
|
|
||||||
else if(props.errorMessage && !props.successMessage && !props.disabled) return 'error';
|
|
||||||
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>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="main-container">
|
|
||||||
<VLabel
|
|
||||||
v-if="props.label"
|
|
||||||
:for="props.selectId"
|
|
||||||
:label="props.label"
|
|
||||||
:required="props.required && !props.disabled"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:type="labelState"
|
|
||||||
:hint="props.hint"
|
|
||||||
>
|
|
||||||
<template #required-type v-if="props.required">
|
|
||||||
<slot name="required"/>
|
|
||||||
</template>
|
|
||||||
</VLabel>
|
|
||||||
<Select
|
|
||||||
:name="props.name || props.selectId"
|
|
||||||
:options="props.options"
|
|
||||||
:optionLabel="props.optionLabel"
|
|
||||||
:optionValue="props.optionValue"
|
|
||||||
:disabled="props.disabled"
|
|
||||||
:aria-disabled="props.disabled"
|
|
||||||
:filter="props.filter"
|
|
||||||
:editable="props.editable"
|
|
||||||
:placeholder="props.placeholder"
|
|
||||||
:filter-message="props.filterMessage"
|
|
||||||
:selection-message="props.selectionMessage"
|
|
||||||
:empty-selection-message="props.emptySelectionMessage"
|
|
||||||
:empty-filter-message="props.emptyFilterMessage"
|
|
||||||
:empty-message="props.emptyMessage"
|
|
||||||
:show-clear="props.showClear"
|
|
||||||
:filter-fields="props.filterFields"
|
|
||||||
:filter-icon="props.filterIcon"
|
|
||||||
:filter-locale="props.filterLocale"
|
|
||||||
:filter-match-mode="props.filterMatchMode"
|
|
||||||
:filter-placeholder="props.filterPlaceholder"
|
|
||||||
:scroll-height="props.scrollHeight"
|
|
||||||
:data-key="props.dataKey"
|
|
||||||
:loading="props.loading"
|
|
||||||
v-bind="$attrs"
|
|
||||||
v-model:model-value="localModelValue"
|
|
||||||
@update:model-value="emit('update:modelValue', $event)"
|
|
||||||
@change="emit('change', $event)"
|
|
||||||
@blur="emit('blur', $event)"
|
|
||||||
@focus="emit('focus', $event)"
|
|
||||||
@value-change="emit('value-change', $event)"
|
|
||||||
@before-hide="emit('before-hide', $event)"
|
|
||||||
@before-show="emit('before-show', $event)"
|
|
||||||
@show="emit('show', $event)"
|
|
||||||
@hide="emit('hide', $event)"
|
|
||||||
@filter="emit('filter', $event)"
|
|
||||||
class="p-select"
|
|
||||||
:class="[
|
|
||||||
styles['text-body-MD-standard-text-Regular'],
|
|
||||||
{
|
|
||||||
'disabled': props.disabled,
|
|
||||||
'error': props.errorMessage && !props.successMessage && !props.disabled,
|
|
||||||
'success': !props.errorMessage && props.successMessage && !props.disabled,
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
v-for="([name]) in availableSlots"
|
|
||||||
:key="name"
|
|
||||||
v-slot:[name]="slotProps"
|
|
||||||
>
|
|
||||||
<slot :name="name" v-bind="slotProps" />
|
|
||||||
</template>
|
|
||||||
</Select>
|
|
||||||
<div
|
|
||||||
:id="`select-${messageState}-desc-${messageState}`"
|
|
||||||
role="alert"
|
|
||||||
aria-live="assertive"
|
|
||||||
>
|
|
||||||
<VHint :title="message" :type="typeMessage" icon/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.main-container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-select{
|
|
||||||
width: 100%;
|
|
||||||
height: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-select.disabled{
|
|
||||||
--p-select-border-color: var(--border-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-border-color: var(--border-plain-error);
|
|
||||||
--p-select-hover-border-color: var(--border-plain-error);
|
|
||||||
--p-select-focus-border-color: var(--border-plain-error);
|
|
||||||
}
|
|
||||||
.p-select.success{
|
|
||||||
--p-select-border-color: var(--border-plain-success);
|
|
||||||
--p-select-hover-border-color: var(--border-plain-success);
|
|
||||||
--p-select-focus-border-color: var(--border-plain-success);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,446 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import DataTable from 'primevue/datatable';
|
|
||||||
import type { DataTableProps, DataTableSlots } from 'primevue/datatable';
|
|
||||||
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'>>{
|
|
||||||
id?: string
|
|
||||||
title?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IVDataTable>(), {
|
|
||||||
id: () => useId(),
|
|
||||||
title: '',
|
|
||||||
selection: undefined,
|
|
||||||
paginator: false,
|
|
||||||
rowsPerPageOptions: undefined,
|
|
||||||
rows: 0,
|
|
||||||
value: undefined,
|
|
||||||
dataKey: undefined,
|
|
||||||
showGridlines: false,
|
|
||||||
stripedRows: false,
|
|
||||||
first: 0,
|
|
||||||
totalRecords: 0,
|
|
||||||
pageLinkSize: 5,
|
|
||||||
paginatorPosition: 'bottom',
|
|
||||||
paginatorTemplate: undefined,
|
|
||||||
alwaysShowPaginator:true,
|
|
||||||
currentPageReportTemplate: '({currentPage} de {totalPages})',
|
|
||||||
lazy: false,
|
|
||||||
loading: false,
|
|
||||||
sortField: undefined,
|
|
||||||
sortOrder: undefined,
|
|
||||||
nullSortOrder: 1,
|
|
||||||
defaultSortOrder: 1,
|
|
||||||
sortMode: 'single',
|
|
||||||
removableSort: false,
|
|
||||||
filters: undefined,
|
|
||||||
filterDisplay: undefined,
|
|
||||||
globalFilterFields: undefined,
|
|
||||||
filterLocale: undefined,
|
|
||||||
selectionMode: undefined,
|
|
||||||
compareSelectionBy: 'deepEquals',
|
|
||||||
metaKeySelection: false,
|
|
||||||
contextMenu: false,
|
|
||||||
contextMenuSelection: undefined,
|
|
||||||
selectAll: undefined,
|
|
||||||
rowHover: false,
|
|
||||||
csvSeparator: ',',
|
|
||||||
exportFilename: 'download',
|
|
||||||
exportFunction: undefined,
|
|
||||||
resizableColumns: false,
|
|
||||||
columnResizeMode: 'fit',
|
|
||||||
reorderableColumns: false,
|
|
||||||
expandedRows: undefined,
|
|
||||||
expandedRowIcon: undefined,
|
|
||||||
collapsedRowIcon: undefined,
|
|
||||||
rowGroupMode: undefined,
|
|
||||||
groupRowsBy: undefined,
|
|
||||||
expandableRowGroups: false,
|
|
||||||
expandedRowGroups: undefined,
|
|
||||||
stateStorage: 'session',
|
|
||||||
stateKey: undefined,
|
|
||||||
editMode: undefined,
|
|
||||||
editingRows: undefined,
|
|
||||||
rowClass: undefined,
|
|
||||||
rowStyle: undefined,
|
|
||||||
scrollable: false,
|
|
||||||
scrollHeight: undefined,
|
|
||||||
virtualScrollerOptions: undefined,
|
|
||||||
frozenValue: undefined,
|
|
||||||
breakpoint: '960px',
|
|
||||||
showHeaders: true,
|
|
||||||
highlightOnSelect: false,
|
|
||||||
size: undefined,
|
|
||||||
tableClass: undefined,
|
|
||||||
tableProps: undefined,
|
|
||||||
tableStyle: undefined,
|
|
||||||
filterButtonProps: undefined,
|
|
||||||
editButtonProps: undefined,
|
|
||||||
multiSortMeta: undefined,
|
|
||||||
})
|
|
||||||
|
|
||||||
type VDataTableSlots = DataTableSlots & {
|
|
||||||
default?: (props: Record<string, unknown>) => unknown
|
|
||||||
};
|
|
||||||
|
|
||||||
const slots = defineSlots<VDataTableSlots>();
|
|
||||||
|
|
||||||
|
|
||||||
const dataTableSlotKeys = [
|
|
||||||
'header',
|
|
||||||
'footer',
|
|
||||||
'empty',
|
|
||||||
'groupheader',
|
|
||||||
'groupfooter',
|
|
||||||
'loading',
|
|
||||||
'expansion',
|
|
||||||
'loadingicon',
|
|
||||||
'rowreorderindicatorupicon',
|
|
||||||
'rowreorderindicatordownicon',
|
|
||||||
'rowgrouptogglericon',
|
|
||||||
'paginatorcontainer',
|
|
||||||
'paginatorstart',
|
|
||||||
'paginatorend',
|
|
||||||
'paginatorfirstpagelinkicon',
|
|
||||||
'paginatorprevpagelinkicon',
|
|
||||||
'paginatornextpagelinkicon',
|
|
||||||
'paginatorlastpagelinkicon',
|
|
||||||
'paginatorrowsperpagedropdownicon',
|
|
||||||
'paginatorjumptopagedropdownicon'
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
const availableSlots = computed(() =>
|
|
||||||
dataTableSlotKeys.filter((key) => !!slots[key]).map((key) => [key, slots[key]])
|
|
||||||
);
|
|
||||||
|
|
||||||
const dataTableRef = ref();
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
exportCSV: () => dataTableRef.value.exportCSV()
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'update:selection',
|
|
||||||
'update:rows',
|
|
||||||
'update:sortField',
|
|
||||||
'update:sortOrder',
|
|
||||||
'update:multiSortMeta',
|
|
||||||
'update:contextMenuSelection',
|
|
||||||
'update:expandedRows',
|
|
||||||
'update:expandedRowGroups',
|
|
||||||
'update:filters',
|
|
||||||
'update:editingRows',
|
|
||||||
'update:first',
|
|
||||||
'page',
|
|
||||||
'sort',
|
|
||||||
'row-select',
|
|
||||||
'row-unselect',
|
|
||||||
'filter',
|
|
||||||
'value-change',
|
|
||||||
'row-click',
|
|
||||||
'row-dbclick',
|
|
||||||
'row-contextmenu',
|
|
||||||
'row-select-all',
|
|
||||||
'row-unselect-all',
|
|
||||||
'select-all-change',
|
|
||||||
'column-resize-end',
|
|
||||||
'column-reorder',
|
|
||||||
'row-reorder',
|
|
||||||
'row-expand',
|
|
||||||
'row-collapse',
|
|
||||||
'rowgroup-expand',
|
|
||||||
'rowgroup-collapse',
|
|
||||||
'cell-edit-init',
|
|
||||||
'cell-edit-complete',
|
|
||||||
'cell-edit-cancel',
|
|
||||||
'row-edit-init',
|
|
||||||
'row-edit-save',
|
|
||||||
'row-edit-cancel',
|
|
||||||
'state-restore',
|
|
||||||
'state-save',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const localSelection = ref(props.selection);
|
|
||||||
watch(() => props.selection, (newVal) => {
|
|
||||||
if(localSelection.value !== newVal) {
|
|
||||||
localSelection.value = newVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
watch(localSelection, (newVal) => {
|
|
||||||
if(props.selection !== newVal){
|
|
||||||
emit('update:selection', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localRows = ref(props.rows);
|
|
||||||
watch(() => props.rows, (newVal) => {
|
|
||||||
if(localRows.value !== newVal){
|
|
||||||
localRows.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localRows, (newVal) => {
|
|
||||||
if(props.rows !== newVal){
|
|
||||||
emit('update:rows', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localFirst = ref(props.first);
|
|
||||||
watch(() => props.first, (newVal) => {
|
|
||||||
if(localFirst.value !== newVal){
|
|
||||||
localFirst.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localFirst, (newVal) => {
|
|
||||||
if(props.first !== newVal){
|
|
||||||
emit('update:first', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localSortField = ref(props.sortField);
|
|
||||||
watch(() => props.sortField, (newVal) => {
|
|
||||||
if(localSortField.value !== newVal){
|
|
||||||
localSortField.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localSortField, (newVal) => {
|
|
||||||
if(props.sortField !== newVal){
|
|
||||||
emit('update:sortField', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localSortOrder = ref(props.sortOrder);
|
|
||||||
watch(() => props.sortOrder, (newVal) => {
|
|
||||||
if(localSortOrder.value !== newVal){
|
|
||||||
localSortOrder.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localSortOrder, (newVal) => {
|
|
||||||
if(props.sortOrder !== newVal){
|
|
||||||
emit('update:sortOrder', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localMultiSortMeta = ref(props.multiSortMeta);
|
|
||||||
watch(() => props.multiSortMeta, (newVal) => {
|
|
||||||
if(localMultiSortMeta.value !== newVal){
|
|
||||||
localMultiSortMeta.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localMultiSortMeta, (newVal) => {
|
|
||||||
if(props.multiSortMeta !== newVal){
|
|
||||||
emit('update:multiSortMeta', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localContextMenuSelection = ref(props.contextMenuSelection);
|
|
||||||
watch(() => props.contextMenuSelection, (newVal) => {
|
|
||||||
if(localContextMenuSelection.value !== newVal){
|
|
||||||
localContextMenuSelection.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localContextMenuSelection, (newVal) => {
|
|
||||||
if(props.contextMenuSelection){
|
|
||||||
emit('update:contextMenuSelection', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localExpandedRows = ref(props.expandedRows);
|
|
||||||
watch(() => props.expandedRows, (newVal) => {
|
|
||||||
if(localExpandedRows.value !== newVal){
|
|
||||||
localExpandedRows.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localExpandedRows, (newVal) => {
|
|
||||||
if(props.expandedRows !== newVal){
|
|
||||||
emit('update:expandedRows', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localExpandedRowGroups = ref(props.expandedRowGroups);
|
|
||||||
watch(() => props.expandedRowGroups, (newVal) => {
|
|
||||||
if(localExpandedRowGroups.value !== newVal){
|
|
||||||
localExpandedRowGroups.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localExpandedRowGroups, (newVal) => {
|
|
||||||
if(props.expandedRowGroups !== newVal){
|
|
||||||
emit('update:expandedRowGroups', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localFilters = ref(props.filters);
|
|
||||||
watch(() => props.filters, (newVal) => {
|
|
||||||
if(localFilters.value !== newVal){
|
|
||||||
localFilters.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localFilters, (newVal) => {
|
|
||||||
if(props.filters !== newVal){
|
|
||||||
emit('update:filters', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const localEditingRows = ref(props.editingRows);
|
|
||||||
watch(() => props.editingRows, (newVal) => {
|
|
||||||
if(localEditingRows.value = newVal){
|
|
||||||
localEditingRows.value = newVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(localEditingRows, (newVal) => {
|
|
||||||
if(props.editingRows !== newVal){
|
|
||||||
emit('update:editingRows', newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<DataTable
|
|
||||||
:id="`datatable-${props.id}`"
|
|
||||||
role="table"
|
|
||||||
ref="dataTableRef"
|
|
||||||
:value="props.value"
|
|
||||||
:paginator="props.paginator"
|
|
||||||
:rowsPerPageOptions="props.rowsPerPageOptions"
|
|
||||||
:data-key="props.dataKey"
|
|
||||||
:show-gridlines="props.showGridlines"
|
|
||||||
:striped-rows="props.stripedRows"
|
|
||||||
:total-records="props.totalRecords"
|
|
||||||
:page-link-size="props.pageLinkSize"
|
|
||||||
:paginator-position="props.paginatorPosition"
|
|
||||||
:paginator-template="props.paginatorTemplate"
|
|
||||||
:always-show-paginator="props.alwaysShowPaginator"
|
|
||||||
:current-page-report-template="props.currentPageReportTemplate"
|
|
||||||
:lazy="props.lazy"
|
|
||||||
:loading="props.loading"
|
|
||||||
:nullSortOrder="props.nullSortOrder"
|
|
||||||
:defaultSortOrder="props.defaultSortOrder"
|
|
||||||
:sortMode="props.sortMode"
|
|
||||||
:removableSort="props.removableSort"
|
|
||||||
:filterDisplay="props.filterDisplay"
|
|
||||||
:globalFilterFields="props.globalFilterFields"
|
|
||||||
:filterLocale="props.filterLocale"
|
|
||||||
:selectionMode="props.selectionMode"
|
|
||||||
:compareSelectionBy="props.compareSelectionBy"
|
|
||||||
:metaKeySelection="props.metaKeySelection"
|
|
||||||
:contextMenu="props.contextMenu"
|
|
||||||
:selectAll="props.selectAll"
|
|
||||||
:rowHover="props.rowHover"
|
|
||||||
:csvSeparator="props.csvSeparator"
|
|
||||||
:exportFilename="props.exportFilename"
|
|
||||||
:exportFunction="props.exportFunction"
|
|
||||||
:resizableColumns="props.resizableColumns"
|
|
||||||
:columnResizeMode="props.columnResizeMode"
|
|
||||||
:reorderableColumns="props.reorderableColumns"
|
|
||||||
:expandedRowIcon="props.expandedRowIcon"
|
|
||||||
:collapsedRowIcon="props.collapsedRowIcon"
|
|
||||||
:rowGroupMode="props.rowGroupMode"
|
|
||||||
:groupRowsBy="props.groupRowsBy"
|
|
||||||
:expandableRowGroups="props.expandableRowGroups"
|
|
||||||
:stateStorage="props.stateStorage"
|
|
||||||
:stateKey="props.stateKey"
|
|
||||||
:editMode="props.editMode"
|
|
||||||
:rowClass="props.rowClass"
|
|
||||||
:rowStyle="props.rowStyle"
|
|
||||||
:scrollable="props.scrollable"
|
|
||||||
:scrollHeight="props.scrollHeight"
|
|
||||||
:virtualScrollerOptions="props.virtualScrollerOptions"
|
|
||||||
:frozenValue="props.frozenValue"
|
|
||||||
:breakpoint="props.breakpoint"
|
|
||||||
:showHeaders="props.showHeaders"
|
|
||||||
:highlightOnSelect="props.highlightOnSelect"
|
|
||||||
:size="props.size"
|
|
||||||
:tableClass="props.tableClass"
|
|
||||||
:tableProps="props.tableProps"
|
|
||||||
:tableStyle="props.tableStyle"
|
|
||||||
:filterButtonProps="props.filterButtonProps"
|
|
||||||
:editButtonProps="props.editButtonProps"
|
|
||||||
style="width: 100%; height: fit-content;"
|
|
||||||
v-model:selection="localSelection"
|
|
||||||
v-model:rows="localRows"
|
|
||||||
v-model:first="localFirst"
|
|
||||||
v-model:sortField="localSortField"
|
|
||||||
v-model:sortOrder="localSortOrder"
|
|
||||||
v-model:filters="localFilters"
|
|
||||||
v-model:contextMenuSelection="localContextMenuSelection"
|
|
||||||
v-model:expandedRows="localExpandedRows"
|
|
||||||
v-model:expandedRowGroups="localExpandedRowGroups"
|
|
||||||
v-model:editingRows="localEditingRows"
|
|
||||||
v-model:multi-sort-meta="localMultiSortMeta"
|
|
||||||
@update:selection="emit('update:selection', $event)"
|
|
||||||
@update:context-menu-selection="emit('update:contextMenuSelection', $event)"
|
|
||||||
@update:editing-rows="emit('update:editingRows', $event)"
|
|
||||||
@update:expanded-row-groups="emit('update:expandedRowGroups', $event)"
|
|
||||||
@update:filters="emit('update:filters', $event)"
|
|
||||||
@update:first="emit('update:first', $event)"
|
|
||||||
@update:multi-sort-meta="emit('update:multiSortMeta', $event)"
|
|
||||||
@update:rows="emit('update:rows', $event)"
|
|
||||||
@update:sort-field="emit('update:sortField', $event)"
|
|
||||||
@update:sort-order="emit('update:sortOrder', $event)"
|
|
||||||
@page="emit('page', $event)"
|
|
||||||
@sort="emit('sort', $event)"
|
|
||||||
@rowSelect="emit('row-select', $event)"
|
|
||||||
@rowUnselect="emit('row-unselect', $event)"
|
|
||||||
@filter="emit('filter', $event)"
|
|
||||||
@valueChange="emit('value-change', $event)"
|
|
||||||
@rowClick="emit('row-click', $event)"
|
|
||||||
@rowCollapse="emit('row-collapse', $event)"
|
|
||||||
@rowContextmenu="emit('row-contextmenu', $event)"
|
|
||||||
@rowDblclick="emit('row-dbclick', $event)"
|
|
||||||
@rowEditCancel="emit('row-edit-cancel', $event)"
|
|
||||||
@rowEditInit="emit('row-edit-init', $event)"
|
|
||||||
@rowEditSave="emit('row-edit-save', $event)"
|
|
||||||
@rowExpand="emit('row-expand', $event)"
|
|
||||||
@rowReorder="emit('row-reorder', $event)"
|
|
||||||
@rowSelectAll="emit('row-select-all', $event)"
|
|
||||||
@rowUnselectAll="emit('row-unselect-all', $event)"
|
|
||||||
@rowgroupCollapse="emit('rowgroup-collapse', $event)"
|
|
||||||
@rowgroupExpand="emit('rowgroup-expand', $event)"
|
|
||||||
@columnReorder="emit('column-reorder', $event)"
|
|
||||||
@columnResizeEnd="emit('column-resize-end', $event)"
|
|
||||||
@cellEditCancel="emit('cell-edit-cancel', $event)"
|
|
||||||
@cellEditComplete="emit('cell-edit-complete', $event)"
|
|
||||||
@cellEditInit="emit('cell-edit-init', $event)"
|
|
||||||
@stateRestore="emit('state-restore', $event)"
|
|
||||||
@stateSave="emit('state-save', $event)"
|
|
||||||
@selectAllChange="emit('select-all-change', $event)"
|
|
||||||
class="p-datatable"
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
v-for="([name]) in availableSlots"
|
|
||||||
:key="name"
|
|
||||||
v-slot:[name]="slotProps"
|
|
||||||
>
|
|
||||||
<slot :name="name" v-bind="slotProps" />
|
|
||||||
</template>
|
|
||||||
<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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.datatable-empty{
|
|
||||||
height: fit-content;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
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>
|
|
5
src/env.d.ts
vendored
5
src/env.d.ts
vendored
|
@ -1,5 +0,0 @@
|
||||||
declare module '*.vue' {
|
|
||||||
import { DefineComponent } from 'vue';
|
|
||||||
const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>;
|
|
||||||
export default component;
|
|
||||||
}
|
|
121
src/index.ts
121
src/index.ts
|
@ -1,121 +0,0 @@
|
||||||
/**
|
|
||||||
* Visua Design System Vue Component Library
|
|
||||||
*
|
|
||||||
* This file serves as the entry point for the library, exporting components, utilities,
|
|
||||||
* and styles that make up the Visua Vue ecosystem.
|
|
||||||
*
|
|
||||||
* @module visua-vue
|
|
||||||
*/
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// Component Exports //
|
|
||||||
//////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accordion components
|
|
||||||
*/
|
|
||||||
export { default as VAccordion } from './components/accordion/VAccordion.vue';
|
|
||||||
export { default as VAccordionChild } from './components/accordion/VAccordionChild.vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alert and notification components
|
|
||||||
*/
|
|
||||||
export { default as VAlert } from './components/alert/VAlert.vue';
|
|
||||||
export { default as VBadge } from './components/badge/VBadge.vue';
|
|
||||||
export { default as VMessage } from './components/message/VMessage.vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button and link components
|
|
||||||
*/
|
|
||||||
export { default as VButton } from './components/button/VButton.vue';
|
|
||||||
export { default as VButtonGroup } from './components/button/VButtonGroup.vue';
|
|
||||||
export { default as VLink } from './components/button/VLink.vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Form input and control components
|
|
||||||
*/
|
|
||||||
export { default as VCheckbox } from './components/checkbox/VCheckbox.vue';
|
|
||||||
export { default as VInput } from './components/input/VInput.vue';
|
|
||||||
export { default as VSelect } from './components/select/VSelect.vue';
|
|
||||||
export { default as VFile } from './components/file/VFile.vue';
|
|
||||||
export { default as VFileUpload } from './components/file/VFileUpload.vue';
|
|
||||||
export { default as VLabelErrorProxy } from './components/file/VLabelErrorProxy.vue';
|
|
||||||
export { default as VHint } from './components/hint/VHint.vue';
|
|
||||||
export { default as VLabel } from './components/label/VLabel.vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Layout and grouping components
|
|
||||||
*/
|
|
||||||
export { default as VDivider } from './components/divider/VDivider.vue';
|
|
||||||
export { default as VGroup } from './components/group/VGroup.vue';
|
|
||||||
export { default as VScrollPanel } from './components/scrollpanel/VScrollPanel.vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigation and menu components
|
|
||||||
*/
|
|
||||||
export { default as VMenuBar } from './components/menu/VMenuBar.vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modal and overlay components
|
|
||||||
*/
|
|
||||||
export { default as VModal } from './components/modal/VModal.vue';
|
|
||||||
export { default as VConfirmModal } from './components/modal/VConfirmModal.vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visual feedback components
|
|
||||||
*/
|
|
||||||
export { default as VProgressBar } from './components/progressbar/VProgressBar.vue';
|
|
||||||
export { default as VDataTable } from './components/table/VDataTable.vue';
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// Composables & Interfaces //
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Composable functions (hooks)
|
|
||||||
*/
|
|
||||||
export * from './components/composable/index.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom TypeScript interfaces
|
|
||||||
*/
|
|
||||||
export * from './components/interface/index.js';
|
|
||||||
|
|
||||||
//////////////////////
|
|
||||||
// Global Styles //
|
|
||||||
//////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Global and layout styles
|
|
||||||
*/
|
|
||||||
import './assets/style/global.css';
|
|
||||||
import './assets/style/primevue-configuration.css';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PrimeVue component-specific style overrides
|
|
||||||
*/
|
|
||||||
import './assets/style/primevue-style/accordion.css';
|
|
||||||
import './assets/style/primevue-style/button.css';
|
|
||||||
import './assets/style/primevue-style/checkbox.css';
|
|
||||||
import './assets/style/primevue-style/confirmdialog.css';
|
|
||||||
import './assets/style/primevue-style/datatable.css';
|
|
||||||
import './assets/style/primevue-style/dialog.css';
|
|
||||||
import './assets/style/primevue-style/divider.css';
|
|
||||||
import './assets/style/primevue-style/fileupload.css';
|
|
||||||
import './assets/style/primevue-style/form.css';
|
|
||||||
import './assets/style/primevue-style/iconfield.css';
|
|
||||||
import './assets/style/primevue-style/input.css';
|
|
||||||
import './assets/style/primevue-style/list.css';
|
|
||||||
import './assets/style/primevue-style/menubar.css';
|
|
||||||
import './assets/style/primevue-style/message.css';
|
|
||||||
import './assets/style/primevue-style/navigation.css';
|
|
||||||
import './assets/style/primevue-style/overlay.css';
|
|
||||||
import './assets/style/primevue-style/paginator.css';
|
|
||||||
import './assets/style/primevue-style/password.css';
|
|
||||||
import './assets/style/primevue-style/progressbar.css';
|
|
||||||
import './assets/style/primevue-style/scrollpanel.css';
|
|
||||||
import './assets/style/primevue-style/select.css';
|
|
||||||
import './assets/style/primevue-style/tag.css';
|
|
||||||
import './assets/style/primevue-style/textarea.css';
|
|
||||||
import './assets/style/primevue-style/toast.css';
|
|
||||||
import './assets/style/primevue-style/various.css';
|
|
|
@ -2,15 +2,9 @@ import './assets/main.css'
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import primeVue from 'primevue/config'
|
import primeVue from 'primevue/config'
|
||||||
import ToastService from 'primevue/toastservice'
|
|
||||||
import ConfirmationService from 'primevue/confirmationservice'
|
|
||||||
// import router from '../template/router'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(primeVue)
|
app.use(primeVue)
|
||||||
app.use(ToastService)
|
|
||||||
app.use(ConfirmationService)
|
|
||||||
// app.use(router)
|
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
import { describe, test, expect } from 'vitest'
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import { h } from 'vue'
|
|
||||||
import VAccordion from '../src/components/accordion/VAccordion.vue'
|
|
||||||
import VAccordionChild from '../src/components/accordion/VAccordionChild.vue'
|
|
||||||
|
|
||||||
describe('VAccordion', () => {
|
|
||||||
const factory = () =>
|
|
||||||
mount(VAccordion, {
|
|
||||||
props: {
|
|
||||||
value: '0',
|
|
||||||
},
|
|
||||||
slots: {
|
|
||||||
default: () => [
|
|
||||||
h(VAccordionChild, { panelValue: '0' }, {
|
|
||||||
header: () => 'Un titre d\'accordéon 1',
|
|
||||||
content: () => h('p', 'Contenu 1'),
|
|
||||||
}),
|
|
||||||
h(VAccordionChild, { panelValue: '1', disabled: true }, {
|
|
||||||
header: () => 'Un titre d\'accordéon 2',
|
|
||||||
content: () => h('p', 'Contenu 2'),
|
|
||||||
}),
|
|
||||||
h(VAccordionChild, { panelValue: '2' }, {
|
|
||||||
header: () => 'Un titre d\'accordéon 3',
|
|
||||||
content: () => h('p', 'Contenu 3'),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
global: {
|
|
||||||
components: {
|
|
||||||
VAccordionChild,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
test('renders three accordion panels', () => {
|
|
||||||
const wrapper = factory()
|
|
||||||
const panels = wrapper.findAll('.p-accordionpanel')
|
|
||||||
expect(panels).toHaveLength(3)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('activates the first panel by default', () => {
|
|
||||||
const wrapper = factory()
|
|
||||||
const firstPanel = wrapper.findAll('.p-accordionpanel')[0]
|
|
||||||
expect(firstPanel.classes()).toContain('p-accordionpanel-active')
|
|
||||||
expect(firstPanel.attributes('data-p-active')).toBe('true')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('disables the second panel', () => {
|
|
||||||
const wrapper = factory()
|
|
||||||
const secondPanel = wrapper.findAll('.p-accordionpanel')[1]
|
|
||||||
expect(secondPanel.classes()).toContain('p-disabled')
|
|
||||||
expect(secondPanel.attributes('data-p-disabled')).toBe('true')
|
|
||||||
expect(secondPanel.find('button').attributes('disabled')).toBeDefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('does not emit update:value when a disabled panel is clicked', async () => {
|
|
||||||
const wrapper = mount(VAccordion, {
|
|
||||||
props: {
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
slots: {
|
|
||||||
default: () => [
|
|
||||||
h(VAccordionChild, { panelValue: '0', disabled: true }, {
|
|
||||||
header: () => 'Panneau désactivé',
|
|
||||||
content: () => h('p', 'Contenu'),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
global: {
|
|
||||||
components: {
|
|
||||||
VAccordionChild,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const header = wrapper.find('.p-accordionheader')
|
|
||||||
expect(header.attributes('disabled')).toBeDefined()
|
|
||||||
await header.trigger('click')
|
|
||||||
expect(wrapper.emitted('update:value')).toBeFalsy()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('emits update:value when a panel is clicked', async () => {
|
|
||||||
const wrapper = mount(VAccordion, {
|
|
||||||
props: {
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
slots: {
|
|
||||||
default: `
|
|
||||||
<VAccordionChild panel-value="0">
|
|
||||||
<template #header>Panel 1</template>
|
|
||||||
<template #content><p>Contenu 1</p></template>
|
|
||||||
</VAccordionChild>
|
|
||||||
<VAccordionChild panel-value="1">
|
|
||||||
<template #header>Panel 2</template>
|
|
||||||
<template #content><p>Contenu 2</p></template>
|
|
||||||
</VAccordionChild>
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
global: {
|
|
||||||
components: {
|
|
||||||
VAccordionChild,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const headers = wrapper.findAll('.p-accordionheader')
|
|
||||||
await headers[1].trigger('click')
|
|
||||||
|
|
||||||
// Checks that the event has been issued with the correct value
|
|
||||||
expect(wrapper.emitted('update:value')).toBeTruthy()
|
|
||||||
expect(wrapper.emitted('update:value')![0]).toEqual(['1'])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toggles an active panel when clicked again', async () => {
|
|
||||||
const wrapper = mount(VAccordion, {
|
|
||||||
props: {
|
|
||||||
value: '0',
|
|
||||||
},
|
|
||||||
slots: {
|
|
||||||
default: () => [
|
|
||||||
h(VAccordionChild, { panelValue: '0' }, {
|
|
||||||
header: () => 'Panneau 1',
|
|
||||||
content: () => h('p', 'Contenu 1'),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
global: {
|
|
||||||
components: {
|
|
||||||
VAccordionChild,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const header = wrapper.find('.p-accordionheader')
|
|
||||||
|
|
||||||
// Vérifie que le panneau est actif au départ
|
|
||||||
const panel = wrapper.find('.p-accordionpanel')
|
|
||||||
expect(panel.attributes('data-p-active')).toBe('true')
|
|
||||||
|
|
||||||
// Clique une première fois pour le refermer
|
|
||||||
await header.trigger('click')
|
|
||||||
|
|
||||||
// Vérifie que le panneau est maintenant inactif
|
|
||||||
expect(panel.attributes('data-p-active')).toBe('false')
|
|
||||||
|
|
||||||
// Vérifie que l'événement a bien été émis avec `null` ou une valeur vide
|
|
||||||
const emitted = wrapper.emitted('update:value')
|
|
||||||
expect(emitted).toBeTruthy()
|
|
||||||
const lastEmission = emitted![emitted!.length - 1]
|
|
||||||
expect(lastEmission[0]).toBe(null)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { mount, flushPromises } from '@vue/test-utils';
|
|
||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
||||||
import VAlert from '../src/components/alert/VAlert.vue';
|
|
||||||
import Toast from 'primevue/toast';
|
|
||||||
import ToastService from 'primevue/toastservice';
|
|
||||||
|
|
||||||
vi.mock('primevue/toastservice', () => ({
|
|
||||||
default: {
|
|
||||||
install: () => {},
|
|
||||||
add: vi.fn()
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('VAlert.vue', () => {
|
|
||||||
let wrapper: ReturnType<typeof mount>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.id = 'app';
|
|
||||||
document.body.appendChild(container);
|
|
||||||
|
|
||||||
wrapper = mount(VAlert, {
|
|
||||||
attachTo: '#app',
|
|
||||||
global: {
|
|
||||||
components: { Toast },
|
|
||||||
plugins: [ToastService]
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
position: 'top-right'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 💡 Helper pour injecter un toast et attendre le rendu
|
|
||||||
const renderToast = async (payload: { severity: string; summary: string; detail: string; life: number }) => {
|
|
||||||
wrapper.vm.$toast?.add(payload);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
|
||||||
await flushPromises();
|
|
||||||
};
|
|
||||||
|
|
||||||
it('renders the Toast container via teleport', async () => {
|
|
||||||
await renderToast({
|
|
||||||
severity: 'success',
|
|
||||||
summary: 'Test Title',
|
|
||||||
detail: 'Test description',
|
|
||||||
life: 3000
|
|
||||||
});
|
|
||||||
|
|
||||||
const toast = document.querySelector('.p-toast');
|
|
||||||
expect(toast).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('responds to "close" and "life-end" events', async () => {
|
|
||||||
await wrapper.vm.$emit('close', { id: 'test-alert' });
|
|
||||||
await wrapper.vm.$emit('life-end', { id: 'test-alert' });
|
|
||||||
|
|
||||||
expect(wrapper.emitted()).toHaveProperty('close');
|
|
||||||
expect(wrapper.emitted()).toHaveProperty('life-end');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,98 +0,0 @@
|
||||||
import { describe, it, expect } from 'vitest'
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import VCheckbox from '../src/components/checkbox/VCheckbox.vue'
|
|
||||||
import PrimeCheckbox from 'primevue/checkbox'
|
|
||||||
|
|
||||||
const globalConfig = {
|
|
||||||
global: {
|
|
||||||
components: {
|
|
||||||
Checkbox: PrimeCheckbox
|
|
||||||
},
|
|
||||||
mocks: {
|
|
||||||
$primevue: {
|
|
||||||
config: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stubs: {
|
|
||||||
VLabel: true,
|
|
||||||
VHint: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('VCheckbox.vue', () => {
|
|
||||||
it('adds value to modelValue array when checkbox is clicked (multiple mode)', async () => {
|
|
||||||
const wrapper = mount(VCheckbox, {
|
|
||||||
props: {
|
|
||||||
label: 'Fruit',
|
|
||||||
modelValue: [],
|
|
||||||
value: 'pomme'
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
|
|
||||||
const input = wrapper.find('input[type="checkbox"]');
|
|
||||||
await input.setValue(true);
|
|
||||||
|
|
||||||
const emitted = wrapper.emitted('update:modelValue');
|
|
||||||
expect(emitted).toBeTruthy();
|
|
||||||
expect(emitted?.[0]?.[0]).toContain('pomme');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes value from modelValue array when checkbox is unchecked (multiple mode)', async () => {
|
|
||||||
const wrapper = mount(VCheckbox, {
|
|
||||||
props: {
|
|
||||||
label: 'Fruit',
|
|
||||||
modelValue: ['pomme'],
|
|
||||||
value: 'pomme'
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
|
|
||||||
const input = wrapper.find('input[type="checkbox"]');
|
|
||||||
await input.setValue(false);
|
|
||||||
|
|
||||||
const emitted = wrapper.emitted('update:modelValue');
|
|
||||||
expect(emitted).toBeTruthy();
|
|
||||||
expect(emitted?.[0]?.[0]).not.toContain('pomme');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('emits change, focus and blur events', async () => {
|
|
||||||
const wrapper = mount(VCheckbox, {
|
|
||||||
props: {
|
|
||||||
label: 'Test Checkbox',
|
|
||||||
modelValue: false
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
const input = wrapper.find('input[type="checkbox"]');
|
|
||||||
await input.trigger('focus');
|
|
||||||
await input.trigger('blur');
|
|
||||||
await input.trigger('change');
|
|
||||||
expect(wrapper.emitted('focus')).toBeTruthy();
|
|
||||||
expect(wrapper.emitted('blur')).toBeTruthy();
|
|
||||||
expect(wrapper.emitted('change')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('applies error and success classes based on props', () => {
|
|
||||||
const errorWrapper = mount(VCheckbox, {
|
|
||||||
props: {
|
|
||||||
label: 'Error Checkbox',
|
|
||||||
modelValue: false,
|
|
||||||
errorMessage: 'Error occurred'
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
expect(errorWrapper.find('.p-checkbox.error').exists()).toBe(true);
|
|
||||||
|
|
||||||
const successWrapper = mount(VCheckbox, {
|
|
||||||
props: {
|
|
||||||
label: 'Success Checkbox',
|
|
||||||
modelValue: true,
|
|
||||||
validMessage: 'Valid input'
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
expect(successWrapper.find('.p-checkbox.success').exists()).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { mount } from '@vue/test-utils';
|
|
||||||
import { describe, it, expect, vi } from 'vitest';
|
|
||||||
import VFileUpload from '../src/components/file/VFileUpload.vue'
|
|
||||||
|
|
||||||
describe('VFileUpload emits', () => {
|
|
||||||
it('doit émettre les événements personnalisés', async () => {
|
|
||||||
// Création des mocks
|
|
||||||
const onSelect = vi.fn();
|
|
||||||
const onUpload = vi.fn();
|
|
||||||
const onError = vi.fn();
|
|
||||||
const onRemove = vi.fn();
|
|
||||||
const onClear = vi.fn();
|
|
||||||
|
|
||||||
const wrapper = mount(VFileUpload, {
|
|
||||||
props: {
|
|
||||||
advanced: true,
|
|
||||||
onSelect,
|
|
||||||
onUpload,
|
|
||||||
onError,
|
|
||||||
onRemove,
|
|
||||||
onClear
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Simule une sélection de fichier
|
|
||||||
await wrapper.vm.$emit('select', { files: [{ name: 'test.png' }] });
|
|
||||||
expect(onSelect).toHaveBeenCalledWith({ files: [{ name: 'test.png' }] });
|
|
||||||
|
|
||||||
// Simule une erreur
|
|
||||||
await wrapper.vm.$emit('error', { message: 'Erreur test' });
|
|
||||||
expect(onError).toHaveBeenCalledWith({ message: 'Erreur test' });
|
|
||||||
|
|
||||||
// Simule un upload
|
|
||||||
await wrapper.vm.$emit('upload', { files: ['fichier1'] });
|
|
||||||
expect(onUpload).toHaveBeenCalledWith({ files: ['fichier1'] });
|
|
||||||
|
|
||||||
// Simule une suppression
|
|
||||||
await wrapper.vm.$emit('remove', { file: 'test.png' });
|
|
||||||
expect(onRemove).toHaveBeenCalledWith({ file: 'test.png' });
|
|
||||||
|
|
||||||
// Simule un clear
|
|
||||||
await wrapper.vm.$emit('clear');
|
|
||||||
expect(onClear).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { describe, test, expect } from 'vitest'
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import VGroup from '../src/components/group/VGroup.vue'
|
|
||||||
|
|
||||||
describe('VGroup', () => {
|
|
||||||
test('renders default slot content', () => {
|
|
||||||
const wrapper = mount(VGroup, {
|
|
||||||
props: {type: undefined},
|
|
||||||
slots: {
|
|
||||||
default: '<span>Test Content</span>',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expect(wrapper.html()).toContain('Test Content')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('applies default class when no type is specified', () => {
|
|
||||||
const wrapper = mount(VGroup)
|
|
||||||
expect(wrapper.classes()).toContain('container')
|
|
||||||
expect(wrapper.classes()).not.toContain('error')
|
|
||||||
expect(wrapper.classes()).not.toContain('success')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('applies "error" class when type is "error"', () => {
|
|
||||||
const wrapper = mount(VGroup, {
|
|
||||||
props: { type: 'error' },
|
|
||||||
})
|
|
||||||
expect(wrapper.classes()).toContain('error')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('applies "success" class when type is "success"', () => {
|
|
||||||
const wrapper = mount(VGroup, {
|
|
||||||
props: { type: 'success' },
|
|
||||||
})
|
|
||||||
expect(wrapper.classes()).toContain('success')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('applies "disabled" class when disabled is true', () => {
|
|
||||||
const wrapper = mount(VGroup, {
|
|
||||||
props: { disabled: true, type: undefined },
|
|
||||||
})
|
|
||||||
expect(wrapper.classes()).toContain('disabled')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('does not apply "disabled" class when disabled is false', () => {
|
|
||||||
const wrapper = mount(VGroup, {
|
|
||||||
props: { disabled: false, type: undefined },
|
|
||||||
})
|
|
||||||
expect(wrapper.classes()).not.toContain('disabled')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,190 +0,0 @@
|
||||||
import { describe, it, expect } from 'vitest'
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import VInput from '../src/components/input/VInput.vue'
|
|
||||||
|
|
||||||
const globalConfig = {
|
|
||||||
global: {
|
|
||||||
mocks: {
|
|
||||||
$primevue: {
|
|
||||||
config: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stubs: {
|
|
||||||
VHint: true,
|
|
||||||
VDivider: true,
|
|
||||||
Password: true,
|
|
||||||
Textarea: true,
|
|
||||||
VLabel: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const noStubVLabelConfig = {
|
|
||||||
...globalConfig.global,
|
|
||||||
stubs: {
|
|
||||||
...globalConfig.global.stubs,
|
|
||||||
VLabel: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const noStubTextareaConfig = {
|
|
||||||
...globalConfig.global,
|
|
||||||
stubs: {
|
|
||||||
...globalConfig.global.stubs,
|
|
||||||
Textarea: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const noStubPasswordConfig = {
|
|
||||||
...globalConfig.global,
|
|
||||||
stubs: {
|
|
||||||
...globalConfig.global.stubs,
|
|
||||||
Textarea: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('VInput.vue', () => {
|
|
||||||
it('renders InputText by default', () => {
|
|
||||||
const wrapper = mount(VInput, globalConfig)
|
|
||||||
expect(wrapper.find('input').exists()).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('renders VLabel when labelVisible is true and passes the label prop', () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
labelVisible: true,
|
|
||||||
label: 'Username'
|
|
||||||
},
|
|
||||||
global: noStubVLabelConfig
|
|
||||||
})
|
|
||||||
|
|
||||||
const labelComponent = wrapper.findComponent({ name: 'VLabel' })
|
|
||||||
expect(labelComponent.exists()).toBe(true)
|
|
||||||
expect(labelComponent.props('label')).toBe('Username')
|
|
||||||
expect(wrapper.text()).toContain('Username')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not render VLabel when labelVisible is false', () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
labelVisible: false,
|
|
||||||
label: 'Username'
|
|
||||||
},
|
|
||||||
global: noStubVLabelConfig
|
|
||||||
})
|
|
||||||
|
|
||||||
const labelComponent = wrapper.findComponent({ name: 'VLabel' })
|
|
||||||
expect(labelComponent.exists()).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test : update:modelValue from InputText
|
|
||||||
it('emits update:modelValue when input value changes', async () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
modelValue: ''
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
})
|
|
||||||
|
|
||||||
const input = wrapper.find('input')
|
|
||||||
await input.setValue('new value')
|
|
||||||
|
|
||||||
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
||||||
expect(wrapper.emitted('update:modelValue')![0]).toEqual(['new value'])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test : value-change from InputText
|
|
||||||
it('emits value-change when InputText emits valueChange', async () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
modelValue: ''
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
})
|
|
||||||
|
|
||||||
const inputText = wrapper.findComponent({ name: 'InputText' })
|
|
||||||
inputText.vm.$emit('valueChange', 'changed')
|
|
||||||
|
|
||||||
await wrapper.vm.$nextTick()
|
|
||||||
|
|
||||||
expect(wrapper.emitted('value-change')).toBeTruthy()
|
|
||||||
expect(wrapper.emitted('value-change')![0]).toEqual(['changed'])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test : value-change from Textarea
|
|
||||||
it('emits value-change when Textarea emits valueChange', async () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
isTextarea: true,
|
|
||||||
modelValue: ''
|
|
||||||
},
|
|
||||||
global: noStubTextareaConfig
|
|
||||||
})
|
|
||||||
|
|
||||||
const textarea = wrapper.findComponent({ name: 'Textarea' })
|
|
||||||
textarea.vm.$emit('valueChange', 'textarea changed')
|
|
||||||
|
|
||||||
await wrapper.vm.$nextTick()
|
|
||||||
|
|
||||||
expect(wrapper.emitted('value-change')).toBeTruthy()
|
|
||||||
expect(wrapper.emitted('value-change')![0]).toEqual(['textarea changed'])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test : value-change from Password
|
|
||||||
it('emits value-change when Password emits valueChange', async () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
isPassword: true,
|
|
||||||
modelValue: ''
|
|
||||||
},
|
|
||||||
global: noStubPasswordConfig
|
|
||||||
})
|
|
||||||
|
|
||||||
const password = wrapper.findComponent({ name: 'Password' })
|
|
||||||
password.vm.$emit('valueChange', 'password changed')
|
|
||||||
|
|
||||||
await wrapper.vm.$nextTick()
|
|
||||||
|
|
||||||
expect(wrapper.emitted('value-change')).toBeTruthy()
|
|
||||||
expect(wrapper.emitted('value-change')![0]).toEqual(['password changed'])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test : update:modelValue from Password
|
|
||||||
it('emits update:modelValue when Password value changes', async () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
isPassword: true,
|
|
||||||
modelValue: ''
|
|
||||||
},
|
|
||||||
global: noStubPasswordConfig
|
|
||||||
})
|
|
||||||
|
|
||||||
const password = wrapper.findComponent({ name: 'Password' })
|
|
||||||
password.vm.$emit('update:modelValue', 'new-password')
|
|
||||||
|
|
||||||
await wrapper.vm.$nextTick()
|
|
||||||
|
|
||||||
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
||||||
expect(wrapper.emitted('update:modelValue')![0]).toEqual(['new-password'])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test : change from Password
|
|
||||||
it('emits change when Password emits change', async () => {
|
|
||||||
const wrapper = mount(VInput, {
|
|
||||||
props: {
|
|
||||||
isPassword: true,
|
|
||||||
modelValue: ''
|
|
||||||
},
|
|
||||||
global: noStubPasswordConfig
|
|
||||||
})
|
|
||||||
|
|
||||||
const password = wrapper.findComponent({ name: 'Password' })
|
|
||||||
password.vm.$emit('change', 'password-changed')
|
|
||||||
|
|
||||||
await wrapper.vm.$nextTick()
|
|
||||||
|
|
||||||
expect(wrapper.emitted('change')).toBeTruthy()
|
|
||||||
expect(wrapper.emitted('change')![0]).toEqual(['password-changed'])
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
|
@ -1,58 +0,0 @@
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import VLink from '../src/components/button/VLink.vue'
|
|
||||||
import {test, expect, describe, vi} from 'vitest'
|
|
||||||
|
|
||||||
describe('VLink', () => {
|
|
||||||
test('renders the label correctly', () => {
|
|
||||||
const wrapper = mount(VLink, {
|
|
||||||
props: {
|
|
||||||
label: 'Link'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
expect(wrapper.text()).toContain('Link');
|
|
||||||
})
|
|
||||||
|
|
||||||
test('renders as an anchor tag when `href` is provided', () => {
|
|
||||||
const wrapper = mount(VLink, {
|
|
||||||
props: {label: 'External', href: 'https://example.com' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const a = wrapper.find('a')
|
|
||||||
expect(a.exists()).toBe(true);
|
|
||||||
expect(a.attributes('href')).toBe('https://example.com');
|
|
||||||
})
|
|
||||||
|
|
||||||
test('disables the link when `disabled` is true', () => {
|
|
||||||
const wrapper = mount(VLink, {
|
|
||||||
props: {label: 'Disabled', disabled: true }
|
|
||||||
})
|
|
||||||
|
|
||||||
const button = wrapper.find('.p-button');
|
|
||||||
expect(button.classes()).toContain('disabled');
|
|
||||||
expect(button.attributes('aria-disabled')).toBe('true');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('prevents click when disabled', async () => {
|
|
||||||
const clickHandler = vi.fn();
|
|
||||||
const wrapper = mount(VLink, {
|
|
||||||
props: {label: 'Disabled', disabled: true, onClick: clickHandler}
|
|
||||||
})
|
|
||||||
|
|
||||||
await wrapper.trigger('click')
|
|
||||||
expect(clickHandler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('places icon on the right when `iconRight` is true', () => {
|
|
||||||
const wrapper = mount(VLink, {
|
|
||||||
props: {
|
|
||||||
label: 'link',
|
|
||||||
icon: "ri-external-link-line",
|
|
||||||
iconRight: true,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const icon = wrapper.find('.p-button-icon-right');
|
|
||||||
expect(icon.exists()).toBe(true);
|
|
||||||
expect(icon.classes()).toContain('ri-external-link-line');
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
import { describe, it, expect } from 'vitest'
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import VMessage from '../src/components/message/VMessage.vue'
|
|
||||||
|
|
||||||
describe('VMessage.vue', () => {
|
|
||||||
it('renders the title', () => {
|
|
||||||
const wrapper = mount(VMessage, {
|
|
||||||
props: { title: 'Test Message' }
|
|
||||||
})
|
|
||||||
expect(wrapper.text()).toContain('Test Message')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('shows the correct icon class for type "alert"', () => {
|
|
||||||
const wrapper = mount(VMessage, {
|
|
||||||
props: { title: 'Alert Message', type: 'alert' }
|
|
||||||
})
|
|
||||||
const icon = wrapper.find('i')
|
|
||||||
expect(icon.classes()).toContain('ri-spam-fill')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('shows the close button if closable is true', () => {
|
|
||||||
const wrapper = mount(VMessage, {
|
|
||||||
props: { title: 'Closable Message', closable: true }
|
|
||||||
})
|
|
||||||
const button = wrapper.find('button[aria-label="Fermer"]')
|
|
||||||
expect(button.exists()).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('emits close event when close button is clicked', async () => {
|
|
||||||
const wrapper = mount(VMessage, {
|
|
||||||
props: { title: 'Closable Message', closable: true }
|
|
||||||
})
|
|
||||||
const button = wrapper.find('button[aria-label="Fermer"]')
|
|
||||||
await button.trigger('click')
|
|
||||||
expect(wrapper.emitted()).toHaveProperty('close')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,69 +0,0 @@
|
||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
||||||
import { mount } from '@vue/test-utils';
|
|
||||||
import { nextTick } from 'vue';
|
|
||||||
import VModal from '../src/components/modal/VModal.vue';
|
|
||||||
import type IVButton from '../src/components/button/IVButton.type';
|
|
||||||
|
|
||||||
describe('VModal.vue', () => {
|
|
||||||
let actions: IVButton[];
|
|
||||||
let visible: boolean;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
visible = true;
|
|
||||||
actions = [
|
|
||||||
{
|
|
||||||
label: 'Bouton primaire',
|
|
||||||
onClick: vi.fn(),
|
|
||||||
title: 'primaire',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Bouton secondaire',
|
|
||||||
secondary: true,
|
|
||||||
onClick: vi.fn(),
|
|
||||||
title: 'secondaire',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Bouton tertiaire',
|
|
||||||
tertiary: true,
|
|
||||||
onClick: vi.fn(),
|
|
||||||
title: 'tertiaire',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('emits update:visible when internal visibility changes', async () => {
|
|
||||||
const wrapper = mount(VModal, {
|
|
||||||
props: {
|
|
||||||
visible: true,
|
|
||||||
actions,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Simulation d’un changement interne
|
|
||||||
await wrapper.vm.$emit('update:visible', false);
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
const emitted = wrapper.emitted('update:visible');
|
|
||||||
expect(emitted).toBeTruthy();
|
|
||||||
expect(emitted?.[0]).toEqual([false]);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('emits lifecycle events (show/hide)', async () => {
|
|
||||||
const wrapper = mount(VModal, {
|
|
||||||
props: {
|
|
||||||
visible,
|
|
||||||
actions,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
wrapper.vm.$emit('show');
|
|
||||||
wrapper.vm.$emit('hide');
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(wrapper.emitted('show')).toBeTruthy();
|
|
||||||
expect(wrapper.emitted('hide')).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import { mount } from '@vue/test-utils';
|
|
||||||
import VProgressBar from '../src/components/progressbar/VProgressBar.vue';
|
|
||||||
|
|
||||||
describe('VProgressBar', () => {
|
|
||||||
it('renders with default props', () => {
|
|
||||||
const wrapper = mount(VProgressBar);
|
|
||||||
const progressBar = wrapper.findComponent({ name: 'ProgressBar' });
|
|
||||||
|
|
||||||
expect(progressBar.exists()).toBe(true);
|
|
||||||
expect(progressBar.props('value')).toBe(0);
|
|
||||||
expect(progressBar.props('mode')).toBe('determinate');
|
|
||||||
expect(progressBar.props('showValue')).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders with custom value', () => {
|
|
||||||
const wrapper = mount(VProgressBar, {
|
|
||||||
props: { value: 75 }
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.findComponent({ name: 'ProgressBar' }).props('value')).toBe(75);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders in indeterminate mode', () => {
|
|
||||||
const wrapper = mount(VProgressBar, {
|
|
||||||
props: { indeterminate: true, value: 75 }
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.findComponent({ name: 'ProgressBar' }).props('mode')).toBe('indeterminate');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('hides value when small is true even if showValue is true', () => {
|
|
||||||
const wrapper = mount(VProgressBar, {
|
|
||||||
props: { showValue: true, small: true, value: 25 }
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.findComponent({ name: 'ProgressBar' }).props('showValue')).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('applies small class when small is true', () => {
|
|
||||||
const wrapper = mount(VProgressBar, {
|
|
||||||
props: { small: true, value: 15 }
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(wrapper.find('.p-progressbar').classes()).toContain('small');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders slot content', () => {
|
|
||||||
const wrapper = mount(VProgressBar, {
|
|
||||||
props: { value: 43, showValue: true },
|
|
||||||
slots: {
|
|
||||||
default: 'Valeur: 43/100'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(wrapper.html()).toContain('Valeur: 43/100');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,101 +0,0 @@
|
||||||
import { describe, it, expect, vi } from 'vitest';
|
|
||||||
import { mount } from '@vue/test-utils';
|
|
||||||
import VSelect from '../src/components/select/VSelect.vue';
|
|
||||||
import Select from 'primevue/select';
|
|
||||||
|
|
||||||
// Mock global matchMedia
|
|
||||||
Object.defineProperty(window, 'matchMedia', {
|
|
||||||
writable: true,
|
|
||||||
value: vi.fn().mockImplementation((query) => ({
|
|
||||||
matches: false,
|
|
||||||
media: query,
|
|
||||||
onchange: null,
|
|
||||||
addListener: vi.fn(), // deprecated
|
|
||||||
removeListener: vi.fn(), // deprecated
|
|
||||||
addEventListener: vi.fn(),
|
|
||||||
removeEventListener: vi.fn(),
|
|
||||||
dispatchEvent: vi.fn(),
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const globalConfig = {
|
|
||||||
global: {
|
|
||||||
components: {
|
|
||||||
Checkbox: Select
|
|
||||||
},
|
|
||||||
mocks: {
|
|
||||||
$primevue: {
|
|
||||||
config: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stubs: {
|
|
||||||
VLabel: false,
|
|
||||||
VHint: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('VSelect.vue', () => {
|
|
||||||
const options = [
|
|
||||||
{ value: 'apple', text: 'Apple' },
|
|
||||||
{ value: 'banana', text: 'Banana' },
|
|
||||||
{ value: 'cherry', text: 'Cherry' }
|
|
||||||
];
|
|
||||||
|
|
||||||
it('renders with basic props', () => {
|
|
||||||
const wrapper = mount(VSelect, {
|
|
||||||
props: {
|
|
||||||
label: 'Fruits',
|
|
||||||
options,
|
|
||||||
modelValue: null
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
|
|
||||||
const label = wrapper.findComponent({ name: 'VLabel' });
|
|
||||||
expect(label.exists()).toBe(true);
|
|
||||||
expect(label.props('label')).toBe('Fruits');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('emits update:modelValue and change when value is selected', async () => {
|
|
||||||
const wrapper = mount(VSelect, {
|
|
||||||
props: {
|
|
||||||
options,
|
|
||||||
modelValue: null
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
|
|
||||||
// Simule la sélection d'une option
|
|
||||||
wrapper.vm.$emit('update:modelValue', 'banana');
|
|
||||||
wrapper.vm.$emit('change', { value: 'banana' });
|
|
||||||
|
|
||||||
const emittedUpdate = wrapper.emitted('update:modelValue');
|
|
||||||
const emittedChange = wrapper.emitted('change');
|
|
||||||
|
|
||||||
expect(emittedUpdate).toBeTruthy();
|
|
||||||
expect(emittedUpdate![0]).toEqual(['banana']);
|
|
||||||
|
|
||||||
expect(emittedChange).toBeTruthy();
|
|
||||||
expect(emittedChange![0]).toEqual([{ value: 'banana' }]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reacts to external modelValue changes', async () => {
|
|
||||||
const wrapper = mount(VSelect, {
|
|
||||||
props: {
|
|
||||||
options,
|
|
||||||
modelValue: 'apple'
|
|
||||||
},
|
|
||||||
global: globalConfig.global
|
|
||||||
});
|
|
||||||
|
|
||||||
await wrapper.setProps({ modelValue: 'cherry' });
|
|
||||||
expect(wrapper.emitted('update:modelValue')).toBeFalsy();
|
|
||||||
|
|
||||||
const select = wrapper.findComponent({ name: 'Select' });
|
|
||||||
expect(select.props('modelValue')).toBe('cherry');
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,13 +1,10 @@
|
||||||
{
|
{
|
||||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "test/VButton.spec.ts"],
|
||||||
"exclude": ["src/**/__tests__/*", "src/App.vue", "src/main.ts"],
|
"exclude": ["src/**/__tests__/*"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"declaration": true,
|
|
||||||
"emitDeclarationOnly": true,
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
"lib": ["es2022", "dom"],
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"],
|
||||||
"@visua/*": ["./node_modules/@cellule-financiere-pmo/visua/output/*"]
|
"@visua/*": ["./node_modules/@cellule-financiere-pmo/visua/output/*"]
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
|
||||||
"types": ["vitest"],
|
|
||||||
"emitDeclarationOnly": true,
|
|
||||||
"declaration": true,
|
|
||||||
"outDir": "dist"
|
|
||||||
},
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
|
||||||
"path": "./tsconfig.app.json"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.node.json"
|
"path": "./tsconfig.node.json"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.types.json"
|
"path": "./tsconfig.app.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["vitest"]
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"declaration": true,
|
|
||||||
"emitDeclarationOnly": true,
|
|
||||||
"outDir": "./dist",
|
|
||||||
"baseUrl": ".",
|
|
||||||
"module": "NodeNext",
|
|
||||||
"target": "ESNext",
|
|
||||||
"moduleResolution": "nodenext",
|
|
||||||
"strict": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"allowJs": true,
|
|
||||||
"lib": ["es2022", "dom"],
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["src/*"],
|
|
||||||
"@visua/*": ["node_modules/@cellule-financiere-pmo/visua/output/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["src", "src/**/*.vue", "env.d.ts"],
|
|
||||||
"exclude": ["src/**/__tests__/*", "src/App.vue", "src/main.ts"]
|
|
||||||
}
|
|
|
@ -1,38 +1,24 @@
|
||||||
// vite.config.ts
|
|
||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vitest/config'
|
||||||
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'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
vueDevTools()
|
vueDevTools(),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
'@visua': path.resolve(__dirname, './node_modules/@cellule-financiere-pmo/visua/output'),
|
'@visua': path.resolve(__dirname, './node_modules/@cellule-financiere-pmo/visua/output')
|
||||||
},
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry: path.resolve(__dirname, 'src/index.ts'),
|
|
||||||
name: 'VisuaVue',
|
|
||||||
fileName: (format) => format === 'es' ? 'visua-vue.es.js' : 'visua-vue.umd.cjs',
|
|
||||||
formats: ['es', 'umd'],
|
|
||||||
},
|
|
||||||
rollupOptions: {
|
|
||||||
external: ['vue', 'primevue', 'vue-router', 'primeicons'],
|
|
||||||
output: {
|
|
||||||
globals: {
|
|
||||||
vue: 'Vue',
|
|
||||||
primevue: 'PrimeVue',
|
|
||||||
'vue-router': 'VueRouter',
|
|
||||||
primeicons: 'primeicons'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'jsdom',
|
||||||
|
include: ['test/**/*.spec.ts'],
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
// vitest.config.ts
|
|
||||||
import { fileURLToPath, URL } from 'node:url';
|
|
||||||
import { defineConfig } from 'vitest/config';
|
|
||||||
import path from 'path';
|
|
||||||
import vue from '@vitejs/plugin-vue';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [vue()],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
||||||
'@visua': path.resolve(__dirname, './node_modules/@cellule-financiere-pmo/visua/output'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
test: {
|
|
||||||
globals: true,
|
|
||||||
environment: 'jsdom',
|
|
||||||
include: ['test/**/*.spec.ts'],
|
|
||||||
},
|
|
||||||
});
|
|
4
vue-shim.d.ts
vendored
4
vue-shim.d.ts
vendored
|
@ -1,4 +0,0 @@
|
||||||
declare module '*.vue' {
|
|
||||||
const component: unknown;
|
|
||||||
export default component;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user