import { defineComponent, h, computed } from "vue";
import { cx, iconNode } from "../utils.js";
let _id = 0;
function nextId() {
return `repeater-item-${++_id}`;
}
export default defineComponent({
name: "GnRepeater",
inheritAttrs: false,
props: {
modelValue: { type: Array, default: () => [] },
label: { type: String, default: "Items" },
addLabel: { type: String, default: "Add" },
addIcon: { type: String, default: "ph-plus" },
removeLabel: { type: String, default: "Remove" },
removeIcon: { type: String, default: "ph-trash" },
minItems: { type: Number, default: 1 },
maxItems: { type: Number, default: 0 },
itemFactory: {
type: Function,
default: () => () => ({ id: nextId() })
},
disabled: { type: Boolean, default: false }
},
emits: ["update:modelValue", "add", "remove"],
setup(props, { attrs, emit, slots }) {
const canAdd = computed(() => {
if(props.disabled) return false;
if(props.maxItems > 0 && props.modelValue.length >= props.maxItems) return false;
return true;
});
function canRemove(index) {
if(props.disabled) return false;
if(props.modelValue.length <= props.minItems) return false;
return true;
}
function add() {
if(!canAdd.value) return;
const item = props.itemFactory();
const next = [...props.modelValue, item];
emit("update:modelValue", next);
emit("add", item);
}
function remove(index) {
if(!canRemove(index)) return;
const removed = props.modelValue[index];
const next = props.modelValue.filter((_, i) => i !== index);
emit("update:modelValue", next);
emit("remove", removed);
}
return () => {
const headerSlot = slots.header?.({ add, canAdd: canAdd.value });
const header = headerSlot || h("div", { class: "repeater-header" }, [
h("span", { class: "repeater-title" }, props.label),
h("button", {
type: "button",
class: cx("btn", "btn-secondary", "btn-small", "with-icon"),
disabled: !canAdd.value,
onClick: add
}, [
iconNode(props.addIcon),
props.addLabel
])
]);
const items = props.modelValue.map((item, index) => {
const itemKey = item?.id ?? item?.key ?? index;
const removable = canRemove(index);
const scope = { item, index, remove: () => remove(index), canRemove: removable };
const body = slots.item?.(scope);
const actions = slots.actions?.(scope);
const defaultActions = !slots.actions && h("button", {
type: "button",
class: cx("btn-icon", "btn-icon-sm"),
"aria-label": props.removeLabel,
disabled: !removable,
onClick: (e) => {
e.stopPropagation();
remove(index);
}
}, [iconNode(props.removeIcon)]);
return h("div", {
class: "repeater-item",
key: itemKey
}, [
h("div", { class: "repeater-item-body" }, body),
(actions || defaultActions) && h("div", { class: "repeater-item-actions" }, actions || [defaultActions])
]);
});
return h("div", {
...attrs,
class: cx("repeater", { "repeater-disabled": props.disabled }, attrs.class)
}, [
header,
h("div", { class: "repeater-list" }, items)
]);
};
}
});