import { createComponentOptionsManager, setMaybeRefValue, isObject, hasOwnProperty, hasNormalizedSlot, normalizeSlot, unrefWithDefault, isPromise, installStoreManager, applyStoreManagerOptions } from '@vuecs/core';
import { isReactive, h, defineComponent, unref, mergeProps } from 'vue';

function buildFormBaseOptions(options, component, defaults) {
    defaults = defaults || {};
    const manager = createComponentOptionsManager({
        name: component
    });
    return {
        ...options,
        slotItems: options.slotItems || {},
        class: manager.buildOrFail({
            key: 'class',
            value: options.class,
            alt: defaults.class || ''
        }),
        props: manager.buildOrFail({
            key: 'props',
            value: options.props,
            alt: defaults.props || {}
        })
    };
}
function handleFormValueChanged(options, value) {
    if (typeof options.value !== 'undefined') {
        if (isReactive(options.value)) {
            options.value = value;
        } else {
            setMaybeRefValue(options.value, value);
        }
    }
    if (options.onChange) {
        options.onChange.call(null, value);
    }
    return options;
}

var Component;
(function(Component) {
    Component["FormGroup"] = "formGroup";
    Component["FormInput"] = "formInput";
    Component["FormInputCheckbox"] = "formInputCheckbox";
    Component["FormInputText"] = "formInputText";
    Component["FormSelect"] = "formSelect";
    Component["FormSubmit"] = "formSubmit";
    Component["FormTextarea"] = "formTextarea";
})(Component || (Component = {}));
var SlotName;
(function(SlotName) {
    SlotName["DEFAULT"] = "default";
    SlotName["LABEL"] = "label";
    SlotName["HINT"] = "hint";
    SlotName["VALIDATION_GROUP"] = "validationGroup";
    SlotName["VALIDATION_ITEM"] = "validationItem";
})(SlotName || (SlotName = {}));

function isValidationRuleResultWithoutParams(input) {
    return isObject(input) && hasOwnProperty(input, '$message') && hasOwnProperty(input, '$pending') && hasOwnProperty(input, '$invalid') && hasOwnProperty(input, '$response');
}
function isValidationRuleResultWithParams(input) {
    return isValidationRuleResultWithoutParams(input) && hasOwnProperty(input, '$params');
}
function template(str, data, regex = /\{\{(.+?)\}\}/g) {
    return Array.from(str.matchAll(regex)).reduce((acc, match)=>{
        if (typeof data[match[1]] !== 'undefined') {
            return acc.replace(match[0], data[match[1]]);
        }
        return acc;
    }, str);
}

function buildValidationGroupOptions(options) {
    return {
        ...options,
        slotItems: options.slotItems || {},
        validationMessages: options.validationMessages || {},
        validationResult: options.validationResult || {}
    };
}
function buildValidationGroup(input) {
    const options = buildValidationGroupOptions(input);
    const translate = (validator, properties)=>{
        if (options.validationMessages && Object.prototype.hasOwnProperty.call(options.validationMessages, validator)) {
            return template(options.validationMessages[validator], properties || {});
        }
        if (typeof options.validationTranslator !== 'undefined') {
            let translation = options.validationTranslator(validator, properties || {});
            if (typeof translation === 'string') {
                translation = template(translation, properties || {});
                if (translation !== validator) {
                    return translation;
                }
            }
        }
        return `The ${validator} operator condition is not fulfilled.`;
    };
    const errors = [];
    const keys = Object.keys(options.validationResult);
    for(let i = 0; i < keys.length; i++){
        if (isObject(options.validationResult[keys[i]]) && options.validationResult[keys[i]].$invalid) {
            if (isValidationRuleResultWithParams(options.validationResult[keys[i]])) {
                errors.push(translate(keys[i], options.validationResult[keys[i]].$params));
            } else if (isValidationRuleResultWithoutParams(options.validationResult[keys[i]])) {
                errors.push(translate(keys[i]));
            }
        }
    }
    const invalid = !!options.validationResult && !!options.validationResult.$dirty && !!options.validationResult.$invalid;
    if (hasNormalizedSlot(SlotName.VALIDATION_GROUP, options.slotItems)) {
        return normalizeSlot(SlotName.VALIDATION_GROUP, {
            data: errors,
            invalid
        }, options.slotItems);
    }
    const children = [];
    for(let i = 0; i < errors.length; i++){
        if (hasNormalizedSlot(SlotName.VALIDATION_ITEM, options.slotItems)) {
            children.push(normalizeSlot(SlotName.VALIDATION_ITEM, {
                data: errors[i],
                invalid
            }, options.slotItems));
        } else {
            children.push(h('div', {
                class: 'form-group-hint group-required'
            }, [
                errors[i]
            ]));
        }
    }
    return children;
}

