diff --git a/playgrounds/nuxt/app/pages/components/field-group.vue b/playgrounds/nuxt/app/pages/components/field-group.vue index 210eba6052..4959380206 100644 --- a/playgrounds/nuxt/app/pages/components/field-group.vue +++ b/playgrounds/nuxt/app/pages/components/field-group.vue @@ -32,9 +32,22 @@ const orientation = ref('horizontal' as keyof typeof theme.variants.orientation) Button - - Button - + + + Open + + + + + + Submit + + + Close + + + + Button diff --git a/src/runtime/components/FieldGroup.vue b/src/runtime/components/FieldGroup.vue index 3bb45f90e7..ca74659588 100644 --- a/src/runtime/components/FieldGroup.vue +++ b/src/runtime/components/FieldGroup.vue @@ -20,6 +20,11 @@ export interface FieldGroupProps { * @defaultValue 'horizontal' */ orientation?: FieldGroup['variants']['orientation'] + /** + * When true, clears the field group context so nested components don't inherit styling. + * @defaultValue false + */ + clear?: boolean class?: any ui?: FieldGroup['slots'] } @@ -46,14 +51,25 @@ const appConfig = useAppConfig() as FieldGroup['AppConfig'] // eslint-disable-next-line vue/no-dupe-keys const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.fieldGroup || {}) })) -provide(fieldGroupInjectionKey, computed(() => ({ - orientation: props.orientation, - size: props.size -}))) +provide(fieldGroupInjectionKey, computed(() => { + if (props.clear) { + return { + orientation: undefined, + size: undefined + } + } + return { + orientation: props.orientation, + size: props.size + } +})) - + + + + diff --git a/src/runtime/components/Input.vue b/src/runtime/components/Input.vue index 625af68958..ee469b0ab2 100644 --- a/src/runtime/components/Input.vue +++ b/src/runtime/components/Input.vue @@ -89,11 +89,11 @@ const modelValue = useVModel, 'modelValue', 'update:modelValue'>(p const appConfig = useAppConfig() as Input['AppConfig'] -const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField>(props, { deferInputValidation: true }) +const { emitFormBlur, emitFormInput, emitFormChange, size: formFieldSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField>(props, { deferInputValidation: true }) const { orientation, size: fieldGroupSize } = useFieldGroup>(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props) -const inputSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const inputSize = computed(() => fieldGroupSize.value || formFieldSize.value) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.input || {}) })({ type: props.type as Input['variants']['type'], diff --git a/src/runtime/components/InputDate.vue b/src/runtime/components/InputDate.vue index 1f65a079e3..165b9bd23f 100644 --- a/src/runtime/components/InputDate.vue +++ b/src/runtime/components/InputDate.vue @@ -90,7 +90,7 @@ const slots = defineSlots() const appConfig = useAppConfig() as InputDate['AppConfig'] const rootProps = useForwardPropsEmits(reactiveOmit(props, 'id', 'name', 'range', 'modelValue', 'defaultValue', 'color', 'variant', 'size', 'highlight', 'disabled', 'autofocus', 'autofocusDelay', 'icon', 'avatar', 'leading', 'leadingIcon', 'trailing', 'trailingIcon', 'loading', 'loadingIcon', 'separatorIcon', 'class', 'ui'), emits) -const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField>(props) +const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formFieldSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField>(props) const { orientation, size: fieldGroupSize } = useFieldGroup>(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props) @@ -100,7 +100,7 @@ const [DefineSegmentsTemplate, ReuseSegmentsTemplate] = createReusableTemplate<{ type?: 'start' | 'end' }>() -const inputSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const inputSize = computed(() => fieldGroupSize.value || formFieldSize.value) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputDate || {}) })({ color: color.value, diff --git a/src/runtime/components/InputMenu.vue b/src/runtime/components/InputMenu.vue index 840ba1d2b1..f5439e0bc5 100644 --- a/src/runtime/components/InputMenu.vue +++ b/src/runtime/components/InputMenu.vue @@ -241,11 +241,11 @@ const virtualizerProps = toRef(() => { }) }) -const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) +const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formFieldSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) const { orientation, size: fieldGroupSize } = useFieldGroup(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown }))) -const inputSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const inputSize = computed(() => fieldGroupSize.value || formFieldSize.value) const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate() const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: InputMenuItem, index: number }>({ diff --git a/src/runtime/components/InputNumber.vue b/src/runtime/components/InputNumber.vue index 80b799a3b9..dcd24e673c 100644 --- a/src/runtime/components/InputNumber.vue +++ b/src/runtime/components/InputNumber.vue @@ -102,10 +102,10 @@ const appConfig = useAppConfig() as InputNumber['AppConfig'] const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultValue', 'min', 'max', 'step', 'stepSnapping', 'formatOptions', 'disableWheelChange', 'invertWheelChange', 'readonly'), emits) -const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size: formGroupSize, name, highlight, disabled, ariaAttrs } = useFormField>(props) +const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size: formFieldSize, name, highlight, disabled, ariaAttrs } = useFormField>(props) const { orientation, size: fieldGroupSize } = useFieldGroup>(props) -const inputSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const inputSize = computed(() => fieldGroupSize.value || formFieldSize.value) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputNumber || {}) })({ color: color.value, diff --git a/src/runtime/components/InputTags.vue b/src/runtime/components/InputTags.vue index 79f88e4159..db7f3058ba 100644 --- a/src/runtime/components/InputTags.vue +++ b/src/runtime/components/InputTags.vue @@ -89,11 +89,11 @@ const appConfig = useAppConfig() as InputTags['AppConfig'] const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'addOnPaste', 'addOnTab', 'addOnBlur', 'duplicate', 'delimiter', 'max', 'convertValue', 'displayValue', 'required'), emits) -const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) +const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formFieldSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) const { orientation, size: fieldGroupSize } = useFieldGroup(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props) -const inputSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const inputSize = computed(() => fieldGroupSize.value || formFieldSize.value) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputTags || {}) })({ color: color.value, diff --git a/src/runtime/components/InputTime.vue b/src/runtime/components/InputTime.vue index a4b56d47c1..b45b72e585 100644 --- a/src/runtime/components/InputTime.vue +++ b/src/runtime/components/InputTime.vue @@ -70,11 +70,11 @@ const appConfig = useAppConfig() as InputTime['AppConfig'] const rootProps = useForwardPropsEmits(reactiveOmit(props, 'id', 'name', 'color', 'variant', 'size', 'highlight', 'disabled', 'autofocus', 'autofocusDelay', 'icon', 'avatar', 'leading', 'leadingIcon', 'trailing', 'trailingIcon', 'loading', 'loadingIcon', 'class', 'ui'), emits) -const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size: formGroupSize, name, highlight, disabled, ariaAttrs } = useFormField(props) +const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size: formFieldSize, name, highlight, disabled, ariaAttrs } = useFormField(props) const { orientation, size: fieldGroupSize } = useFieldGroup(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props) -const inputSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const inputSize = computed(() => fieldGroupSize.value || formFieldSize.value) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputTime || {}) })({ color: color.value, diff --git a/src/runtime/components/Select.vue b/src/runtime/components/Select.vue index e4cc93b80c..c54acfee71 100644 --- a/src/runtime/components/Select.vue +++ b/src/runtime/components/Select.vue @@ -169,11 +169,11 @@ const portalProps = usePortal(toRef(() => props.portal)) const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as SelectContentProps) const arrowProps = toRef(() => props.arrow as SelectArrowProps) -const { emitFormChange, emitFormInput, emitFormBlur, emitFormFocus, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) +const { emitFormChange, emitFormInput, emitFormBlur, emitFormFocus, size: formFieldSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) const { orientation, size: fieldGroupSize } = useFieldGroup(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown }))) -const selectSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const selectSize = computed(() => fieldGroupSize.value || formFieldSize.value) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.select || {}) })({ color: color.value, diff --git a/src/runtime/components/SelectMenu.vue b/src/runtime/components/SelectMenu.vue index 374449c4f2..87a083fd0d 100644 --- a/src/runtime/components/SelectMenu.vue +++ b/src/runtime/components/SelectMenu.vue @@ -235,11 +235,11 @@ const virtualizerProps = toRef(() => { }) const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t('selectMenu.search'), variant: 'none' }) as InputProps) -const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) +const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formFieldSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props) const { orientation, size: fieldGroupSize } = useFieldGroup(props) const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown }))) -const selectSize = computed(() => fieldGroupSize.value || formGroupSize.value) +const selectSize = computed(() => fieldGroupSize.value || formFieldSize.value) const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate() const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: SelectMenuItem, index: number }>({ diff --git a/test/components/FieldGroup.spec.ts b/test/components/FieldGroup.spec.ts index 1b4f02f0b1..880072da34 100644 --- a/test/components/FieldGroup.spec.ts +++ b/test/components/FieldGroup.spec.ts @@ -14,6 +14,7 @@ describe('FieldGroup', () => { // Props ['with as', { props: { as: 'section' } }], ['with class', { props: { class: 'absolute' } }], + ['with clear', { props: { clear: true } }], // Slots ['with default slot', { slots: { diff --git a/test/components/__snapshots__/FieldGroup-vue.spec.ts.snap b/test/components/__snapshots__/FieldGroup-vue.spec.ts.snap index 303a94870f..a62cf43378 100644 --- a/test/components/__snapshots__/FieldGroup-vue.spec.ts.snap +++ b/test/components/__snapshots__/FieldGroup-vue.spec.ts.snap @@ -16,6 +16,8 @@ exports[`FieldGroup > renders with as correctly 1`] = `" renders with class correctly 1`] = `""`; +exports[`FieldGroup > renders with clear correctly 1`] = `""`; + exports[`FieldGroup > renders with default slot correctly 1`] = ` " diff --git a/test/components/__snapshots__/FieldGroup.spec.ts.snap b/test/components/__snapshots__/FieldGroup.spec.ts.snap index 303a94870f..a62cf43378 100644 --- a/test/components/__snapshots__/FieldGroup.spec.ts.snap +++ b/test/components/__snapshots__/FieldGroup.spec.ts.snap @@ -16,6 +16,8 @@ exports[`FieldGroup > renders with as correctly 1`] = `" renders with class correctly 1`] = `""`; +exports[`FieldGroup > renders with clear correctly 1`] = `""`; + exports[`FieldGroup > renders with default slot correctly 1`] = ` "