@fastkit/vue-disabled-reason
Version:
A headless Vue utility that monitors disabled elements and provides a slot or render function to display the reason.
222 lines (218 loc) • 9.24 kB
TypeScript
import * as _fastkit_vue_utils from '@fastkit/vue-utils';
import { VNodeSkipHandler, VNodeChildOrSlot } from '@fastkit/vue-utils';
import * as vue from 'vue';
import { ExtractPropTypes, VNodeArrayChildren, VNodeChild, PropType, VNode } from 'vue';
/**
* CSS layer name used for the disabled reason layer.
* Can be used in CSS `@layer` rules to style the disabled reason layer.
*/
declare const DISABLED_REASON_CSS_LAYER_NAME = "vue-disabled-reason";
/**
* Data attribute set on elements when the disabled reason layer is activated.
* Indicates that the disabled reason is currently displayed.
*/
declare const DISABLED_REASON_ACTIVATED_ATTR = "data-disabled-reason-activated";
/**
* Data attribute used to mark a container element for the disabled reason layer.
* The layer will be rendered inside this container if present.
*/
declare const DISABLED_REASON_CONTAINER_ATTR = "data-disabled-reason-container";
/**
* Object to bind the disabled reason container attribute via v-bind.
* Example usage: `<div v-bind="DISABLED_REASON_CONTAINER_BIND"></div>`
*/
declare const DISABLED_REASON_CONTAINER_BIND: {
"data-disabled-reason-container": string;
};
/**
* List of standard HTML elements that can be disabled via the `disabled` attribute.
*
* DisabledReason component will monitor these elements for disabled state.
*
* Note:
* - The `<a>` tag does not actually support the `disabled` attribute.
* It is included here purely for internal implementation convenience.
*/
declare const DISABLEABLE_NATIVE_ELEMENTS: readonly ["button", "a", "input:not([type=\"hidden\"])", "select", "textarea", "fieldset", "optgroup", "option"];
/**
* List of ARIA roles whose elements can have a disabled state via `aria-disabled`.
*
* DisabledReason component will monitor elements with these roles.
*
* Note:
* - Some roles do not require `aria-disabled` by specification.
* They are included here for internal implementation convenience.
*/
declare const ARIA_DISABLEABLE_ROLES: readonly ["button", "link", "checkbox", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "switch", "tab", "treeitem"];
/**
* CSS selector string that matches all elements monitored by
* DisabledReason for disabled state.
*
* Combines:
* - `DISABLEABLE_NATIVE_ELEMENTS` (HTML elements with `disabled` attribute)
* - `ARIA_DISABLEABLE_ROLES` (elements with ARIA roles supporting `aria-disabled`)
*
* Used internally to locate elements that should be observed for
* disabled state.
*/
declare const DISABLEABLE_ELEMENT_QUERY: string;
type PropsOptionsImpl = Record<string, any>;
/**
* Public API available inside DisabledReason components.
*
* Exposes the component's resolved props and whether
* the target element is currently disabled.
*/
interface DisabledReasonAPI<Props extends Readonly<PropsOptionsImpl> = {}> {
/** Resolved component props. */
get props(): ExtractPropTypes<Props>;
/** True if the wrapped element is disabled. */
get disabled(): boolean;
}
/**
* The setup context passed to a DisabledReason component's `setup` function.
*
* Provides reactive access to:
* - The component's resolved props.
* - The evaluated disabled state of the target element.
*
* Use this context inside `setup` to determine whether the target
* element is disabled and to render the disabled reason accordingly.
*
* @example
* ```ts
* defineDisabledReasonComponent({
* setup(api) {
* return (reason) => (
* <MyTooltip disabled={!reason || !api.disabled}>
* {{ reason }}
* </MyTooltip>
* );
* },
* });
* ```
*/
interface DisabledReasonSetupContext<Props extends Readonly<PropsOptionsImpl> = {}> extends DisabledReasonAPI<Props> {
}
/**
* Render function used to display the disabled reason.
*
* Receives the content provided by the `reason` slot or prop,
* and should return the element (e.g. tooltip, overlay) that
* actually displays the reason to the user.
*/
type DisabledReasonRenderFunction = (reason: VNodeArrayChildren | undefined) => VNodeChild;
/**
* Options for {@link defineDisabledReasonComponent}.
*
* @typeParam Props - Additional props to extend the component.
*/
interface DisabledReasonComponentOptions<Props extends Readonly<PropsOptionsImpl>> {
/** Optional name for the component. */
name?: string;
/** Additional props to extend the base props. */
props?: Props;
/**
* Optional handler to control how the target element is detected.
* Can be used to skip or replace certain VNodes during traversal.
*/
skipVNode?: VNodeSkipHandler;
/**
* Setup function returning a render function for the reason layer.
* This is where you provide the actual UI (e.g. a tooltip).
*/
setup(this: void, ctx: DisabledReasonSetupContext<Props>): DisabledReasonRenderFunction;
}
declare const DISABLED_REASON_BASE_PROPS: {
/** The content or slot that provides the disabled reason */
reason: PropType<VNodeChildOrSlot>;
};
/**
* Creates a headless Vue component that observes whether its
* child element is disabled and displays a "disabled reason"
* when applicable.
*
* This component monitors:
* - Native `disabled` attributes on standard elements (button, input, etc.)
* - Elements with ARIA roles that support disabled state
* (e.g., button, link, checkbox) via the `aria-disabled` attribute
*
* Use the optional `skipVNode` callback to skip or replace
* certain VNodes during the traversal. This allows you to
* ignore wrapper elements or other nodes that should not
* be considered as the target for the disabled reason.
*
* The component itself does not provide any UI. Instead, supply
* a render function in `setup` to display the disabled reason
* (e.g., tooltip, overlay, etc.).
*
* @example
* ```ts
* export const UIDisabledReason = defineDisabledReasonComponent({
* name: 'UIDisabledReason',
* skipVNode: (vnode, el) => el.dataset.skip === 'true',
* setup(api) {
* return (reason) => (
* <MyTooltip
* disabled={!reason || !api.disabled}
* v-slots={{
* activator: ({ attrs }) => <span {...attrs} />,
* default: () => reason,
* }}
* />
* );
* },
* });
* ```
*
* @param options - Component configuration.
* @returns A Vue component definition.
*/
declare function defineDisabledReasonComponent<Props extends Readonly<PropsOptionsImpl> = {}>(options: DisabledReasonComponentOptions<Props>): vue.DefineComponent<vue.ComponentObjectPropsOptions<{
[x: string]: unknown;
}> extends Props & {
'v-slots': PropType<{
default?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
reason?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
}>;
/** The content or slot that provides the disabled reason */
reason: PropType<VNodeChildOrSlot>;
} ? {} : ExtractPropTypes<Props & {
'v-slots': PropType<{
default?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
reason?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
}>;
/** The content or slot that provides the disabled reason */
reason: PropType<VNodeChildOrSlot>;
}>, () => (string | number | boolean | void | VNodeArrayChildren | VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}> | null)[], {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ComponentObjectPropsOptions<{
[x: string]: unknown;
}> extends Props & {
'v-slots': PropType<{
default?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
reason?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
}>;
/** The content or slot that provides the disabled reason */
reason: PropType<VNodeChildOrSlot>;
} ? {} : ExtractPropTypes<Props & {
'v-slots': PropType<{
default?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
reason?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
}>;
/** The content or slot that provides the disabled reason */
reason: PropType<VNodeChildOrSlot>;
}>> & Readonly<{}>, vue.ExtractDefaultPropTypes<Props & {
'v-slots': PropType<{
default?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
reason?: ((api: DisabledReasonAPI<{}>) => VNodeChild) | undefined;
}>;
/** The content or slot that provides the disabled reason */
reason: PropType<VNodeChildOrSlot>;
}>, _fastkit_vue_utils.DefinedSlots<{
/** The main content that will be observed for disabled state */
default?: (api: DisabledReasonAPI) => any;
/** Slot for providing the disabled reason content */
reason?: (api: DisabledReasonAPI) => any;
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
export { ARIA_DISABLEABLE_ROLES, DISABLEABLE_ELEMENT_QUERY, DISABLEABLE_NATIVE_ELEMENTS, DISABLED_REASON_ACTIVATED_ATTR, DISABLED_REASON_BASE_PROPS, DISABLED_REASON_CONTAINER_ATTR, DISABLED_REASON_CONTAINER_BIND, DISABLED_REASON_CSS_LAYER_NAME, type DisabledReasonAPI, type DisabledReasonComponentOptions, type DisabledReasonRenderFunction, type DisabledReasonSetupContext, defineDisabledReasonComponent };