function buildFormGroupOptions(input) {
    const manager = createComponentOptionsManager({
        name: Component.FormGroup
    });
    return {
        ...input,
        slotItems: input.slotItems || {},
        hint: manager.build({
            key: 'hint',
            value: input.hint
        }),
        hintClass: manager.buildOrFail({
            key: 'hintClass',
            value: input.hintClass,
            alt: []
        }),
        hintContent: manager.build({
            key: 'hintContent',
            value: input.hintContent
        }),
        hintTag: manager.buildOrFail({
            key: 'hintTag',
            value: input.hintTag,
            alt: 'div'
        }),
        class: manager.buildOrFail({
            key: 'class',
            value: input.class,
            alt: []
        }),
        props: manager.buildOrFail({
            key: 'props',
            value: input.props,
            alt: {}
        }),
        label: manager.build({
            key: 'label',
            value: input.label
        }),
        labelClass: manager.buildOrFail({
            key: 'labelClass',
            value: input.labelClass,
            alt: []
        }),
        labelContent: manager.build({
            key: 'labelContent',
            value: input.labelContent
        }),
        labelTag: manager.buildOrFail({
            key: 'labelTag',
            value: input.labelTag,
            alt: 'label'
        }),
        validation: manager.buildOrFail({
            key: 'validation',
            value: input.validation,
            alt: true
        }),
        validationMessages: input.validationMessages || {},
        validationResult: input.validationResult || {},
        // errorClass
        validationErrorClass: manager.buildOrFail({
            key: 'validationErrorClass',
            value: input.validationErrorClass,
            alt: []
        }),
        // warningClass
        validationWarningClass: manager.buildOrFail({
            key: 'validationWarningClass',
            value: input.validationWarningClass,
            alt: []
        })
    };
}
function buildFormGroup(input) {
    const options = buildFormGroupOptions(input);
    const children = [];
    let label;
    if (typeof options.label === 'boolean') {
        label = options.label;
    } else {
        label = !!options.labelContent || hasNormalizedSlot(SlotName.LABEL, options.slotItems);
    }
    if (label) {
        if (hasNormalizedSlot(SlotName.LABEL, options.slotItems)) {
            children.push(h(options.labelTag, {
                class: options.labelClass
            }, [
                normalizeSlot(SlotName.LABEL, {}, options.slotItems)
            ]));
        } else if (options.labelContent) {
            children.push(h(options.labelTag, {
                class: options.labelClass
            }, [
                options.labelContent
            ]));
        }
    }
    if (hasNormalizedSlot(SlotName.DEFAULT, options.slotItems)) {
        children.push(normalizeSlot(SlotName.DEFAULT, {}, options.slotItems));
    } else if (options.content) {
        children.push(options.content);
    }
    if (options.validation) {
        children.push(buildValidationGroup({
            slotItems: options.slotItems,
            validationResult: options.validationResult,
            validationMessages: options.validationMessages,
            validationTranslator: options.validationTranslator
        }));
    }
    let hint;
    if (typeof options.hint === 'boolean') {
        hint = options.hint;
    } else {
        hint = !!options.hintContent || hasNormalizedSlot(SlotName.HINT, options.slotItems);
    }
    if (hint) {
        if (hasNormalizedSlot(SlotName.HINT, options.slotItems)) {
            children.push(h(options.hintTag, {
                class: options.hintClass
            }, [
                normalizeSlot(SlotName.HINT, {}, options.slotItems)
            ]));
        } else if (options.hintContent) {
            children.push(h(options.hintTag, {
                class: options.hintClass
            }, [
                options.hintContent
            ]));
        }
    }
    return h('div', {
        class: [
            options.class,
            ...options.validationResult && options.validationResult.$invalid && options.validationResult.$dirty ? [
                options.validationErrorClass
            ] : [],
            ...options.validationResult && options.validationResult.$invalid && !options.validationResult.$dirty ? [
                options.validationWarningClass
            ] : []
        ],
        ...options.props
    }, children);
}

