UNPKG

@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
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 };