Newer
Older
vmk-ui-kit / src / vue / components / GnInput.js
/**
 * GnInput - Text input field with label, icon, state, and help text.
 *
 * @typedef {Object} GnInputProps
 * @property {string|number} [modelValue=''] - Bound value
 * @property {string} [label=''] - Label text
 * @property {string} [type='text'] - input type attribute
 * @property {string} [icon=''] - Phosphor icon name with ph- prefix or VMK key
 * @property {string} [state=''] - error | warning | success
 * @property {string} [help=''] - Help or validation message
 * @property {boolean} [bare=false] - Render bare input without .form-group wrapper
 *
 * @emits update:modelValue
 */

import { defineComponent, h } from "vue";
import { cx, eventValue, iconNode, normalizeInputState } from "../utils.js";

export default defineComponent({
	name: "GnInput",
	inheritAttrs: false,
	props: {
		modelValue: { type: [String, Number], default: "" },
		label: { type: String, default: "" },
		type: { type: String, default: "text" },
		icon: { type: String, default: "" },
		state: { type: String, default: "" },
		help: { type: String, default: "" },
		bare: { type: Boolean, default: false }
	},
	emits: ["update:modelValue"],
	setup(props, { attrs, emit }) {
		return () => {
			const state = normalizeInputState(props.state);
			const hasIcon = Boolean(props.icon);

			const input = h("input", {
				...attrs,
				type: props.type,
				value: props.modelValue,
				class: cx(
					props.bare ? "" : "input",
					{
						"input-icon-left": hasIcon
					},
					attrs.class
				),
				onInput: event => emit("update:modelValue", eventValue(event))
			});

			if (props.bare) {
				return input;
			}

			return h("div", { class: cx("form-group", state) }, [
				props.label && h("label", { class: "label" }, [
					props.label,
					iconNode(props.icon),
					input
				]),
				!props.label && input,
				props.help && h("p", { class: cx("input-help", state && `is-${state}`) }, props.help)
			]);
		};
	}
});