const VCFormGroup = defineComponent({
    slots: Object,
    props: {
        label: {
            type: Boolean,
            default: undefined
        },
        labelClass: {
            type: String
        },
        labelContent: {
            type: String
        },
        hint: {
            type: Boolean,
            default: undefined
        },
        hintClass: {
            type: String
        },
        hintContent: {
            type: String
        },
        validation: {
            type: Boolean,
            default: undefined
        },
        validationResult: {
            type: Object,
            default: undefined
        },
        validationMessages: {
            type: Object,
            default: undefined
        },
        validationTranslator: {
            type: Function,
            default: undefined
        }
    },
    setup (props, { attrs, slots }) {
        return ()=>buildFormGroup({
                label: props.label,
                labelClass: props.labelClass,
                labelContent: props.labelContent,
                hint: props.hint,
                hintClass: props.hintClass,
                hintContent: props.hintContent,
                validation: props.validation,
                validationResult: props.validationResult,
                validationMessages: props.validationMessages,
                validationTranslator: props.validationTranslator,
                slotItems: slots,
                props: attrs
            });
    }
});

function buildFormInputOptions(input, component) {
    component = component || Component.FormInput;
    const options = buildFormBaseOptions(input, component);
    const manager = createComponentOptionsManager({
        name: component
    });
    return {
        ...options,
        type: manager.buildOrFail({
            key: 'type',
            value: options.type,
            alt: 'text'
        }),
        group: manager.buildOrFail({
            key: 'group',
            value: options.group,
            alt: false
        }),
        groupClass: manager.buildOrFail({
            key: 'groupClass',
            value: options.groupClass,
            alt: []
        }),
        groupAppend: manager.buildOrFail({
            key: 'groupAppend',
            value: options.groupAppend,
            alt: false
        }),
        groupAppendClass: manager.build({
            key: 'groupAppendClass',
            value: options.groupAppendClass,
            alt: ''
        }),
        groupAppendContent: manager.build({
            key: 'groupAppendContent',
            value: options.groupAppendContent,
            alt: ''
        }),
        groupPrepend: manager.buildOrFail({
            key: 'groupPrepend',
            value: options.groupPrepend,
            alt: false
        }),
        groupPrependClass: manager.build({
            key: 'groupPrependClass',
            value: options.groupPrependClass,
            alt: ''
        }),
        groupPrependContent: manager.build({
            key: 'groupPrependContent',
            value: options.groupPrependContent,
            alt: ''
        })
    };
}
function buildFormInput(input) {
    const options = buildFormInputOptions(input);
    return buildFormInputFromOptions(options);
}
function buildFormInputFromOptions(options) {
    const children = [];
    if (options.groupPrepend) {
        children.push(h('span', {
            class: options.groupPrependClass
        }, [
            options.groupPrependContent
        ]));
    }
    const rawValue = unref(options.value);
    children.push(h('input', mergeProps({
        type: options.type,
        class: options.class,
        onInput ($event) {
            if ($event.target.composing) {
                return;
            }
            handleFormValueChanged(options, $event.target.value);
        },
        ...typeof rawValue !== 'undefined' ? {
            value: rawValue
        } : {}
    }, options.props)));
    if (options.groupAppend) {
        children.push(h('span', {
            class: options.groupAppendClass
        }, [
            options.groupAppendContent
        ]));
    }
    if (children.length > 1 || options.group) {
        return h('div', {
            class: options.groupClass
        }, children);
    }
    return children[0];
}

const VCFormInput = defineComponent({
    props: {
        modelValue: {
            type: String,
            default: ''
        },
        type: {
            type: String,
            default: 'text'
        }
    },
    emits: [
        'update:modelValue'
    ],
    setup (props, { attrs, emit }) {
        return ()=>buildFormInput({
                type: props.type,
                value: props.modelValue,
                onChange (input) {
                    emit('update:modelValue', input);
                },
                props: attrs
            });
    }
});

