diff --git a/.cursor/rules/Component-development-guidelines.mdc b/.cursor/rules/Component-development-guidelines.mdc index 9433c68..a979290 100644 --- a/.cursor/rules/Component-development-guidelines.mdc +++ b/.cursor/rules/Component-development-guidelines.mdc @@ -119,6 +119,18 @@ alwaysApply: false - Support validation states with error styling - Include proper labels and helper text +#### InputGroup Integration +- **MANDATORY**: All form input components must support InputGroup integration +- Import `useInputGroup` composable and destructure required properties +- Hide individual labels and error messages when `isInInputGroup` is true +- Use `props.error` directly for error state (InputGroup passes this via props) +- Apply conditional class binding: InputGroup classes when in group, default when standalone +- Implement focus/blur event handlers for border management +- Use `h-10` height and `flex-1` width for form inputs in InputGroup +- Use content width for buttons in InputGroup +- Apply `!important` to error borders for proper CSS specificity +- Exclude TextArea from InputGroup due to height constraints + #### Modal Components - Use Headless UI Dialog components - Support custom triggers via slots @@ -189,4 +201,5 @@ alwaysApply: false - **RTL Support**: Check directional styling uses logical properties - **Accessibility**: Always consider accessibility when creating/modifying components - **Pattern Consistency**: Follow established patterns from existing components +- **InputGroup Compliance**: Ensure all new form components support InputGroup integration - **Code Examples**: Provide complete, runnable code examples diff --git a/CHANGELOG-DEV.md b/CHANGELOG-DEV.md index d119fd9..a179c35 100644 --- a/CHANGELOG-DEV.md +++ b/CHANGELOG-DEV.md @@ -1,3 +1,32 @@ +# [1.17.0-dev.37](https://github.com/codebridger/lib-vue-components/compare/dev-1.17.0-dev.36...dev-1.17.0-dev.37) (2025-09-09) + + +### Bug Fixes + +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Improve error handling and styling in InputGroup and related components ([4df36d0](https://github.com/codebridger/lib-vue-components/commit/4df36d014f7aee577c32406daf84e25370b8536c)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Update Button accessibility test and add InputGroup tests ([49a570a](https://github.com/codebridger/lib-vue-components/commit/49a570a351777c9165fa9ef4b0fda272b2e23331)) + + +### Features + +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Enhance InputGroup and related components for improved styling and functionality ([40ee215](https://github.com/codebridger/lib-vue-components/commit/40ee215e904beacb2fdc36dafd74b624aeb398b7)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Add InputGroup component with Storybook documentation ([41ccd32](https://github.com/codebridger/lib-vue-components/commit/41ccd326aeed2fbce0cb58853d21ffc66385d470)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Enhance Button and Input components for InputGroup consistency ([dcb6d69](https://github.com/codebridger/lib-vue-components/commit/dcb6d69bf9234ea3f92b7869939eab80306eb91c)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Enhance Button and InputGroup integration with new button styling ([abff417](https://github.com/codebridger/lib-vue-components/commit/abff417b5f4491ace084eda7e72780878c8fc1da)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Enhance Input and TextArea components for InputGroup integration ([dc90f6f](https://github.com/codebridger/lib-vue-components/commit/dc90f6f4e01c6726cebb7cbf55ddab3254278d57)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Enhance InputGroup and related components for improved styling and consistency ([0593109](https://github.com/codebridger/lib-vue-components/commit/05931096a7e87de5c55193328b741c9d911289d5)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Enhance InputGroup and related components with focus management and border styling ([8029f74](https://github.com/codebridger/lib-vue-components/commit/8029f742c8499c60c82d19b2203436edb160f79c)) +* [#86](https://github.com/codebridger/lib-vue-components/issues/86)eunvbgj Refactor InputGroup and Button components for improved styling and consistency ([dab9956](https://github.com/codebridger/lib-vue-components/commit/dab9956eecf59ab2b365e57b88e9e08d58d8e2e3)) +* Enhance Button component with focus styling and loading state handling ([9023937](https://github.com/codebridger/lib-vue-components/commit/9023937a494c30f5ee9f1f8ca89ecbc6913176ad)) +* Update Component Development Guidelines to include InputGroup integration requirements ([e50d144](https://github.com/codebridger/lib-vue-components/commit/e50d1448e4c1fce202c21af2732629e68c67a276)) + +# [1.17.0-dev.36](https://github.com/codebridger/lib-vue-components/compare/dev-1.17.0-dev.35...dev-1.17.0-dev.36) (2025-09-01) + + +### Features + +* Add badge label support to IconButton component ([5084bce](https://github.com/codebridger/lib-vue-components/commit/5084bce5f7048beacbc1efee050864467b958155)) + # [1.17.0-dev.35](https://github.com/codebridger/lib-vue-components/compare/dev-1.17.0-dev.34...dev-1.17.0-dev.35) (2025-08-30) diff --git a/package.json b/package.json index 77d1196..6757675 100644 --- a/package.json +++ b/package.json @@ -127,4 +127,4 @@ "vitest": "^3.2.4", "vue-tsc": "^0.40.4" } -} +} \ No newline at end of file diff --git a/src/composables/use-input-group.ts b/src/composables/use-input-group.ts new file mode 100644 index 0000000..46c9b93 --- /dev/null +++ b/src/composables/use-input-group.ts @@ -0,0 +1,160 @@ +import { inject, computed, getCurrentInstance, ref } from "vue"; + +interface InputGroupContext { + isRtl: boolean; + isDarkMode: boolean; + error: boolean; + disabled: boolean; + removeRightBorder?: boolean; +} + +export function useInputGroup() { + const inputGroupContext = inject( + "inputGroupContext", + null + ); + + if (!inputGroupContext) { + return { + isInInputGroup: false, + position: null, + inputGroupClasses: computed(() => []), + inputGroupItemClasses: computed(() => []), + inputGroupButtonClasses: computed(() => []), + inputGroupBorderClasses: "", + }; + } + + const instance = getCurrentInstance(); + const position = instance?.vnode?.props?.["data-input-group-position"] as + | string + | null; + const removeRightBorder = instance?.vnode?.props?.[ + "data-input-group-remove-right-border" + ] as boolean | null; + const childIndex = instance?.vnode?.props?.["data-input-group-index"] as + | number + | null; + + // Track focus state for this specific child + const isFocused = ref(false); + + const inputGroupClasses = computed(() => { + const classes: string[] = []; + const { isRtl, error, disabled } = inputGroupContext; + + // Base classes for input group children (replaces form-input) + classes.push( + "border px-4 py-2 text-sm font-semibold text-black dark:text-white-dark focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary" + ); + + if (error) { + classes.push("!border-red-500"); + } else { + classes.push("border-gray-300 dark:border-gray-600"); + } + + // Don't handle disabled styling here - let child components handle their own disabled styling + classes.push("bg-white dark:bg-gray-800"); + + // Position-based styling - with gaps, all elements get full borders and radius + if (position) { + if (position === "first") { + classes.push(isRtl ? "rounded-r-md" : "rounded-l-md"); + } else if (position === "last") { + classes.push(isRtl ? "rounded-l-md" : "rounded-r-md"); + } else if (position === "only") { + classes.push("rounded-md"); + } else { + // Middle elements - no radius + classes.push("rounded-none"); + } + } + + // Border removal for seamless connection - but not when focused + if (removeRightBorder && !isFocused.value) { + classes.push("border-r-0"); + } + + return classes; + }); + + // Additional classes for InputGroupItem (non-form elements) + const inputGroupItemClasses = computed(() => { + const classes = [...inputGroupClasses.value]; + + // Add default styling for non-form elements + classes.push("bg-gray-100 dark:bg-gray-700"); + classes.push( + "flex items-center px-3 text-sm font-medium text-gray-700 dark:text-gray-300" + ); + + return classes; + }); + + // Classes for buttons in InputGroup (no flex-1, different styling) + const inputGroupButtonClasses = computed(() => { + const classes: string[] = []; + const { isRtl, error, disabled } = inputGroupContext; + + // Base classes for buttons in input group - only border and padding + classes.push( + "border px-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary" + ); + + if (error) { + classes.push("!border-red-500"); + } else if (disabled) { + classes.push("border-gray-300 dark:border-gray-600"); + } else { + // Don't set border color when not disabled or error - let button color classes handle it + // But we still need a base border for the group styling + classes.push("border-gray-300 dark:border-gray-600"); + } + + // Don't handle disabled styling here - let child components handle their own disabled styling + // Don't set background color - let button color classes handle it + + // Position-based styling - with gaps, all elements get full borders and radius + if (position) { + if (position === "first") { + classes.push(isRtl ? "rounded-r-md" : "rounded-l-md"); + } else if (position === "last") { + classes.push(isRtl ? "rounded-l-md" : "rounded-r-md"); + } else if (position === "only") { + classes.push("rounded-md"); + } else { + // Middle elements - no radius + classes.push("rounded-none"); + } + } + + // Border removal for seamless connection - but not when focused + if (removeRightBorder && !isFocused.value) { + classes.push("border-r-0"); + } + + return classes; + }); + + // Focus event handlers + const handleFocus = () => { + isFocused.value = true; + }; + + const handleBlur = () => { + isFocused.value = false; + }; + + return { + isInInputGroup: true, + position, + inputGroupClasses, + inputGroupItemClasses, + inputGroupButtonClasses, + inputGroupBorderClasses: inject("inputGroupBorderClasses", ""), + context: inputGroupContext, + handleFocus, + handleBlur, + }; +} diff --git a/src/elements/Button.vue b/src/elements/Button.vue index 5ed24ea..69a13d1 100644 --- a/src/elements/Button.vue +++ b/src/elements/Button.vue @@ -1,33 +1,59 @@