UNPKG

aio-input

Version:

generate all input types in react j

1,095 lines 172 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { createElement as _createElement } from "react"; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { createRef, useContext, createContext, useState, useEffect, useRef, Fragment } from 'react'; import usePopup from "aio-popup"; import * as UT from 'aio-utils'; import AIODate from 'aio-date'; import { $, successSound, errorSound, BrowserMultiFormatReader } from 'aio-deps'; import { useCanvas } from 'aio-canvas'; import { AIOSlider, AIOSpinner } from 'aio-range'; import { GetSvg } from 'aio-utils'; import './index.css'; const Context = createContext({}); const Provider = ({ value, children }) => { return (_jsx(Context.Provider, { value: value, children: children })); }; const useProvider = () => useContext(Context); const AIOInput = (p) => { const [datauniqid] = useState('aiobutton' + (Math.round(Math.random() * 10000000))); let props = Object.assign({}, p); const { type } = props; if (type === 'text') { if (typeof props.value !== 'string') { props.value = ''; } } else if (type === 'number') { if (typeof props.value !== 'number') { props.value = undefined; } } else if (type === 'panels') { props.deSelect = true; } else if (type === 'tree') { if (props.size === undefined) { props.size = 36; } } if (type === 'file') { if (props.placeholder === undefined && props.text === undefined && props.multiple) { props.text = 'Select Files'; } props.deSelect = props.deSelect !== false ? true : false; } else if (type === 'select' && !props.text && props.multiple) { props.text = 'Select Items'; } else if (type === 'date') { if (props.multiple) { props.text = props.text || `Select Dates`; } } else if (type === 'time') { if (props.multiple) { props.text = props.text || `Select Times`; } } if (type === 'text' && (props.options || props.fetchSuggestions) && !props.isSuggestion) { return _jsx(SuggestionInput, Object.assign({}, props)); } if (type === "custom") { return null; } function getHasPopover() { if (['select', 'date', 'time'].indexOf(type) !== -1) { return true; } if (['text', 'number', 'textarea'].indexOf(type) !== -1 && props.options) { return true; } if (type === 'button' && props.popover) { return true; } return false; } function getTypes() { let { type } = props; return { isMultiple: ['radio', 'range', 'file', 'image', 'buttons', 'select', 'date', 'time', 'panels', 'tabs'].indexOf(type) !== -1, isInput: ['text', 'number', 'textarea', 'password', 'color'].indexOf(type) !== -1, hasOption: ['text', 'number', 'textarea', 'color', 'select', 'radio', 'tabs', 'list', 'buttons', 'tags'].indexOf(type) !== -1, ai_height: ['text', 'number', 'password', 'color', 'select', 'date', 'time', 'checkbox', 'file', 'slider'].indexOf(type) !== -1, hasKeyboard: ['text', 'textarea', 'number', 'password'].indexOf(type) !== -1, hasTags: ['date', 'time', 'select', 'file'].indexOf(type) !== -1 }; } //inja kheili mohemme baraye inke chejoori option ha sakhte beshan type ha dastane khodeshoono daran baraye sakhtan option ha //date time options nadare pas dateDetails ro be onvane options dar nazar migire function getOptions() { if (type === 'date' || type === 'time') { const dateValueDetails = AIActions().getDateValueDetails(props); if (!props.multiple) { return []; } return dateValueDetails.map((o) => (Object.assign({}, o))); } else if (type === 'file') { return AIActions().getFileOptions(props.value); } else if (props.options) { return props.options; } } function getOptionDetails() { const options = getOptions(); if (!options) { return { list: [], dic: {} }; } const multiple = !!types.isMultiple && !!props.multiple; return optionActions().getOptions({ rootProps: props, optionOrgs: options || [], optionProp: props.option || {}, multiple }); } const types = getTypes(); const optionDetails = getOptionDetails(); return _jsx(AIOINPUT, { props: Object.assign({}, props), hasPopover: getHasPopover(), types: types, datauniqid: datauniqid, optionDetails: optionDetails }); }; export default AIOInput; const AIOINPUT = (_props) => { const { props, hasPopover, types, datauniqid, optionDetails } = _props; const propsRef = useRef(props); propsRef.current = props; let { type, value, attrs = {} } = props; const changable = !!props.onChange && props.disabled !== true; let [parentDom] = useState(createRef()); const isInputFocusedRef = useRef(false); const setInputFocused = (v) => isInputFocusedRef.current = v; const mainRef = useRef(createRef()); const modalRef = useRef(createRef()); let popup = usePopup({ rtl: props.rtl }); const keyboardNavHook = useKeyboardNav({ rootProps: props, getOptionDetails: () => optionDetails, getContainer: () => hasPopover ? modalRef.current : mainRef.current }); function getPopover() { let className = 'ai-border-radius-md'; if (types.hasOption) { className += ' aio-input-dropdown'; } const popover = (props.popover || {}); let body = null; if (popover.body) { body = popover.body; } else if (type === 'date' || type === 'time') { body = _jsx(CalendarWraper, { onClose: closePopup }); } else { if (optionDetails.list.length === 0) { return; } body = _jsx(Options, {}); } const modalAttrs = UT.AddToAttrs(Object.assign({}, popover.modalAttrs), { className }); let obj = Object.assign(Object.assign({}, (props.popover || {})), { isPopover: true, rtl: !!props.rtl, focus: !types.hasKeyboard, position: popover.position || 'popover', fitHorizontal: ['text', 'number', 'textarea'].indexOf(type) !== -1 || (type === 'select' && !!props.multiple) || !!popover.fitHorizontal, onClose: () => closePopup(), body, getTarget: () => { const res = $(mainRef.current).children('.aio-input'); return res; }, modalAttrs, ref: modalRef }); return obj; } function closePopup() { popup.removeModal(); setTimeout(() => $(parentDom.current).focus(), 0); } function openPopover() { let open = !!popup.getModals().length; if (open) { return; } const popover = getPopover(); if (!popover) { return; } popup.addModal(popover); } function AIChange(a, b) { if (props.onChange) { props.onChange(a, b); } } const click = (e, optionDetail, isTag, tagRemove) => { var _a, _b; if (optionDetail) { if (isTag) { if (props.disabled === true || optionDetail.disabled === true) { return; } const propsValue = props.value || []; let newValue = []; if (tagRemove) { if (props.type === 'file') { if (props.multiple) { newValue = propsValue.filter((rpv) => rpv.name !== optionDetail.value); } else { newValue = undefined; } } else { newValue = propsValue.filter((rpv) => rpv !== optionDetail.value); } AIChange(newValue, optionDetail); } else if ((_a = optionDetail.tagAttrs) === null || _a === void 0 ? void 0 : _a.onClick) { (_b = optionDetail.tagAttrs) === null || _b === void 0 ? void 0 : _b.onClick(optionDetail); } } else { let { attrs = {}, onClick, close } = optionDetail; if (onClick) { onClick(optionDetail.optionOrg, optionDetail); } else if (attrs.onClick) { attrs.onClick(optionDetail); } else { if (types.isInput) { /*do nothing*/ } else if (type === 'tree') { /*do nothing*/ } else if (type === 'file') { /*do nothing*/ } else if (type === 'toggleButton') { let newIndex = optionDetail.index + 1; if (newIndex > optionDetails.list.length - 1) { newIndex = 0; } const newOptionDetail = optionDetails.list[newIndex]; AIChange(newOptionDetail.value, newOptionDetail); } else if (props.multiple && types.isMultiple) { let { maxLength } = props, newValue; value = value || []; if (value.indexOf(optionDetail.value) === -1) { newValue = value.concat(optionDetail.value); } else { newValue = value.filter((o) => o !== optionDetail.value); } while (!!maxLength && newValue.length > maxLength) { newValue = newValue.slice(1, newValue.length); } AIChange(newValue, optionDetail); } else { if (optionDetail.value !== props.value) { AIChange(optionDetail.value, optionDetail); } else if (props.deSelect) { if (type === 'radio' || type === 'tabs' || type === 'buttons' || type === 'panels') { AIChange(undefined, optionDetail); } } } } if (close) { closePopup(); } } } else { if (type === 'checkbox') { AIChange(!value, e); } else if (hasPopover) { openPopover(); } else if (typeof props.onClick === 'function') { props.onClick(e); } else if (attrs.onClick) { attrs.onClick(); } } }; const render = () => { if (type === 'textlist') { return _jsx(TextList, {}); } if (type === 'toggleButton') { return _jsx(Input_ToggleButton, {}); } if (type === 'panels') { return _jsx(Panels, {}); } if (type === 'tree') { return _jsx(Input_Tree, {}); } if (type === 'list') { return _jsx(List, {}); } if (type === 'image') { return _jsx(Input_Image, {}); } return _jsx(AILayout, {}); }; useEffect(() => { if (props.isSuggestion && isInputFocusedRef.current) { const { options = [] } = props; if (options.length) { openPopover(); } else { closePopup(); } } }, [props.isSuggestion ? props.options : undefined]); if (!type) { return null; } const renderPopup = () => { var _a; if (!hasPopover) { return null; } if ((_a = props === null || props === void 0 ? void 0 : props.popover) === null || _a === void 0 ? void 0 : _a.portal) { return popup.portal(); } return popup.render(); }; const comp = render(); return (_jsxs(Provider, { value: { changable, AIChange, optionDetails, popup, keyboardNavHook, rootProps: Object.assign(Object.assign({}, props), { value }), datauniqid, touch: 'ontouchstart' in document.documentElement, openPopover, hasPopover, click, types, setInputFocused, mainRef }, children: [_jsx("div", { ref: mainRef, style: { display: 'contents' }, children: _jsx(TagsLayout, { children: comp }) }), renderPopup()] })); }; export const AIMask = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'mask' })); export const AITextList = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'textlist' })); export const AIText = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'text' })); export const AINumber = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'number' })); export const AITextarea = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'textarea' })); export const AIPassword = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'password' })); export const AIColor = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'color' })); export const AIToggleButton = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'toggleButton' })); export const AISelect = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'select' })); export const AIRadio = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'radio' })); export const AITabs = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'tabs' })); export const AIButtons = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'buttons' })); export const AITree = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'tree' })); export const AIImage = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'image' })); export const AIFile = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'file' })); export const AICheckbox = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'checkbox' })); export const AIDate = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'date' })); export const AITime = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'time' })); export const AIPanels = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'panels' })); export const AIList = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'list' })); export const AISlider = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'slider' })); export const AISpinner = (props) => _jsx(AIOInput, Object.assign({}, props, { type: 'spinner' })); // #endregion types // #region layout const Before = ({ context, optionDetail, indent, isTag }) => { const { onSwap, type, value, multiple, before, checkIcon, switchOptions } = context.rootProps; const getOptionBefore = () => { let hasContent = false; const content = []; if (onSwap && !!optionDetail) { const d = "M9,3H11V5H9V3M13,3H15V5H13V3M9,7H11V9H9V7M13,7H15V9H13V7M9,11H11V13H9V11M13,11H15V13H13V11M9,15H11V17H9V15M13,15H15V17H13V15M9,19H11V21H9V19M13,19H15V21H13V19Z"; content.push(_jsx("svg", { viewBox: "8 4 10 13", role: "presentation", style: { width: 12, height: '1.8rem' }, children: _jsx("path", { d: d, style: { fill: 'currentcolor' } }) })); hasContent = true; } if (!!indent && indent !== null) { content.push(indent); hasContent = true; } if (!!optionDetail ? typeof optionDetail.checked === 'boolean' : type === 'checkbox') { const checked = !!optionDetail ? optionDetail.checked : !!value; if (switchOptions) { content.push(_jsx(Switch, { value: value, switchOptions: switchOptions })); } else { content.push((_jsx(CheckIcon, { round: !multiple && type === 'radio', checked: checked, checkIcon: optionDetail ? optionDetail.checkIcon : checkIcon }))); } hasContent = true; } const Before = optionDetail ? optionDetail.before : before; if (Before !== undefined) { content.push(Before); hasContent = true; } const fileIconNode = type === 'file' && !multiple && !!value ? _jsx(FileIcon, { file: value }) : null; if (fileIconNode !== null) { content.push(fileIconNode); hasContent = true; } return !hasContent ? false : content; }; const getTagBefore = () => { if (!optionDetail) { return false; } const { tagBefore = new UT.GetSvg('em').getIcon('mdiCircleMedium', 0.7) } = optionDetail; return [_jsx("div", { className: "aio-input-before", children: tagBefore })]; }; const content = isTag ? getTagBefore() : getOptionBefore(); if (content === false) { return null; } return _jsx("div", { className: "aio-input-before", children: content.map((o, i) => _jsx(Fragment, { children: o }, 'layoutbefore' + i)) }); }; const After = ({ context, optionDetail, isTag }) => { const { click, types, hasPopover, rootProps } = context; const { caret, onChange, disabled } = rootProps; const changable = !!onChange && disabled !== true; const hasDeselect = () => { if (rootProps.multiple || !rootProps.deSelect || optionDetail || !changable) { return false; } if (rootProps.value === undefined || rootProps.value === null || rootProps.value === '') { return false; } return ['text', 'number', 'date', 'time', 'select', 'file'].indexOf(rootProps.type) !== -1; }; const hasCaret = () => { if (!hasPopover || optionDetail || (types.isInput && !rootProps.options)) { return false; } return caret !== false; }; const getOptionAfter = () => { const content = []; let hasContent = false; const after = optionDetail ? optionDetail === null || optionDetail === void 0 ? void 0 : optionDetail.after : rootProps.after; if (after !== undefined && after !== null) { content.push(after); hasContent = true; } if (hasDeselect()) { content.push(_jsx(After_deselect, { onClick: () => { if (onChange) { onChange(undefined, undefined); } } })); hasContent = true; } if (hasCaret()) { content.push(_jsx(After_caret, { caret: caret })); hasContent = true; } return hasContent ? content : false; }; const getTagAfter = () => { const content = []; let hasContent = false; const tagAfter = optionDetail ? optionDetail.tagAfter : undefined; if (tagAfter !== undefined) { content.push(tagAfter); hasContent = true; } if (changable) { content.push(_jsx("div", { className: `aio-input-tag-icon aio-input-tag-remove`, onClick: (e) => { e.stopPropagation(); click(e, optionDetail, true, true); }, children: new UT.GetSvg('em').getIcon('mdiClose', 0.7) })); hasContent = true; } return hasContent ? content : false; }; const content = isTag ? getTagAfter() : getOptionAfter(); if (content === false) { return null; } return (_jsxs(_Fragment, { children: [_jsx("div", { style: { flex: 1 } }), _jsx("div", { className: "aio-input-after", children: content.map((o, i) => _jsx(Fragment, { children: o }, 'layoutafter' + i)) })] })); }; const After_deselect = ({ onClick }) => { return (_jsx("div", { className: 'aio-input-deSelect', onClick: (e) => { e.stopPropagation(); e.preventDefault(); onClick(); }, children: new UT.GetSvg('em').getIcon('mdiClose', .8) })); }; const After_caret = ({ caret }) => { return (_jsx("div", { className: 'aio-input-caret', children: caret === undefined ? new UT.GetSvg('em').getIcon('mdiChevronDown', .8) : caret })); }; const Text = ({ optionDetail, isTag, context }) => { const { rootProps, types, hasPopover, optionDetails } = context; const { type } = rootProps; const getText = () => { if (optionDetail) { if (isTag) { return optionDetail.tagText || optionDetail.text; } return optionDetail.text; } else { if (types.isInput) { return _jsx(Input, {}); } if (rootProps.options && types.hasOption && !hasPopover) { return _jsx(Options, {}); } if (type === "mask") { return _jsx(AIMaskComponent, {}); } if (type === "slider") { return _jsx(AIOSlider, Object.assign({}, rootProps)); } if (type === "spinner") { return _jsx(AIOSpinner, Object.assign({}, rootProps)); } if (type === 'date' || type === 'time') { return _jsx("div", { style: { direction: 'ltr', width: 'fit-content' }, children: AIActions().getDateText(rootProps, true) }); } if (type === 'select') { if (rootProps.text !== undefined) { return rootProps.text; } if (!rootProps.multiple) { if (rootProps.value !== null && rootProps.value !== undefined) { const option = optionDetails.dic[`a${rootProps.value}`]; if (option) { return option.text; } } } return null; } if (type === 'file') { if (!!rootProps.value) { try { return rootProps.value.name; } catch (_a) { } } } return rootProps.text; } }; const text = getText(); if (text !== null && text !== undefined) { const subtext = optionDetail ? (isTag ? optionDetail.tagSubtext : optionDetail.subtext) : rootProps.subtext; const justify = optionDetail ? optionDetail.justify : rootProps.justify; const valueClassName = !optionDetail ? `aio-input-${type}-value` : (isTag ? 'aio-input-tag-option-value' : `aio-input-${type}-option-value`); let cls = `aio-input-value`; if (valueClassName) { cls += ` ${valueClassName}`; } const className = `${cls}${justify ? ' aio-input-value-justify' : ''}${!!subtext ? ' aio-input-has-subtext' : ''}`; return _jsx("div", { className: className, "data-subtext": subtext, children: text }); } const placeholder = !optionDetail ? rootProps.placeholder : undefined; if (placeholder !== undefined && placeholder !== null) { return _jsx("div", { className: 'aio-input-placeholder', children: placeholder }); } return null; }; const AILayout = (props) => { const context = useProvider(); const { optionDetail, indent, isTag } = props; const { rootProps } = useProvider(); const attrs = LayoutActions().getAttrs(context, optionDetail, isTag); return (_jsxs("label", Object.assign({}, attrs, { children: [_jsx(Before, { optionDetail: optionDetail, indent: indent, isTag: isTag, context: context }), _jsx(Text, { optionDetail: optionDetail, context: context, isTag: isTag }), _jsx(After, { optionDetail: optionDetail, isTag: isTag, context: context }), rootProps.type === 'file' && _jsx(InputFile, {}), rootProps.type === 'tabs' && _jsx("div", { className: 'aio-input-tabs-option-bar' })] }))); }; const TagsLayout = ({ children }) => { let { rootProps, optionDetails, types } = useProvider(); if (!types.hasTags) { return children; } const { type, value, multiple, hideTags } = rootProps; if (!multiple || hideTags) { return children; } const getTagOptionDetails = () => { if (type === 'select') { let values = Array.isArray(value) ? [...value] : (value !== undefined ? [value] : []); if (!values.length) { return []; } return values.map((o) => optionDetails.dic['a' + o]); } if (type === 'date' || type === 'time' || type === 'file') { return optionDetails.list; } return []; }; const tagOptionDetails = getTagOptionDetails(); if (!tagOptionDetails.length) { return children; } return _jsxs("div", { className: 'aio-input-multiselect-container', children: [children, _jsx(TagOptions, { optionDetails: getTagOptionDetails() })] }); }; const TagOptions = (props) => { let context = useProvider(); const list = props.optionDetails || context.optionDetails.list; if (!list.length) { return null; } let renderOptions = list.map((optionDetail, i) => _jsx(AILayout, { optionDetail: optionDetail, isTag: true }, i)); const attrs = LayoutActions().getTagOptionsAttrs(context); return (_jsx("div", Object.assign({}, attrs, { children: renderOptions }))); }; const Input_Tree = () => { const { rootProps } = useProvider(); return _jsx(AITreeComponent, Object.assign({}, rootProps, { value: rootProps.value || [] })); }; const Input_ToggleButton = () => { const { optionDetails, rootProps } = useProvider(); let option = optionDetails.list.find((o) => o.value === rootProps.value) || optionDetails.list[0]; return !option ? null : _jsx(AILayout, { optionDetail: option }); }; export const CheckIcon = (props) => { if (props.checked === undefined) { return null; } if (Array.isArray(props.checkIcon)) { return _jsx(_Fragment, { children: props.checkIcon[props.checked === false ? 0 : 1] || null }); } return (_jsx("div", { className: 'aio-input-check-out aio-input-active-color' + (props.checked ? ' checked' : '') + (props.round ? ' aio-input-check-round' : ''), style: { background: 'none' }, children: _jsx("div", { className: 'aio-input-active-bg aio-input-check-in' }) })); }; const Switch = ({ value = false, switchOptions = {} }) => { const { rootProps } = useProvider(); const { onChange = () => { } } = rootProps; const { textButton = null, textOff = null, textOn = null, groove, size, width } = switchOptions; const attrs = UT.AddToAttrs(switchOptions.attrs, { className: ['aio-input-switch', !!value ? 'active' : ''], style: { fontSize: size, width }, attrs: { onClick: () => onChange(!value) } }); const buttonAttrs = UT.AddToAttrs(switchOptions.buttonAttrs, { className: ['aio-input-switch-button', !!value ? 'active' : ''] }); return (_jsxs("div", Object.assign({}, attrs, { children: [_jsx("div", { className: "aio-input-switch-off-content", children: _jsx("div", { className: "aio-input-switch-text", children: textOff }) }), _jsx("div", { className: "aio-input-switch-on-content", children: _jsx("div", { className: "aio-input-switch-text", children: textOn }) }), !!groove && _jsx("div", { className: "aio-input-switch-groove" }), _jsx("div", Object.assign({}, buttonAttrs, { children: textButton }))] }))); }; // #endregion layout // #region input const Input = () => { const { rootProps, types, optionDetails, AIChange, changable, setInputFocused } = useProvider(); const { type, delay = 100, showPassword } = rootProps; const { maxLength = Infinity, spin = true } = rootProps; let { filter = [] } = rootProps; let [dom] = useState(createRef()); let [temp] = useState({ atimeout: undefined, btimeout: undefined, clicked: false }); let [datauniqid] = useState(`ac${Math.round(Math.random() * 100000)}`); let [value, setValue] = useState(rootProps.value || ''); let valueRef = useRef(value); const getRef = () => { const { inputAttrs = {} } = rootProps; if (inputAttrs.ref) { return inputAttrs.ref; } return dom; }; valueRef.current = value; function setSwip() { if (type === 'number' && rootProps.swip) { new UT.Swip({ speedY: rootProps.swip * 0.2, reverseY: true, minY: rootProps.min, maxY: rootProps.max, stepY: rootProps.swip, dom: () => { const ref = getRef(); if (ref !== null && ref.current !== null) { const res = $(ref.current); return res; } }, start: () => { let vref = +valueRef.current; vref = isNaN(vref) ? 0 : vref; return [0, vref]; }, move: (p) => { let { y } = p.change || { y: 0 }; if (rootProps.min !== undefined && y < rootProps.min) { y = rootProps.min; } if (rootProps.max !== undefined && y > rootProps.max) { y = rootProps.max; } change(y); } }); } } useEffect(() => { setSwip(); }, []); function getValidValue() { let v = rootProps.value; if (type === 'number') { if (v === '') { return undefined; } //important because +('') is 0 else if (!isNaN(+v)) { v = +v; if (typeof rootProps.min === 'number' && v < rootProps.min) { v = rootProps.min; } else if (typeof rootProps.max === 'number' && v > rootProps.max) { v = rootProps.max; } } } return v; } function update() { clearTimeout(temp.atimeout); temp.atimeout = setTimeout(() => { let v = getValidValue(); if (v !== value) { setValue(v); } }, delay); } useEffect(() => { update(); }, [rootProps.value]); function change(value) { if (!changable) { return; } if (!Array.isArray(filter)) { filter = []; } if (types.hasKeyboard) { value = UT.keyboard_filter(value, { maxLength, filter, toPersian: true }); } if (rootProps.type === 'number') { if (value === '') { value = undefined; } else { value = +value; } } setValue(value); if (!rootProps.blurChange) { clearTimeout(temp.btimeout); temp.btimeout = setTimeout(() => AIChange(value, undefined), delay); } } function click() { if (rootProps.autoHighlight === false) { return; } if (temp.clicked) { return; } temp.clicked = true; $(getRef().current).focus().select(); } function blur() { temp.clicked = false; setInputFocused(false); if (rootProps.blurChange && changable) { AIChange(value, undefined); } } function inputFocus() { setInputFocused(true); } function getInputAttrs() { let InputAttrs = UT.AddToAttrs(rootProps.inputAttrs, { className: !spin ? 'no-spin' : undefined, style: rootProps.justify ? { textAlign: 'center' } : undefined }); let p = Object.assign(Object.assign({}, InputAttrs), { ref: getRef(), value, type, disabled: rootProps.disabled, placeholder: rootProps.placeholder, list: rootProps.options ? datauniqid : undefined, onClick: () => click(), onChange: (e) => change(e.target.value), onBlur: () => blur(), onFocus: () => inputFocus() }); if (type === 'password' && showPassword) { p = Object.assign(Object.assign({}, p), { type: 'text', style: Object.assign(Object.assign({}, p.style), { textAlign: 'center' }) }); } if (filter.length === 1 && filter[0] === 'number') { p.pattern = "\d*"; p.inputMode = "numeric"; } return p; } let attrs = getInputAttrs(); if (type === 'color') { return (_jsxs("label", { style: { width: '100%', height: '100%', background: value }, children: [_jsx("input", Object.assign({}, attrs, { style: { opacity: 0 } })), !!optionDetails.list.length && _jsx("datalist", { id: datauniqid, children: optionDetails.list.map((o) => _jsx("option", { value: o.value })) })] })); } else if (type === 'textarea') { return _jsx("textarea", Object.assign({}, attrs)); } else { return (_jsx("input", Object.assign({}, attrs))); } }; const FormNodeContext = createContext({}); const FormProvider = ({ value, children }) => { return _jsx(FormNodeContext.Provider, { value: value, children: children }); }; const useFormProvider = () => useContext(FormNodeContext); export const useForm = (p) => { const loadingHook = useLoading(); const dataHook = useData(p); const wizardHook = useWizard(p.wizard, dataHook); const errorHook = useError(p, wizardHook, loadingHook); const inputHook = useInput({ dataHook, errorHook, rootProps: p, wizardHook }); const renderHook = useRender({ rootProps: p, dataHook, inputHook, errorHook, wizardHook, loadingHook }); const { errorsDic, errorsList } = errorHook; const { data } = dataHook; const { disabled } = renderHook; const { getChanges } = inputHook; return { render: renderHook, errorsDic, errorsList, getChanges, data, disabled }; }; const useLoading = () => { const [loading, setLoading] = useState(false); return { loading, setLoading }; }; const useData = (rootProps) => { const initDataStringRef = useRef(JSON.stringify(rootProps.data)); const dataRef = useRef(rootProps.data); dataRef.current = rootProps.data; const isDataChanged = initDataStringRef.current === JSON.stringify(rootProps.data); const onChange = (field, value) => { UT.setValueByField(dataRef.current, field, value); rootProps.onChange(Object.assign({}, dataRef.current), { field, value }); }; return { isDataChanged, data: dataRef.current, onChange }; }; const useRender = (p) => { const { inputHook, rootProps, dataHook, errorHook, wizardHook, loadingHook } = p; const { wizard, closeText = 'Close', onSubmit, onClose } = rootProps; const [disabled, setDisabled] = useState(true); const getDisabled = () => loadingHook.loading ? true : !!errorHook.errorsList.length; const changeDisabled = () => setDisabled(getDisabled()); useEffect(() => { changeDisabled(); }, [dataHook.isDataChanged, errorHook.errorsDic, loadingHook.loading]); const nodeHook = useNode(!!rootProps.rtl); const tagsHook = useTags({ inputHook, rootProps, errorHook, dataHook }); const mainNodeRef = useRef(undefined); const submit = () => __awaiter(void 0, void 0, void 0, function* () { if (!rootProps.onSubmit) { return; } loadingHook.setLoading(true); const res = yield rootProps.onSubmit(); loadingHook.setLoading(false); if (res !== false && rootProps.onClose) { rootProps.onClose(); } }); const submitButtonAttrs = () => { if (!rootProps.onSubmit) { return null; } if (rootProps.wizard && !wizardHook.isLastStep(wizardHook.step)) { return null; } const className = `ai-form-submit-button${disabled ? ' disabled' : ''}`; return { className, disabled, onClick: submit, type: 'button' }; }; const submitButton = () => { const attrs = submitButtonAttrs(); if (attrs === null) { return null; } let { submitText = 'Submit' } = rootProps; const text = typeof submitText === 'function' ? submitText(rootProps.data) : submitText; const loader = !loadingHook.loading ? null : new UT.GetSvg().getIcon('mdiLoading', 0.8, { spin: 0.4 }); return (_jsxs("button", Object.assign({}, attrs, { children: [text, loader] }))); }; const wizardStepButtonAttrs = (dir) => { const { changeStep, getNextStep, step } = wizardHook; const { prev = (() => true), next = (() => true) } = step; const fn = dir === 1 ? next : prev; const nextStep = getNextStep(dir); if (nextStep === false) { return null; } const className = `ai-form-wizard-step-button${disabled ? ' disabled' : ''}`; return { type: 'button', className, disabled, onClick: () => __awaiter(void 0, void 0, void 0, function* () { const res = yield fn(); if (res === true) { changeStep(dir); } }) }; }; const wizardStepButton = (dir) => { const attrs = wizardStepButtonAttrs(dir); const { step } = wizardHook; const { nextText = 'Next', prevText = 'Previous' } = step; return attrs === null ? null : _jsx("button", Object.assign({}, attrs, { children: dir === 1 ? nextText : prevText })); }; const wizardStepButtons = () => !wizard ? null : _jsxs(_Fragment, { children: [wizardStepButton(-1), wizardStepButton(1)] }); const provider = (children) => _jsx(FormProvider, { value: { inputHook, errorHook, wizardHook, nodeHook, rootProps }, children: children }); const closeButtonAttrs = () => !onClose ? null : { type: 'button', className: "ai-form-wizard-close-button", onClick: () => rootProps.onClose() }; const closeButton = () => { const attrs = closeButtonAttrs(); return attrs === null ? null : _jsx("button", Object.assign({}, attrs, { children: typeof closeText === 'function' ? closeText(rootProps.data) : closeText })); }; const footer = () => { if (!wizard && !onSubmit && !onClose) { return null; } return provider(_jsxs("div", { className: "ai-form-footer", children: [closeButton(), " ", wizardStepButtons(), " ", submitButton()] })); }; const input = (inputName, p) => { const { attrs = {} } = p || {}; const input = inputHook.getInputByName(inputName, (p === null || p === void 0 ? void 0 : p.nodeName) || ''); if (!input) { return null; } return (provider(_jsx(AIInputNode, { input: input, attrs: attrs, wizardId: '', show: true, inputName: inputName }))); }; const layout = (node) => { mainNodeRef.current = node; return provider(_jsx(AIFormNode, { node: node, level: 0, show: node.show !== false, parentNodes: [] })); }; const tags = (node, disabled) => { return provider(_jsx("div", { className: 'ai-form-tags', children: tagsHook.getTags(node, disabled) })); }; return { input, footer, submitButton, wizardStepButtons, layout, tags, disabled }; }; const useTags = (p) => { const { rootProps, inputHook, errorHook, dataHook } = p; const { fa } = rootProps; const tagsResultRef = useRef([]); const getTag = (param) => { const change = (v) => inputHook.changeByInput({ input, value: v, wizardId: '', inputName: param.inputName }); const getOption = (option) => { let res = { value: undefined, text: undefined }; if (input.option && input.option.value) { res.value = input.option.value(option, {}) || option.value; } else { res.value = option.value; } if (input.option && input.option.text) { res.text = input.option.text(option, {}) || option.text; } else { res.text = option.text; } return res; }; let valuePresent = ''; const { value, input, disabled } = param; if (!input.tag) { return; } let label = input.tag || ''; if ((value === undefined || value === null || value === '') && input.type !== 'checkbox') { return; } if (input.type === 'date') { const { dateUnit = 'day', jalali } = input; if (dateUnit === 'month') { valuePresent = new AIODate().getDateByPattern(value, '{year}/{month}', jalali); } else if (dateUnit === 'day') { valuePresent = new AIODate().getDateByPattern(value, '{year}/{month}/{day}', jalali); } } else if (input.type === 'time') { const { timeUnit = { year: true, month: true, day: true }, jalali } = input; valuePresent = new AIODate().getDateByPattern(value, `{year}/{month}/{day}${timeUnit.hour ? ` - {hour} : ${timeUnit.minute ? '{minute}' : '00'}` : ''}`, jalali); } else if (['radio', 'select', 'buttons', 'toggleButton'].indexOf(input.type) !== -1) { const { options = [] } = input; if (input.multiple) { if (!Array.isArray(value) || !value.length) { return; } valuePresent = options.filter((o) => { const option = getOption(o); return value.indexOf(option.value) !== -1; }).map((o) => getOption(o).text).join(' | '); } else { const res = options.find((o) => value === getOption(o).value); valuePresent = getOption(res).text; } } else if (input.type === 'checkbox') { valuePresent = input.text || input.label; label = (_jsx("div", { className: "ai-tag-checkbox", onClick: () => change(!value), children: _jsx(CheckIcon, { checked: !!value }) })); } else if (typeof value === 'number') { valuePresent = UT.SplitNumber(value); } else { valuePresent = value; } const text = (_jsxs("div", { className: UT.classListToString(['ai-form-tag', disabled ? 'ai-form-tag-disabled' : undefined]), children: [_jsx("div", { className: 'ai-form-tag-label', children: label }), _jsx("div", { className: 'ai-form-tag-value', children: valuePresent }), !disabled && _jsx("div", { className: "ai-form-tag-remove", onClick: (e) => { e.stopPropagation(); inputHook.changeByInput({ input, value: undefined, wizardId: '', inputName: param.inputName }); }, children: new UT.GetSvg('em').getIcon('mdiClose', 0.6) })] }, input.field + param.indexes.join('-'))); if (!disabled) { let props = { caret: false, text, value, rtl: !!fa, className: 'ai-form-tag-input', onChange: change, before: undefined, after: undefined, subtext: undefined, hideTags: true }; if (input.type === 'checkbox') { return text; } if (['radio', 'select', 'buttons', 'toggleButton'].indexOf(input.type) !== -1) { props = Object.assign(Object.assign(Object.assign({}, input), { type: 'select', option: Object.assign(Object.assign({}, input.option), { justify: () => false }), popover: { modalAttrs: { style: { minWidth: '8em' } } } }), props); } else if (input.type === 'date' || input.type === 'time') { props = Object.assign(Object.assign({}, input), props); } else { props = Object.assign(Object.assign({ type: 'button' }, props), { popover: { body: _jsx(AIOInput, Object.assign({}, input, { value: value, onChange: change })), bodyAttrs: { style: { padding: 0, borderRadius: '0.4em', border: 'none' } } } }); } return (_jsx(AIOInput, Object.assign({}, props))); } return text; }; const getTags = (node, disabled) => { tagsResultRef.current = []; getTags_req(node, [], disabled); return tagsResultRef.current; }; const getTags_req = (node, indexes, disabled) => { if (node.input) { const input = inputHook.getInputByName(node.input, node.name || ''); if (!input) { return; } if (!input.tag) { return; } const value = inputHook.getValueByInput(input, node.input); const error = errorHook.getErrorByInput(input, node.input, value, dataHook.data); if (!error) { const res = getTag({ value, input, indexes, disabled, inputName: node.input }); if (res) { tagsResultRef.current.push(res); } } } else { const nodes = node.v || node.h || []; for (let i = 0; i < nodes.length; i++) { getTags_req(nodes[i], [...indexes, i], disabled); } } }; return { getTags }; }; const useInput = (p) => { const { rootProps, errorHook, dataHook, wizardHook } = p; const { option } = rootProps; const initValuesRef = useRef({}); const fieldChangesRef = useRef({}); const getValueByField = (field) => UT.getValueByField(dataHook.data, field); const getValueByInput = (input, inputName) => { const value = getValueByField(input.field); if (!initValuesRef.current[inputName]) { initValuesRef.current[inputName] = { value }; } return value; }; const getChanges = () => Object.keys(fieldChangesRef.current).map((field) => fieldChangesRef.current[field]); const getInputByName = (inputName, nodeName) => { const input = rootProps.inputs[inputName]; if (!input) { return false; } let res = Object.assign({}, input); if (!option) { return res; } for (let prop in option) { if (prop === 'validate') { continue; } const fn = option[prop]; const v = res[prop]; res[prop] = v === undefined ? fn({ inputName, data: dataHook.data, input: res, nodeName }) : v; } return res; }; const changeByInput = (p) => { fieldChangesRef.current = Object.assign(Object.assign({}, fieldChangesRef.current), { [p.inputName]: { from: initValuesRef.current[p.inputName], to: p.value, input: p.input } }); errorHook.updateErrorByInputValue(p); dataHook.onChange(p.input.field, p.value); }; const getInputProps = (p) => { const obj = p.input; const { inputAttrs, field } = obj; const wrappedAttrs = UT.AddToAttrs(p.input.attrs, { className: [p.input.className, !!p.error ? 'has-error' : undefined] }); const inputProps = Object.assign(Object.assign(Object.assign({}, p.input), wrappedAttrs), { inputAttrs: Object.assign(Object.assign({}, inputAttrs), { 'aria-label': field }), value: p.value, rtl: !!p.input.rtl || !!rootProps.fa, onChange: (v) => __awaiter(void 0, void 0, void 0, function* () { return changeByInput({ input: p.input, value: v, wizardId: p.wizardId, inputName: p.inputName }); }) }); if (wizardHook.isDisabledByStep(p.wizardId)) { inputProps.disabled = true; } return inputProps; }; const renderInput = (inputProps) => { if (inputProps.type === "custom") { if (!inputProps.customType) { console.error('useForm error => input.type is set as "custom" but missing set input.customType'); return null; } if (!rootProps.customTypes || !rootProps.customTypes[inputProps.customType]) { console.error(`useForm error => input.customType is set as "${inputProps.customType}" but missing set customTypes.${inputProps.customType} in hook root props`); return null; } else { return rootProps.customTypes[inputProps.customType](Object.assign({}, inputProps)); } } else { return _jsx(AIOInput, Object.assign({}, inputProps)); } }; return { getValueByInput, changeByInput, getInputByName, getChanges, getInputProps, renderInput }; }; const useWizard = (wizard, dataHook) => { const steps = ((wizard === null || wizard === void 0 ? void 0 : wizard.steps) || []); const [step, setStep] = useState(getFirstStep); const stepRef = useRef(step); stepRef.current = step; function getFirstStep() { return steps.find(({ show = () => true }) => !!show(dataHook.data)); } const getLastStep = () => { const list = [...steps]; list.reverse(); return list.find(({ show = () => true })