function buildFormInputCheckboxOptions(input) {
    const options = buildFormBaseOptions(input, Component.FormInputCheckbox);
    const manager = createComponentOptionsManager({
        name: Component.FormInputCheckbox
    });
    return {
        ...options,
        group: manager.buildOrFail({
            key: 'group',
            value: options.group,
            alt: false
        }),
        groupClass: manager.buildOrFail({
            key: 'groupClass',
            value: unref(options.groupClass),
            alt: ''
        }),
        label: manager.buildOrFail({
            key: 'label',
            value: options.label,
            alt: true
        }),
        labelClass: manager.buildOrFail({
            key: 'labelClass',
            value: options.labelClass,
            alt: []
        }),
        labelContent: manager.buildOrFail({
            key: 'labelContent',
            value: options.labelContent,
            alt: 'Input'
        })
    };
}
function buildFormInputCheckbox(input) {
    const options = buildFormInputCheckboxOptions(input);
    const id = (Math.random() + 1).toString(36).substring(7);
    const rawValue = unref(options.value);
    let isChecked;
    let index = -1;
    if (typeof rawValue !== 'undefined') {
        if (Array.isArray(rawValue)) {
            if (typeof options.props.value !== 'undefined') {
                index = rawValue.indexOf(options.props.value);
                isChecked = index !== -1;
            }
        } else {
            isChecked = !!rawValue;
        }
    }
    const element = h('input', mergeProps({
        id,
        type: 'checkbox',
        class: options.class,
        onInput ($event) {
            if ($event.target.composing) {
                return;
            }
            if (Array.isArray(rawValue)) {
                if (typeof options.props.value !== 'undefined') {
                    if (index === -1) {
                        rawValue.push(options.props.value);
                    } else {
                        rawValue.splice(index, 1);
                    }
                }
                handleFormValueChanged(options, rawValue);
            } else {
                handleFormValueChanged(options, !rawValue);
            }
        },
        ...typeof isChecked === 'boolean' ? {
            checked: isChecked
        } : {}
    }, options.props));
    if (options.label || options.group) {
        const children = [];
        if (options.label) {
            if (hasNormalizedSlot(SlotName.LABEL, options.slotItems)) {
                children.push(normalizeSlot(SlotName.LABEL, {
                    class: options.class,
                    id
                }, options.slotItems));
            } else {
                children.push(h('label', {
                    class: options.labelClass,
                    for: id
                }, [
                    options.labelContent
                ]));
            }
        }
        children.push(element);
        return h('div', {
            class: options.groupClass
        }, children);
    }
    return element;
}

const VCFormInputCheckbox = defineComponent({
    slots: Object,
    props: {
        modelValue: {
            type: [
                Object,
                Boolean,
                String,
                Number,
                Array
            ]
        },
        group: {
            type: Boolean
        },
        groupClass: {
            type: String
        },
        label: {
            type: Boolean
        },
        labelClass: {
            type: String
        },
        labelContent: {
            type: String
        }
    },
    emits: [
        'update:modelValue'
    ],
    setup (props, { attrs, emit, slots }) {
        return ()=>buildFormInputCheckbox({
                group: props.group,
                groupClass: props.groupClass,
                label: props.label,
                labelClass: props.labelClass,
                labelContent: props.labelContent,
                value: props.modelValue,
                onChange (input) {
                    emit('update:modelValue', input);
                },
                slotItems: slots,
                props: attrs
            });
    }
});

function buildFormInputText(input) {
    const options = buildFormInputOptions(input, Component.FormInputText);
    return buildFormInputFromOptions(options);
}

