UNPKG

vue-smart-ui

Version:

A collection of Vue 3 smart and highly customizable UI components for modern web applications

1,373 lines (1,104 loc) 51.5 kB
# Vue Components Library A library of reusable and highly customizable Vue components. ## 📱 Examples Visit our website to see examples for all components: [https://vuesmartui.edgarimai.com/](https://vuesmartui.edgarimai.com/) Here you will find: - Interactive demonstrations of each component - Available customization options ## 🚀 How to Use 1. Install the package ```bash npm install vue-smart-ui # or yarn add vue-smart-ui # or pnpm add vue-smart-ui ``` 2. Import the styles on main.js ```javascript import 'vue-smart-ui/style.css' ``` 3. Import the required components ```javascript import { BaseButton, BaseInput /* ... */ } from 'vue-smart-ui' ``` 4. Use the components in your templates ```vue <template> <BaseButton variant="primary"> My Button </BaseButton> </template> ``` ## ⚙️ General Features - 🎨 Consistent and modern design - 📱 Fully responsive - ♿ Accessible - 🎯 Slot support for maximum flexibility - 🌈 Theming system via CSS variables - 🔧 Highly customizable - ⚡ Optimized performance ## 📦 Available Components ### BaseButton A versatile and customizable button that supports different variants, sizes, and states. #### Props | Prop | Type | Default | Description | | -------- | ------- | --------- | ------------------------------------------------------------------------- | | id | String | - | Optional button ID | | type | String | 'button' | Button style. Options: 'button', 'submit', 'reset' | | variant | String | 'primary' | Button style. Options: 'primary', 'secondary', 'gray', 'outline', 'ghost' | | size | String | 'medium' | Button size. Options: 'small', 'medium', 'large', 'auto' | | block | Boolean | false | If true, button will take 100% of available width | | disabled | Boolean | false | Disables the button | | loading | Boolean | false | Shows a loading indicator | | iconOnly | Boolean | false | If true, button will be square with no padding | #### Slots - `default`: Main button content - `prefix`: Content before main text (e.g., icon) - `suffix`: Content after main text (e.g., icon) #### Events - `click`: Emitted when button is clicked #### Usage Example ```vue <BaseButton variant="primary" size="medium" :loading="false" @click="handleClick"> Click Here </BaseButton> ``` ### BaseAccordion and BaseAccordionItem Accordion system for displaying expandable content. #### BaseAccordion props | Prop | Type | Default | Description | | -------- | ------- | --------- | --------------------------------------------------------------- | | id | String | - | Optional accordion ID | | variant | String | 'default' | Accordion style. Options: 'default', 'bordered', 'minimal' | | multiple | Boolean | false | If true, allows multiple sections to be expanded simultaneously | #### BaseAccordionItem props - `title` (String): Required. The title text for the accordion item - `disabled` (Boolean): If true, disables the accordion item - Default: false #### Slots - `default`: Main content of the accordion item - `title`: Custom title content (overrides title prop) - `icon`: Icon content before the title - `chevron`: Custom chevron/arrow icon #### Usage Example ```vue <div class="accordion-container"> <h2>Accordion</h2> <BaseAccordion multiple variant="bordered"> <BaseAccordionItem title="Section 1"> <p>Section 1 content</p> </BaseAccordionItem> <BaseAccordionItem title="Section 2" disabled> Section 2 content </BaseAccordionItem> <BaseAccordionItem title="Section 3"> <template #title> <span class="custom-title">Custom title</span> </template> Section 3 content </BaseAccordionItem> </BaseAccordion> </div> ``` ### BaseInput Highly customizable input field supporting different input types, input masks (phone, currency, etc), and built-in validation rules. The BaseInput component provides a rich set of features for form inputs: - Works with all HTML input types (text, password, email, number, etc) - **Built-in Validation**: - Required fields - Email format - Min/max length - Pattern matching - Custom validation functions - Customizable error messages - Form-level validation support - **Input Masks**: - Phone numbers: Automatically formats as (00) 00000-0000 - Currency: Formats as currency with proper decimals (e.g. R$ 1.234,56) - Custom masks support - **Styling**: - Label customization - Error state styling - Disabled state - Read-only state - Focus state - Placeholder support #### BaseInput props | Prop | Type | Default | Description | | ----------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | modelValue | any | - | The v-model value | | label | String | - | Input label text | | type | String | 'text' | HTML input type | | placeholder | String | - | Input placeholder text | | disabled | Boolean | false | Disables the input | | readonly | Boolean | false | Makes input readonly | | rules | Array | - | Validation rules array. Each rule can be:<br>• String: 'required', 'email', 'min', 'max', 'pattern'<br>• Object: { required: true, message: 'Custom message' }<br>• Object: { min: number, message: 'Custom message' }<br>• Object: { max: number, message: 'Custom message' }<br>• Object: { pattern: RegExp, message: 'Custom message' } | #### Events - `@update:modelValue`: Emitted when input value changes - `@mounted`: Emitted when component is mounted, passes input ref - `@focus`: Emitted on input focus - `@blur`: Emitted on input blur #### Basic example ```vue <BaseInput v-model="name" label="Name" :rules="['required']" /> ``` #### Usage Example with multiple inputs and validation ```js const formRefs = ref({}) // Function to validate all inputs and show erros if has any const validateForm = () => { let isValid = true // Validate all registered inputs Object.values(formRefs.value).forEach((input) => { if (input?.validate && !input.validate()) { isValid = false } }) return isValid } const handleSubmit = () => { if (validateForm()) { console.log('Form submitted:', pageState.input) } else { console.log('Form has errors') } } const registerInput = (name, ref) => { console.log(name, ref) if (ref) { formRefs.value[name] = ref } } ``` ```vue <!-- Email validation --> <BaseInput v-model="input.email" label="Email" :rules="['required', 'email']" @mounted="(ref) => registerInput('email', ref)" /> <!-- Multiple rules with custom messages --> <BaseInput v-model="password" type="password" label="Password" :rules="[ { required: true, message: 'Password is required' }, { min: 8, message: 'Password must be at least 8 characters' }, ]" @mounted="(ref) => registerInput('password', ref)" /> <!-- Custom validator --> <BaseInput v-model="username" label="Username" :rules="[ { validator: (value) => /^[a-z0-9]+$/.test(value), message: 'Username can only contain lowercase letters and numbers', }, ]" /> <!-- Phone mask --> <BaseInput v-model="phone" label="Phone" :rules="['required', 'pattern: ^\\d{10}$']" @mounted="(ref) => registerInput('phone', ref)" mask="phone" placeholder="(00) 00000-0000" /> <!-- Currency mask --> <BaseInput v-model="price" label="Price" mask="currency" placeholder="R$ 0,00" /> ``` ### BaseTextarea Highly customizable textarea component that matches the BaseInput styling and functionality. Perfect for multi-line text input with support for validation, different variants, and auto-resize capability. Features: - Consistent styling with BaseInput - Auto-resize capability - Built-in validation - Different variants (default, filled, outlined) - Support for helper text and error states - Customizable through CSS variables - Flexible width control (block/inline) #### Props | Prop | Type | Default | Description | | --------------- | --------- | ------------ | ------------------------------------------------------------------ | | modelValue | `String` | `''` | The v-model value | | variant | `String` | `'default'` | Textarea style. Options: 'default', 'filled', 'outlined' | | label | `String` | `null` | Label text | | placeholder | `String` | `''` | Placeholder text | | rows | `Number` | `3` | Initial number of rows | | maxRows | `Number` | `null` | Maximum number of rows when using autoResize | | disabled | `Boolean` | `false` | Disables the textarea | | readonly | `Boolean` | `false` | Makes textarea readonly | | required | `Boolean` | `false` | Makes the field required | | helperText | `String` | `null` | Helper text displayed below the textarea | | errorMessage | `String` | `null` | Error message to display | | rules | `Array` | `[]` | Validation rules array | | validateOnBlur | `Boolean` | `true` | Trigger validation on blur | | validateOnInput | `Boolean` | `false` | Trigger validation on input | | block | `Boolean` | `false` | Makes the textarea full width | | resize | `String` | `'vertical'` | Resize behavior. Options: 'none', 'both', 'horizontal', 'vertical' | | autoResize | `Boolean` | `false` | Automatically adjust height based on content | #### Events - `@update:modelValue`: Emitted when textarea value changes - `@focus`: Emitted on textarea focus - `@blur`: Emitted on textarea blur - `@input`: Emitted on input - `@validation`: Emitted when validation occurs, includes validation status - `@mounted`: Emitted when component is mounted, passes textarea ref #### Basic Examples ```vue <!-- Simple textarea --> <BaseTextarea v-model="description" label="Description" placeholder="Enter your description" /> <!-- With validation --> <BaseTextarea v-model="comment" label="Comment" :rules="['required', { min: 10, message: 'Comment must be at least 10 characters' }]" helper-text="Please provide detailed feedback" /> <!-- Auto-resizing textarea --> <BaseTextarea v-model="content" label="Content" :rows="3" :max-rows="10" auto-resize block /> <!-- Filled variant --> <BaseTextarea v-model="notes" label="Notes" variant="filled" placeholder="Add your notes here" /> <!-- With custom resize behavior --> <BaseTextarea v-model="text" label="Resizable" resize="both" :rows="4" /> ``` #### Form Validation Example ```vue <template> <form @submit.prevent="handleSubmit"> <BaseTextarea v-model="feedback" label="Feedback" :rules="[ 'required', { min: 20, message: 'Feedback must be at least 20 characters long' }, { max: 500, message: 'Feedback cannot exceed 500 characters' }, ]" block @mounted="(ref) => registerTextarea('feedback', ref)" /> <BaseButton type="submit">Submit Feedback</BaseButton> </form> </template> <script setup> const formRefs = ref({}) const registerTextarea = (name, ref) => { if (ref) { formRefs.value[name] = ref } } const validateForm = () => { let isValid = true Object.values(formRefs.value).forEach((textarea) => { if (textarea?.validate && !textarea.validate()) { isValid = false } }) return isValid } const handleSubmit = () => { if (validateForm()) { console.log('Form is valid, submitting...') } } </script> ``` ### BaseCheckbox Checkbox component with customizable size, state and styling. Supports single checkbox or group functionality. Features: - Three sizes (small, medium, large) - Disabled state - Indeterminate state - Custom colors and styling - Group functionality for multiple checkboxes #### Props | Prop | Type | Default | Description | | ------------- | -------------------------------- | ----------- | --------------------------------- | | modelValue | `boolean \| Array` | `false` | v-model binding value | | label | `string` | `''` | Label text for the checkbox | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | Size of the checkbox | | disabled | `boolean` | `false` | Whether checkbox is disabled | | indeterminate | `boolean` | `false` | Show indeterminate state | | value | `any` | `undefined` | Value when used in checkbox group | | name | `string` | `''` | Input name attribute | #### Basic examples ```vue <BaseCheckbox v-model="checked" label="Basic checkbox" /> <BaseCheckbox v-model="checked"> Checkbox with <span style="color: purple">slots</span> </BaseCheckbox> <BaseCheckbox v-model="checked" error label="Required field with error" /> <BaseCheckbox v-model="checked" disabled label="Disabled checkbox" /> <BaseCheckbox v-model="checked" size="small" label="Small option" /> <BaseCheckbox v-model="checked" size="medium" label="Medium option" /> <BaseCheckbox v-model="checked" size="large" label="Large option" /> ``` #### Example with multiple options ```js const pageState = reactive({ checkbox: { checked: false, selectedFruits: [], selectAll: false, someSelected: false, }, fruits: [ { name: 'Apple', value: 'apple', }, { name: 'Banana', value: 'banana', }, { name: 'Orange', value: 'orange', }, { name: 'Pineapple', value: 'pineapple', }, ], }) const allSelected = computed(() => { return pageState.checkbox.selectedFruits.length === pageState.fruits.length }) const someSelected = computed(() => { return pageState.checkbox.selectedFruits.length > 0 && !allSelected.value }) const handleSelectAll = (checked) => { if (!checked) return (pageState.checkbox.selectedFruits = []) pageState.fruits.forEach((fruit) => { if (!pageState.checkbox.selectedFruits.includes(fruit.value)) { pageState.checkbox.selectedFruits.push(fruit.value) } }) } ``` ```vue <BaseCheckbox v-for="fruit in pageState.fruits" :key="fruit.value" v-model="pageState.checkbox.selectedFruits" :value="fruit.value" :label="fruit.name" /> <BaseCheckbox v-model="allSelected" :indeterminate="someSelected" label="Select all" @update:model-value="handleSelectAll" /> ``` ### BaseToast Toast notification system for user feedback. Provides a flexible way to show notifications with different variants (success, error, warning, info), positions, and durations. Features: - Multiple toast positions (top-right, top-center, top-left, bottom-right, bottom-center, bottom-left) - Customizable duration - Different variants with appropriate styling - Pause timer on hover - Auto-dismiss with configurable timeout - Stacking multiple toasts - Smooth enter/exit animations #### Base Toast props | Prop | Type | Default | Description | | ------------ | ------------------------------------------------------------------------------------------------- | ------------- | --------------------------------------------------------- | | variant | `'default' \| 'success' \| 'error' \| 'warning' \| 'info'` | `'default'` | Style variant of the toast | | title | `string` | `undefined` | Optional title text shown at top of toast | | message | `string` | `''` | Main toast message content | | duration | `number` | `5000` | Time in ms before auto-dismissing (0 for no auto-dismiss) | | position | `'top-right' \| 'top-center' \| 'top-left' \| 'bottom-right' \| 'bottom-center' \| 'bottom-left'` | `'top-right'` | Position where toast appears | | closable | `boolean` | `true` | Whether to show close button | | pauseOnHover | `boolean` | `true` | Pause dismiss timer when hovering | | simple | `boolean` | `false` | Simple toast option | #### Basic example ```vue <script setup> import { useToast } from 'vue-smart-ui' const toast = useToast() const showToast = () => { toast.success('Basic toast') } const showCustom = () => { toast.warning('Custom toast', { title: 'Title', position: 'bottom-center', duration: 5000, }) } const showSimple = () => { toast.error('Simple toast', { position: 'bottom-center', duration: 5000, simple: true, }) } </script> ``` ### BaseInfiniteScroll Component for implementing infinite scroll functionality. #### Props | Prop | Type | Default | Description | | ----------- | --------- | ------------------------- | ---------------------------------------------------------- | | loading | `boolean` | `false` | Whether more content is currently being loaded | | disabled | `boolean` | `false` | Disable infinite scroll functionality | | threshold | `number` | `20` | Distance in pixels from bottom before triggering load more | | container | `string` | `null` | CSS selector for scroll container. If null, uses window | | loadingText | `string` | `'Loading more items...'` | Text shown during loading. If empty, no text is shown | | endText | `string` | `'No more items to load'` | Text shown when disabled. If empty, no text is shown | #### Slots - `default`: Main content area for the list of items - `loading`: Custom loading indicator when fetching more items - `disabled`: Custom message or content when there are no more items to load #### Basic example ```vue <BaseInfiniteScroll :loading="loading" :disabled="!hasMore" @load-more="loadMore"> <!-- Items list --> <div class="items-grid"> <div v-for="item in pageState.infiniteScroll.items" :key="item.id" class="item"> {{ item.title }} </div> </div> <!-- Loading custom --> <template #loading> <div class="custom-loader">Loading more items...</div> </template> <!-- End message --> <template #disabled> <div class="end-message">There are no more items to load</div> </template> </BaseInfiniteScroll> ``` ### BaseSlider A highly customizable slider component that supports single values or ranges, with custom marks, value formatting, and different visual states. Features: - Support for single values or ranges - Customizable marks for specific points - Custom value formatting - Different visual states (success, error, warning) - Configurable minimum and maximum values - Adjustable steps - Fully accessible with keyboard support - Display of values at extremities and above controls #### Props | Prop | Type | Default | Description | | ------------ | ----------------- | ----------- | ------------------------------------------------ | | modelValue | `Number \| Array` | `0` | Current slider value (single or array for range) | | min | `Number` | `0` | Minimum value of the slider | | max | `Number` | `100` | Maximum value of the slider | | step | `Number` | `1` | Increment between values | | label | `String` | `null` | Label text for the slider | | disabled | `Boolean` | `false` | Disables the slider | | required | `Boolean` | `false` | Marks the field as required | | helperText | `String` | `null` | Helper text displayed below the slider | | errorMessage | `String` | `null` | Error message to display | | state | `String` | `null` | Visual state: 'success', 'error', 'warning' | | showValue | `Boolean` | `true` | Displays the current value above the control | | range | `Boolean` | `false` | Enables range mode with two controls | | variant | `String` | `'default'` | Visual variant: 'default', 'filled' | | marks | `Array` | `[]` | Marks for specific values | | formatValue | `Function` | `null` | Function to format the display of values | #### Events - `@update:modelValue`: Emitted when the slider value changes - `@change`: Emitted when the user changes the value - `@mounted`: Emitted when the component is mounted, passes the slider reference #### Basic Examples ```vue <!-- Simple slider --> <BaseSlider v-model="volume" label="Volume" /> <!-- Slider with value formatting --> <BaseSlider v-model="price" :min="0" :max="1000" :format-value="(val) => `$${val.toFixed(2)}`" label="Price" /> <!-- Slider with marks --> <BaseSlider v-model="rating" :min="0" :max="5" :step="0.5" :marks="[ { value: 0, label: 'Poor' }, { value: 2.5, label: 'Average' }, { value: 5, label: 'Excellent' }, ]" label="Rating" /> <!-- Range slider --> <BaseSlider v-model="priceRange" :min="0" :max="1000" range :format-value="(val) => `$${val}`" label="Price Range" /> <!-- Slider with error state --> <BaseSlider v-model="value" state="error" error-message="Please select a higher value" label="Value" /> ``` #### Validation Example ```vue <template> <form @submit.prevent="handleSubmit"> <BaseSlider v-model="formData.budget" label="Budget" :min="100" :max="10000" :step="100" :format-value="(val) => `$${val.toLocaleString()}`" :marks="[ { value: 1000, label: 'Basic' }, { value: 5000, label: 'Intermediate' }, { value: 10000, label: 'Premium' }, ]" @mounted="(ref) => registerSlider('budget', ref)" /> <BaseButton type="submit">Submit</BaseButton> </form> </template> <script setup> const formRefs = ref({}) const registerSlider = (name, ref) => { if (ref) { formRefs.value[name] = ref } } const validateForm = () => { let isValid = true // Validate if budget is at least 1000 const budgetRef = formRefs.value.budget if (budgetRef && budgetRef.getValue() < 1000) { budgetRef.setError('Minimum budget is $1,000') isValid = false } return isValid } const handleSubmit = () => { if (validateForm()) { console.log('Form is valid, submitting...') } } </script> ``` ### BaseSegmentedButtons A customizable segmented button control that displays a set of options in a connected button group. Perfect for toggling between different options or selecting multiple values from a set of related choices. Features: - Single or multiple selection modes - Support for string, number, or object options - Customizable appearance with different variants and sizes - Proper styling for connected buttons with rounded corners at ends - Consistent with other form components #### Props | Prop | Type | Default | Description | | ---------- | --------------------------- | ----------- | ------------------------------------------------------------- | | modelValue | `String \| Number \| Array` | `[]` | Selected value(s) (v-model binding) | | options | `Array` | `[]` | Array of options to display as buttons | | valueKey | `String` | `'value'` | Property name to use as value when options are objects | | labelKey | `String` | `'label'` | Property name to use as display text when options are objects | | variant | `String` | `'primary'` | Visual style. Options: 'primary', 'secondary', 'gray' | | size | `String` | `'medium'` | Button size. Options: 'small', 'medium', 'large' | | disabled | `Boolean` | `false` | Disables the entire control | | block | `Boolean` | `false` | If true, buttons will expand to fill the container width | | multiple | `Boolean` | `false` | Enables selection of multiple options | #### Events - `@update:modelValue`: Emitted when selection changes - `@change`: Emitted when selection changes, with the new value #### Basic Examples ```vue <!-- Simple string options --> <BaseSegmentedButtons v-model="selectedFruit" :options="['apple', 'banana', 'orange']" /> <!-- Object options --> <BaseSegmentedButtons v-model="selectedColor" :options="[ { value: 'red', label: 'Red' }, { value: 'green', label: 'Green' }, { value: 'blue', label: 'Blue' }, ]" /> <!-- Multiple selection mode --> <BaseSegmentedButtons v-model="selectedCategories" :options="['Work', 'Personal', 'Family']" multiple /> <!-- Custom variants and sizes --> <BaseSegmentedButtons v-model="alignment" :options="['Left', 'Center', 'Right']" variant="secondary" size="small" /> <!-- Block display (full width) --> <BaseSegmentedButtons v-model="viewMode" :options="['List', 'Grid', 'Table']" block /> <!-- Custom object keys --> <BaseSegmentedButtons v-model="selectedItem" :options="[ { id: 1, name: 'Option A' }, { id: 2, name: 'Option B' }, { id: 3, name: 'Option C' }, ]" valueKey="id" labelKey="name" /> ``` ### BaseColorPicker A customizable color picker component that allows users to select colors through an input field, color picker interface, and preset colors. Features: - Color preview swatch - Text input for direct color code entry - Native color picker interface - Preset color palette - Different variants to match other form components - Validation support - Dark mode compatible #### Props | Prop | Type | Default | Description | | ------------ | --------- | ----------- | ------------------------------------------------------ | | id | `String` | `''` | Optional color picker ID | | modelValue | `String` | `'#000000'` | The selected color value (hex, rgb, etc.) | | variant | `String` | `'default'` | Visual style. Options: 'default', 'filled', 'outlined' | | state | `String` | `null` | Visual state: 'success', 'error', 'warning' | | label | `String` | `null` | Label text for the color picker | | disabled | `Boolean` | `false` | Disables the color picker | | readonly | `Boolean` | `false` | Makes the control read-only | | required | `Boolean` | `false` | Marks the field as required | | helperText | `String` | `null` | Helper text displayed below the picker | | errorMessage | `String` | `null` | Error message to display | | rules | `Array` | `[]` | Validation rules array | | name | `String` | `''` | Input name attribute | | format | `String` | `'hex'` | Color format. Options: 'hex', 'rgb', 'rgba', 'hsl' | | showPreview | `Boolean` | `true` | Shows color preview swatch | | presets | `Array` | `[]` | Array of preset colors to display | #### Events - `@update:modelValue`: Emitted when color value changes - `@focus`: Emitted on field focus - `@blur`: Emitted on field blur - `@input`: Emitted on input - `@validation`: Emitted when validation occurs, includes validation status - `@mounted`: Emitted when component is mounted, passes component reference #### Basic Examples ```vue <!-- Simple color picker --> <BaseColorPicker v-model="color" label="Choose a color" /> <!-- With custom presets --> <BaseColorPicker v-model="themeColor" label="Theme Color" :presets="['#FF5733', '#33FF57', '#3357FF', '#F3FF33', '#FF33F3']" /> <!-- With validation --> <BaseColorPicker v-model="brandColor" label="Brand Color" :rules="['required']" helper-text="Please select a brand color" /> <!-- Without preview --> <BaseColorPicker v-model="accentColor" :showPreview="false" label="Accent Color" /> <!-- Filled variant --> <BaseColorPicker v-model="backgroundColor" variant="filled" label="Background" /> ``` ### BaseDropdown Customizable dropdown menu component that provides a toggleable menu with positioning and click handling. #### Props | Prop | Type | Default | Description | | ------------------- | --------- | --------- | ----------------------------------------------- | | modelValue | `boolean` | `false` | Controls visibility of dropdown (v-model) | | variant | `string` | `default` | Visual style - 'default', 'white', or 'dark' | | width | `string` | `auto` | Width of dropdown menu | | closeOnClick | `boolean` | `true` | Whether to close dropdown when item clicked | | closeOnClickOutside | `boolean` | `true` | Whether to close dropdown when clicking outside | #### Slots - `trigger`: Content that triggers the dropdown (usually a button) - `default`: Content of the dropdown menu #### Basic example ```vue <BaseDropdown v-model="show" variant="default"> <!-- Trigger --> <template #trigger> <BaseButton>Open Dropdown</BaseButton> </template> <!-- Dropdown items --> <div class="base-dropdown-item">Profile</div> <div class="base-dropdown-item">Settings</div> <div class="base-dropdown-item" data-prevent-close>Notifications</div> <div class="base-dropdown-item" style="color: red">Logout</div> </BaseDropdown> ``` ### BaseSkeleton Component for displaying loading states and placeholders while content is being loaded. Supports different variants like text, headings, circles, and rectangles with optional animations. #### Props | Prop | Type | Default | Description | | -------- | --------- | ----------- | --------------------------------------------------------------------- | | variant | `string` | `rectangle` | Type of skeleton - 'rectangle', 'circle', 'text', 'heading', 'button' | | width | `string` | `100%` | Width of the skeleton element | | height | `string` | - | Height of the skeleton element (overrides variant default) | | rounded | `boolean` | `false` | Whether to apply rounded corners | | animated | `boolean` | `true` | Whether to show loading animation | #### Basic examples ```vue <!-- Avatar --> <BaseSkeleton variant="circle" width="5rem" height="5rem" /> <!-- Title --> <BaseSkeleton variant="heading" width="60%" /> <!-- Text --> <BaseSkeleton variant="text" /> <BaseSkeleton variant="text" /> <BaseSkeleton variant="text" /> <!-- Button --> <BaseSkeleton variant="button" rounded /> ``` ### BasePopup Customizable modal/popup component with support for different variants, animations and positioning. Provides a flexible way to display modal dialogs, alerts and confirmation boxes. #### Props | Prop | Type | Default | Description | | ------------------- | --------- | --------- | ---------------------------------------------------------------------- | | modelValue | `boolean` | `false` | Controls visibility of the popup (v-model) | | variant | `string` | `default` | Popup style variant - 'default', 'info', 'success', 'warning', 'error' | | size | `string` | `medium` | Size of the popup - 'small', 'medium', 'large' | | position | `string` | `center` | Position of the popup - 'center', 'top', 'bottom', 'left', 'right' | | disableClickOutside | `boolean` | `false` | Whether clicking outside closes the popup | | closeOnEsc | `boolean` | `true` | Whether pressing Escape key closes the popup | #### Slots - `default`: Main content of the popup - `trigger`: Element that triggers the popup when clicked #### Usage example ```vue <BasePopup v-model="show" :variant="variant" :size="size" :position="position"> <template #header> <p style="font-size: 1.2rem; font-weight: bold; text-align: center; margin-block: 0.5rem"> Configured Popup </p> </template> <div class="popup-content"> <p>Current configuration:</p> <ul> <li>Variant: {{ variant }}</li> <li>Size: {{ size }}</li> <li>Position: {{ position }}</li> </ul> </div> <template #footer> <div style="text-align: center"> <BaseButton @click="show = false">Close</BaseButton> </div> </template> <template #close> <span>&times;</span> </template> </BasePopup> ``` ### BaseOTP A customizable One-Time Password (OTP) input component for secure authentication flows. Provides individual input fields for each digit with automatic focus management and validation. Features: - Configurable number of input fields (default 6) - Automatic focus progression between fields - Support for different input types (text, number, password) - Paste support for complete codes - Keyboard navigation (arrow keys, backspace, delete) - Built-in validation for complete/incomplete codes - Clear all functionality with Shift+Backspace or Shift+Delete - Different visual variants and states - Auto-focus capability #### Props | Prop | Type | Default | Description | | ------------ | --------- | --------------------------------------------- | ------------------------------------------------- | | id | `String` | `''` | Optional OTP input ID | | modelValue | `String` | `''` | The OTP value (v-model binding) | | length | `Number` | `6` | Number of OTP input fields | | variant | `String` | `'default'` | Visual style. Options: 'default', 'filled' | | state | `String` | `null` | Visual state: 'success', 'error', 'warning' | | label | `String` | `null` | Label text for the OTP input | | disabled | `Boolean` | `false` | Disables all input fields | | readonly | `Boolean` | `false` | Makes all input fields read-only | | required | `Boolean` | `false` | Marks the field as required | | helperText | `String` | `null` | Helper text displayed below the inputs | | errorMessage | `String` | `null` | Error message to display | | autoFocus | `Boolean` | `false` | Automatically focus the first input on mount | | type | `String` | `'text'` | Input type. Options: 'text', 'number', 'password' | | instructions | `String` | `'Press Shift+Backspace to clear all fields'` | Instructions text displayed below the inputs | #### Events - `@update:modelValue`: Emitted when OTP value changes - `@complete`: Emitted when all fields are filled, passes the complete OTP value - `@validation`: Emitted when validation occurs, includes validation status - `@mounted`: Emitted when component is mounted, passes component reference with methods #### Basic Examples ```vue <!-- Simple 6-digit OTP --> <BaseOTP v-model="otpCode" label="Simple 6-digit OTP" required /> <!-- Required with helper text --> <BaseOTP v-model="otpCode" label="Required with helper text" helperText="Enter the code sent to your phone" required /> <!-- Variant filled --> <BaseOTP v-model="otpCode" label="Variant filled" helperText="Enter the code sent to your phone" required variant="filled" /> <!-- Auto focus with 4 digits --> <BaseOTP v-model="otpCode" :length="4" label="Auto focus with 4 digits" helper-text="Enter the 4-digit code" required auto-focus /> <!-- No required --> <BaseOTP v-model="otpCode" label="No required" helper-text="Enter the 6-digit code" /> ``` #### Form Validation Example ```vue <template> <form @submit.prevent="handleSubmit"> <BaseOTP v-model="formData.otpCode" label="Verification Code" required auto-focus helper-text="Enter the 6-digit code sent to your email" @complete="handleOTPComplete" @mounted="(ref) => registerOTP('otp', ref)" /> <BaseButton type="submit" :disabled="!isFormValid"> Verify Code </BaseButton> </form> </template> <script setup> const formRefs = ref({}) const formData = reactive({ otpCode: '', }) const registerOTP = (name, ref) => { if (ref) { formRefs.value[name] = ref } } const handleOTPComplete = (code) => { console.log('OTP completed:', code) // Auto-submit or validate when complete } const validateForm = () => { let isValid = true Object.values(formRefs.value).forEach((field) => { if (field?.validate && !field.validate()) { isValid = false } }) return isValid } const handleSubmit = () => { if (validateForm()) { console.log('Form is valid, submitting OTP:', formData.otpCode) } } const isFormValid = computed(() => { return formData.otpCode.length === 6 }) </script> ``` ## 🎨 Customization ### CSS Variables The cpomponents can be customized in multiple ways: - Using CSS variables for theming - Overriding component classes directly - Using Tailwind CSS classes #### Available CSS variables for each component ```css :root { // Base colors --color-primary: #3b82f6; --color-primary-dark: #2563eb; --color-primary-light: #e0e7ff; --color-primary-text: #1e40af; --color-secondary: #9333ea; --color-secondary-dark: #7e22ce; --color-secondary-light: #ede9fe; --color-secondary-text: #4338ca; --color-info: #3b82f6; --color-info-dark: #0284c7; --color-info-light: #dbeafe; --color-success: #22c55e; --color-success-dark: #15803d; --color-success-light: #f0fdf4; --color-warning: #f59e0b; --color-warning-dark: #92400e; --color-warning-light: #fefce8; --color-error: #ef4444; --color-error-dark: #991b1b; --color-error-light: #fee2e2; // Background colors --bg-default: #ffffff; --bg-subtle: #f9fafb; --bg-muted: #f3f4f6; --bg-emphasized: #e5e7eb; --bg-disabled: #f3f4f6; // Surface colors (for cards, modals, etc.) --surface-default: #f9fafb; --surface-hover: #f3f4f6; --surface-active: #e5e7eb; --surface-selected: #eff6ff; // Text colors --text-default: #111827; --text-muted: #374151; --text-subtle: #6b7280; --text-disabled: #9ca3af; --text-inverse: #ffffff; // Border colors --border-default: #e5e7eb; --border-strong: #c0c4c9; --border-focus: #d1d5db; --color-white: #ffffff; --color-black: #000000; // Font base --font-family-base: 'Roboto', sans-serif; // --font-size-base: 62.5%; // Buttons variables --button-primary-bg: var(--color-primary); --button-primary-hover: var(--color-primary-dark); --button-primary-text: var(--color-white); --button-secondary-bg: var(--color-secondary); --button-secondary-hover: var(--color-secondary-dark); --button-secondary-text: var(--color-white); --button-gray-bg: var(--bg-muted); --button-gray-hover: var(--bg-emphasized); --button-gray-text: var(--text-muted); --button-outline-border: var(--border-strong); --button-outline-hover: var(--bg-muted); --button-outline-text: var(--text-muted); --button-ghost-text: var(--text-muted); --button-focus-outline: var(--color-primary); --button-small-font-size: 0.875rem; --button-medium-font-size: 1rem; --button-large-font-size: 1.125rem; // Popups variables --popup-default-color: var(--text-subtle); --popup-info-color: var(--color-info); --popup-success-color: var(--color-success); --popup-warning-color: var(--color-warning); --popup-error-color: var(--color-error); --popup-border-color: var(--border-strong); --popup-default-bg: var(--surface-default); --popup-info-bg: var(--surface-default); --popup-success-bg: var(--surface-default); --popup-warning-bg: var(--surface-default); --popup-error-bg: var(--surface-default); // Toasts variables --toast-default-bg: var(--surface-default); --toast-default-color: var(--border-strong); --toast-default-text: var(--text-default); --toast-info-bg: var(--color-primary-light); --toast-info-color: var(--color-primary); --toast-info-text: var(--color-primary-text); --toast-success-bg: var(--color-success-light); --toast-success-color: var(--color-success); --toast-success-text: var(--color-success-dark); --toast-warning-bg: var(--color-warning-light); --toast-warning-color: var(--color-warning); --toast-warning-text: var(--color-warning-dark); --toast-error-bg: var(--color-error-light); --toast-error-color: var(--color-error); --toast-error-text: var(--color-error-dark); --toast-title-font-size: 1rem; --toast-message-font-size: 0.75rem; // Dropdowns variables --dropdown-bg: var(--surface-default); --dropdown-border-color: var(--border-default); --dropdown-item-hover-bg: var(--surface-hover); --dropdown-dark-bg: var(--text-muted); --dropdown-dark-border-color: #4b5563; --dropdown-dark-item-hover-bg: #4b5563; // Skeletons variables --skeleton-bg: var(--bg-emphasized); --skeleton-rectangle-height: 1.5rem; --skeleton-circle-size: 3rem; --skeleton-heading-height: 2rem; --skeleton-button-height: 2.5rem; --skeleton-button-width: 8rem; --skeleton-text-height: 1rem; --skeleton-rounded-radius: 0.375rem; --skeleton-shine: rgba(255, 255, 255, 0.3); // Accordions variables --accordion-border-color: var(--border-default); --accordion-header-color: var(--text-default); --accordion-hover-bg: var(--bg-subtle); --accordion-focus-ring: var(--color-primary); // Input variables --input-label-color: var(--text-muted); --input-required-color: var(--color-error); --input-helper-color: var(--text-subtle); --input-border-color: var(--border-strong); --input-bg: var(--color-white); --input-focus-border-color: var(--color-primary); --input-focus-ring-color: rgba(59, 130, 246, 0.1); --input-placeholder-color: var(--text-disabled); --input-disabled-bg: var(--bg-muted); --input-disabled-text: var(--text-subtle); --input-icon-color: var(--text-subtle); --input-filled-bg: var(--bg-muted); --input-filled-focus-bg: var(--bg-subtle); --input-success-color: var(--color-success); --input-error-color: var(--color-error); --input-warning-color: var(--color-warning); --input-label-font-size: 0.875rem; --input-field-font-size: 0.875rem; --input-helper-font-size: 0.75rem; // Checkbox variables --checkbox-border: 1px solid var(--checkbox-border-color); --checkbox-border-color: var(--border-strong); --checkbox-hover-border-color: var(--bg-emphasized); --checkbox-border-radius: 0.25rem; --checkbox-bg: var(--color-white); --checkbox-check: var(--text-muted); --checkbox-text: var(--text-muted); --checkbox-small-font-size: 0.875rem; --checkbox-medium-font-size: 1rem; --checkbox-large-font-size: 1.125rem; // Textarea variables --textarea-font-size: 1rem; --textarea-font-family: var(--font-family-base); --textarea-line-height: 1.6; --textarea-padding: 0.75rem 1rem; --textarea-border-radius: 0.5rem; --textarea-bg: var(--color-white); --textarea-text-color: var(--text-default); --textarea-border-color: var(--border-strong); --textarea-focus-border-color: var(--color-primary); --textarea-success-color: var(--color-success); --textarea-error-color: var(--color-error); --textarea-warning-color: var(--color-warning); --textarea-filled-bg: var(--bg-muted); --textarea-filled-hover-bg: var(--bg-emphasized); --textarea-filled-focus-bg: var(--bg-subtle); --textarea-helper-font-size: 0.75rem; --textarea-label-font-size: 0.875rem; // Slider variables --slider-track-height: 6px; --slider-track-bg: var(--surface-active); --slider-track-radius: 3px; --slider-fill-color: var(--color-primary); --slider-thumb-size: 18px; --slider-thumb-bg: var(--surface-default); --slider-thumb-border-width: 2px; --slider-thumb-border-color: var(--color-primary); --slider-thumb-shado