diff --git a/CHANGELOG.md b/CHANGELOG.md index 15f6f1e..d87b97f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.10] - 2025-07-27 +### Added +- File compoenent +- File upload component +- Scroll panel component + ## [1.0.9] - 2025-07-24 ### Added - Message component diff --git a/README.md b/README.md index 44e4ef0..86e0390 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # visua-vue -**Current version: v1.0.9** \ No newline at end of file +**Current version: v1.0.10** \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2cc8904..707beef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.9", + "version": "1.0.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.9", + "version": "1.0.10", "license": "ISC", "dependencies": { "@cellule-financiere-pmo/visua": "1.1.3", diff --git a/package.json b/package.json index 04b8ff0..e0a863f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cellule-financiere-pmo/visua-vue", - "version": "1.0.9", + "version": "1.0.10", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.vue b/src/App.vue index 61efa4d..1f2a29d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,10 +5,11 @@ // 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 VBadgeView from '../template/VBadgeView.vue'; +// import VSelectView from '../template/VSelectView.vue'; // import VProgressBarView from '../template/VProgressBarView.vue'; -import VMessageView from '../template/VMessageView.vue'; +// import VMessageView from '../template/VMessageView.vue'; +import VFileUploadView from '../template/VFileUploadView.vue' @@ -19,8 +20,9 @@ import VMessageView from '../template/VMessageView.vue'; - - + - + + diff --git a/src/assets/style/primevue-configuration.css b/src/assets/style/primevue-configuration.css index a92e270..8120b3b 100644 --- a/src/assets/style/primevue-configuration.css +++ b/src/assets/style/primevue-configuration.css @@ -13,3 +13,5 @@ @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'; diff --git a/src/assets/style/primevue-style/fileupload.css b/src/assets/style/primevue-style/fileupload.css new file mode 100644 index 0000000..a3dba2a --- /dev/null +++ b/src/assets/style/primevue-style/fileupload.css @@ -0,0 +1,24 @@ +: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(--p-primary-color); + --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-transparent); + --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); +} diff --git a/src/assets/style/primevue-style/scrollpanel.css b/src/assets/style/primevue-style/scrollpanel.css new file mode 100644 index 0000000..aa06f8c --- /dev/null +++ b/src/assets/style/primevue-style/scrollpanel.css @@ -0,0 +1,11 @@ +: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); +} diff --git a/src/components/file/IVFileUpload.type.ts b/src/components/file/IVFileUpload.type.ts new file mode 100644 index 0000000..707f95c --- /dev/null +++ b/src/components/file/IVFileUpload.type.ts @@ -0,0 +1,198 @@ +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>, + Partial> { + + /** + * If true, enables the advanced mode of the file upload component, + * which may include features like drag-and-drop, file previews, etc. + */ + advanced?: boolean; +} diff --git a/src/components/file/VFile.vue b/src/components/file/VFile.vue new file mode 100644 index 0000000..1a6fcff --- /dev/null +++ b/src/components/file/VFile.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/src/components/file/VFileUpload.vue b/src/components/file/VFileUpload.vue new file mode 100644 index 0000000..59f6ef5 --- /dev/null +++ b/src/components/file/VFileUpload.vue @@ -0,0 +1,362 @@ + + + + + diff --git a/src/components/file/VLabelErrorProxy.vue b/src/components/file/VLabelErrorProxy.vue new file mode 100644 index 0000000..1274c65 --- /dev/null +++ b/src/components/file/VLabelErrorProxy.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/components/scrollpanel/VScrollpanel.vue b/src/components/scrollpanel/VScrollpanel.vue new file mode 100644 index 0000000..49e6b9c --- /dev/null +++ b/src/components/scrollpanel/VScrollpanel.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/test/VFileUpload.spec.ts b/test/VFileUpload.spec.ts new file mode 100644 index 0000000..cd44ec4 --- /dev/null +++ b/test/VFileUpload.spec.ts @@ -0,0 +1,45 @@ +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(); + }); +});