function buildFormSelectOptions(input) {
    const options = buildFormBaseOptions(input, Component.FormSelect);
    const manager = createComponentOptionsManager({
        name: Component.FormSelect
    });
    return {
        ...options,
        options: options.options,
        optionDefault: manager.buildOrFail({
            key: 'optionDefault',
            value: options.optionDefault,
            alt: true
        }),
        optionDefaultId: manager.buildOrFail({
            key: 'optionDefaultId',
            value: options.optionDefaultId,
            alt: ''
        }),
        optionDefaultValue: manager.buildOrFail({
            key: 'optionDefaultValue',
            value: options.optionDefaultValue,
            alt: '-- Select --'
        })
    };
}
function buildFormSelect(input) {
    const options = buildFormSelectOptions(input);
    const rawValue = unref(options.value);
    const children = [];
    if (options.optionDefault) {
        children.push(h('option', {
            value: options.optionDefaultId
        }, [
            options.optionDefaultValue
        ]));
    }
    for(let i = 0; i < options.options.length; i++){
        children.push(h('option', {
            key: options.options[i].id,
            value: options.options[i].id,
            selected: options.options[i].id === rawValue
        }, options.options[i].value));
    }
    return h('select', mergeProps({
        class: options.class,
        onChange ($event) {
            const $$selectedVal = Array.prototype.filter.call($event.target.options, (o)=>o.selected).map((o)=>'_value' in o ? o._value : o.value);
            const value = $event.target.multiple ? $$selectedVal : $$selectedVal[0];
            handleFormValueChanged(options, value);
        },
        ...typeof rawValue !== 'undefined' ? {
            value: rawValue
        } : {}
    }, options.props), children);
}

const VCFormSelect = defineComponent({
    props: {
        modelValue: {
            type: String,
            default: ''
        },
        options: {
            type: Object,
            required: true
        },
        optionDefault: {
            type: Boolean,
            default: true
        },
        optionDefaultId: {
            type: [
                String,
                Number
            ]
        },
        optionDefaultValue: {
            type: String
        }
    },
    emits: [
        'update:modelValue'
    ],
    setup (props, { attrs, emit }) {
        return ()=>buildFormSelect({
                value: props.modelValue,
                onChange (input) {
                    emit('update:modelValue', input);
                },
                props: attrs,
                options: props.options,
                optionDefault: props.optionDefault,
                optionDefaultId: props.optionDefaultId,
                optionDefaultValue: props.optionDefaultValue
            });
    }
});

function buildFormSubmitOptions(options) {
    const manager = createComponentOptionsManager({
        name: Component.FormSubmit
    });
    return {
        ...options,
        type: manager.buildOrFail({
            key: 'type',
            value: options.type,
            alt: 'button'
        }),
        class: manager.buildOrFail({
            key: 'class',
            value: options.class,
            alt: []
        }),
        props: manager.buildOrFail({
            key: 'props',
            value: options.props,
            alt: {}
        }),
        icon: manager.buildOrFail({
            key: 'icon',
            value: options.icon,
            alt: true
        }),
        validationResult: unrefWithDefault(options.validationResult, {}),
        updateText: manager.buildOrFail({
            key: 'updateText',
            value: options.updateText,
            alt: 'Update'
        }),
        updateIconClass: manager.buildOrFail({
            key: 'updateIconClass',
            value: options.updateIconClass,
            alt: []
        }),
        updateButtonClass: manager.buildOrFail({
            key: 'updateButtonClass',
            value: options.updateButtonClass,
            alt: []
        }),
        createText: manager.buildOrFail({
            key: 'createText',
            value: options.createText,
            alt: 'Create'
        }),
        createIconClass: manager.buildOrFail({
            key: 'createIconClass',
            value: options.createIconClass,
            alt: []
        }),
        createButtonClass: manager.buildOrFail({
            key: 'createButtonClass',
            value: options.createButtonClass,
            alt: []
        }),
        busy: options.busy ?? false,
        valid: options.valid ?? true,
        isEditing: options.isEditing ?? false
    };
}
function buildFormSubmit(input) {
    const options = buildFormSubmitOptions(input);
    let icon = [];
    if (options.icon) {
        icon = [
            h('i', {
                class: [
                    ...options.isEditing ? [
                        options.updateIconClass
                    ] : [],
                    ...!options.isEditing ? [
                        options.createIconClass
                    ] : []
                ]
            })
        ];
    }
    return h(options.type, mergeProps({
        ...options.type === 'button' ? {
            type: 'submit'
        } : {},
        class: [
            ...options.isEditing ? [
                options.updateButtonClass
            ] : [],
            ...!options.isEditing ? [
                options.createButtonClass
            ] : []
        ],
        disabled: options.validationResult.$invalid || !options.valid || unref(options.busy),
        onClick ($event) {
            $event.preventDefault();
            setMaybeRefValue(options.busy, true);
            const promise = options.submit();
            if (isPromise(promise)) {
                promise.finally(()=>{
                    setMaybeRefValue(options.busy, false);
                });
            }
            return promise;
        }
    }, options.props), [
        icon,
        ' ',
        options.isEditing ? options.updateText : options.createText
    ]);
}

