Newer
Older
vmk-ui-kit / src / vue / components / GnDataRow.js
@Eugene Sukhodolskiy Eugene Sukhodolskiy 19 hours ago 3 KB so many changes
/**
 * GnDataRow — compact data table/list row.
 *
 * Figma: Row - Asset List / Order list / Audience contact list,
 *        Campain search card / Contact search card.
 *
 * @typedef {Object} GnDataRowColumn
 * @property {string} value - Cell value.
 * @property {string} [label] - Optional column label.
 * @property {boolean} [main=false] - Whether this cell expands.
 *
 * @typedef {Object} GnDataRowMetric
 * @property {string} value - Metric value text.
 * @property {string} [icon] - VMK icon key or legacy Phosphor name (ph-*).
 */

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

export default defineComponent({
	name: "GnDataRow",
	inheritAttrs: false,
	props: {
		selected: { type: Boolean, default: false },
		image: { type: String, default: "" },
		icon: { type: String, default: "" },
		columns: { type: Array, default: () => [] },
		meta: { type: Array, default: () => [] },
		metrics: { type: Array, default: () => [] },
		actions: { type: Array, default: () => [] }
	},
	emits: ["select", "action"],
	setup(props, { attrs, slots, emit }) {
		const renderMeta = () => {
			const items = props.meta;
			if (!items.length) {
				return null;
			}

			return h("span", { class: "data-row-meta" }, items.map((item, index) => {
				const nodes = [];

				if (index > 0) {
					nodes.push(h("span", { class: "data-row-meta-separator" }));
				}

				if (item.icon) {
					nodes.push(iconNode(item.icon));
				}

				nodes.push(h("span", null, item.label || item.value || item));

				return h("span", { key: index, class: "data-row-meta-item" }, nodes);
			}));
		};

		return () => h("div", {
			...attrs,
			class: cx("data-row", { "data-row-selected": props.selected }, attrs.class)
		}, [
			slots.select?.() || h("label", { class: "data-row-select" }, [
				h("input", {
					type: "checkbox",
					checked: props.selected,
					onChange: event => emit("select", event.target.checked)
				})
			]),
			(props.image || props.icon || slots.media) && h("div", {
				class: cx("data-row-media", { "data-row-media--icon": props.icon && !props.image })
			}, [
				slots.media?.(),
				props.image && !slots.media && h("img", { src: props.image, alt: "" }),
				props.icon && !props.image && !slots.media && iconNode(props.icon)
			]),
			h("div", { class: "data-row-content" }, [
				...props.columns.map((col, index) => h("div", {
					class: cx("data-row-cell", { "data-row-cell-main": col.main })
				}, [
					col.label && h("span", { class: "data-row-label" }, col.label),
					h("span", { class: "data-row-value" }, slots[`col-${index}`]?.({ col }) || col.value)
				])),
				slots.meta?.() || renderMeta()
			]),
			(props.metrics.length || slots.metrics) && h("div", { class: "data-row-metrics" }, [
				slots.metrics?.(),
				props.metrics.map(metric => h("span", { class: "data-row-metric" }, [
					metric.icon && iconNode(metric.icon),
					metric.value
				]))
			]),
			(props.actions.length || slots.actions) && h("div", { class: "data-row-actions" }, [
				slots.actions?.(),
				props.actions.map(action => h("button", {
					class: cx("btn", "btn-icon", `btn-${action.variant || "secondary"}`, action.size ? `btn-${action.size}` : "btn-sm"),
					type: "button",
					"aria-label": action.label,
					onClick: () => emit("action", action.key || action.label)
				}, iconNode(action.icon)))
			])
		]);
	}
});