UNPKG

ui-ingredients

Version:

Headless component library for Svelte powered by zag

160 lines (159 loc) 5.26 kB
import { ariaAttr, dataAttr, getDocument, getWindow } from '@zag-js/dom-query'; import { reflect } from '@zag-js/svelte'; import { createUniqueId } from '../create-unique-id.js'; import { getEnvironmentContext } from '../environment-provider/enviroment-provider-context.svelte.js'; import { parts } from './field-anatomy.js'; export function createField(props) { const environment = getEnvironmentContext(); const id_ = createUniqueId(); const { /**/ ids, invalid, disabled, required, readOnly, } = $derived.by(() => { const id = props.id ?? id_; const ids = { root: props.ids?.root ?? `field:${id}`, label: props.ids?.label ?? `field:${id}:label`, control: props.ids?.control ?? `field:${id}:control`, errorText: props.ids?.errorText ?? `field:${id}:error-text`, helperText: props.ids?.helperText ?? `field:${id}:helper-text`, }; return { ids, invalid: props.invalid ?? false, required: props.required ?? false, disabled: props.disabled ?? false, readOnly: props.readOnly ?? false, }; }); let hasErrorText = $state(false); let hasHelperText = $state(false); const ariaDescribedby = $derived.by(() => { const l = []; if (hasErrorText) { l.push(ids.errorText); } if (hasHelperText) { l.push(ids.helperText); } return l.join(' '); }); function getRootProps() { return { ...parts.root.attrs, id: ids.root, role: 'group', 'data-invalid': dataAttr(invalid), 'data-disabled': dataAttr(disabled), 'data-required': dataAttr(required), 'data-readonly': dataAttr(readOnly), }; } function getLabelProps() { return { ...parts.label.attrs, id: ids.label, for: ids.control, 'data-invalid': dataAttr(invalid), 'data-disabled': dataAttr(disabled), 'data-required': dataAttr(required), 'data-readonly': dataAttr(readOnly), }; } function getRequiredIndicatorProps() { return { ...parts.requiredIndicator.attrs, hidden: !required, 'aria-hidden': true, 'data-invalid': dataAttr(invalid), 'data-disabled': dataAttr(disabled), 'data-readonly': dataAttr(readOnly), }; } function getErrorTextProps() { return { ...parts.errorText.attrs, id: ids.errorText, hidden: !hasErrorText, 'aria-live': 'polite', 'data-invalid': dataAttr(invalid), 'data-disabled': dataAttr(disabled), 'data-required': dataAttr(required), 'data-readonly': dataAttr(readOnly), }; } function getHelperTextProps() { return { ...parts.helperText.attrs, id: ids.helperText, 'data-invalid': dataAttr(invalid), 'data-disabled': dataAttr(disabled), 'data-required': dataAttr(required), 'data-readonly': dataAttr(readOnly), }; } function getControlProps() { return { id: ids.control, disabled, required, 'aria-describedby': ariaDescribedby, 'aria-invalid': ariaAttr(invalid), 'aria-disabled': ariaAttr(disabled), 'aria-required': ariaAttr(required), 'aria-readonly': ariaAttr(readOnly), 'data-invalid': dataAttr(invalid), 'data-disabled': dataAttr(disabled), 'data-required': dataAttr(required), 'data-readonly': dataAttr(readOnly), }; } function getInputProps() { return { readonly: readOnly, ...getControlProps(), ...parts.input.attrs, }; } function getTextareaProps() { return { readonly: readOnly, ...getControlProps(), ...parts.textarea.attrs, }; } function getSelectProps() { return { ...getControlProps(), ...parts.select.attrs, }; } $effect(() => { const rootNode = environment?.getRootNode() ?? document; const doc = getDocument(rootNode); const win = getWindow(rootNode); function handler() { hasErrorText = invalid && doc.getElementById(ids.errorText) !== null; hasHelperText = doc.getElementById(ids.helperText) !== null; } handler(); const observer = new win.MutationObserver(handler); observer.observe(rootNode, { childList: true, subtree: true }); return () => observer.disconnect(); }); return reflect(() => ({ ids, disabled, required, readOnly, invalid, 'aria-describedby': ariaDescribedby, getRootProps, getLabelProps, getErrorTextProps, getHelperTextProps, getInputProps, getSelectProps, getTextareaProps, getRequiredIndicatorProps, })); }