const VCFormSubmit = defineComponent({
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        icon: {
            type: Boolean,
            default: true
        },
        isEditing: {
            type: Boolean,
            default: false
        },
        busy: {
            type: Boolean,
            default: false
        },
        createText: {
            type: [
                String,
                Object
            ],
            default: undefined
        },
        createIconClass: {
            type: [
                String,
                Object
            ],
            default: undefined
        },
        createButtonClass: {
            type: [
                String,
                Object
            ],
            default: undefined
        },
        updateText: {
            type: [
                String,
                Object
            ],
            default: undefined
        },
        updateIconClass: {
            type: [
                String,
                Object
            ],
            default: undefined
        },
        updateButtonClass: {
            type: [
                String,
                Object
            ],
            default: undefined
        },
        submit: {
            type: Function
        },
        validationResult: {
            type: Object,
            default: undefined
        }
    },
    emits: [
        'update:modelValue'
    ],
    setup (props, { attrs, emit }) {
        return ()=>buildFormSubmit({
                icon: props.icon,
                busy: props.modelValue || props.busy,
                isEditing: props.isEditing,
                createText: props.createText,
                createIconClass: props.createIconClass,
                createButtonClass: props.createButtonClass,
                updateText: props.updateText,
                updateIconClass: props.updateIconClass,
                updateButtonClass: props.updateButtonClass,
                submit () {
                    if (!props.submit) {
                        return;
                    }
                    emit('update:modelValue', true);
                    Promise.resolve().then(props.submit).then(()=>{
                        emit('update:modelValue', false);
                    });
                },
                validationResult: props.validationResult,
                props: attrs
            });
    }
});

function buildFormTextareaOptions(input) {
    const options = buildFormBaseOptions(input, Component.FormTextarea);
    return {
        ...options
    };
}
function buildFormTextarea(input) {
    const options = buildFormTextareaOptions(input);
    const rawValue = unref(options.value);
    return h('textarea', mergeProps({
        placeholder: '...',
        class: options.class,
        onInput ($event) {
            if ($event.target.composing) {
                return;
            }
            handleFormValueChanged(options, $event.target.value);
        },
        ...typeof rawValue !== 'undefined' ? {
            value: rawValue
        } : {}
    }, options.props));
}

const VCFormTextarea = defineComponent({
    props: {
        modelValue: {
            type: String,
            default: ''
        }
    },
    emits: [
        'update:modelValue'
    ],
    setup (props, { attrs, emit }) {
        return ()=>buildFormTextarea({
                value: props.modelValue,
                onChange (input) {
                    emit('update:modelValue', input);
                },
                props: attrs
            });
    }
});

function install(instance, options = {}) {
    const storeManager = installStoreManager(instance);
    if (options.storeManager) {
        applyStoreManagerOptions(storeManager, options.storeManager);
    }
    Object.entries({
        VCFormGroup,
        VCFormInputCheckbox,
        VCFormInput,
        VCFormSelect,
        VCFormSubmit,
        VCFormTextarea
    }).forEach(([componentName, component])=>{
        instance.component(componentName, component);
    });
}
var index = {
    install
};

export { Component, SlotName, VCFormGroup, VCFormInput, VCFormInputCheckbox, VCFormSelect, VCFormSubmit, VCFormTextarea, buildFormBaseOptions, buildFormGroup, buildFormGroupOptions, buildFormInput, buildFormInputCheckbox, buildFormInputCheckboxOptions, buildFormInputFromOptions, buildFormInputOptions, buildFormInputText, buildFormSelect, buildFormSelectOptions, buildFormSubmit, buildFormSubmitOptions, buildFormTextarea, buildFormTextareaOptions, buildValidationGroup, buildValidationGroupOptions, index as default, handleFormValueChanged, install };
//# sourceMappingURL=index.mjs.map
