aio-input
Version:
generate all input types in react j[A[C[B[A[D[D[B[A[C[F[C[C[C[C[C[C[C[C[C[C[C[C[C[C[B[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C[C
1,095 lines • 172 kB
JavaScript
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 })