UNPKG

ui-ingredients

Version:

Headless component library for Svelte powered by zag

167 lines (166 loc) 5.59 kB
import { ariaAttr, dataAttr, getDocument, getWindow } from '@zag-js/dom-query'; import { getEnvironmentContext } from '../EnvironmentProvider/EnvironmentProviderContext.svelte.js'; import { getLocaleContext } from '../LocaleProvider/LocaleProviderContext.svelte.js'; import { parts } from './Field.anatomy.js'; export function createField(props) { const locale = getLocaleContext(); const environment = getEnvironmentContext(); const { /**/ ids, invalid, disabled, required, readOnly, } = $derived.by(() => { const id = props().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`, requiredIndicator: props().ids?.requiredIndicator ?? `field:${id}:required-indicator`, }; 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, dir: locale?.().dir, 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, dir: locale?.().dir, 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, id: ids.requiredIndicator, dir: locale?.().dir, 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, dir: locale?.().dir, 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, dir: locale?.().dir, 'data-invalid': dataAttr(invalid), 'data-disabled': dataAttr(disabled), 'data-required': dataAttr(required), 'data-readonly': dataAttr(readOnly), }; } function getControlProps() { return { id: ids.control, dir: locale?.().dir, 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 () => ({ ids, disabled, required, readOnly, invalid, 'aria-describedby': ariaDescribedby, getRootProps, getLabelProps, getErrorTextProps, getHelperTextProps, getInputProps, getSelectProps, getTextareaProps, getRequiredIndicatorProps, }); }