diff --git a/packages/builder/src/components/backend/DataTable/modals/OptionsEditor.svelte b/packages/builder/src/components/backend/DataTable/modals/OptionsEditor.svelte index 26d4e39c36..ed66906aea 100644 --- a/packages/builder/src/components/backend/DataTable/modals/OptionsEditor.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/OptionsEditor.svelte @@ -4,6 +4,7 @@ import { Icon, Popover } from "@budibase/bbui" import { onMount, tick } from "svelte" import { Constants } from "@budibase/frontend-core" + import { getSequentialName } from "helpers/duplicate" export let constraints export let optionColors = {} @@ -15,25 +16,33 @@ let colorPopovers = [] let anchors = [] - const removeInput = idx => { - delete optionColors[options[idx].name] - constraints.inclusion = constraints.inclusion.filter((e, i) => i !== idx) - options = options.filter((e, i) => i !== idx) + $: enrichedOptions = options.map((option, idx) => ({ + ...option, + color: optionColors?.[option.name] || defaultColor(idx), + })) + + const defaultColor = idx => OptionColours[idx % OptionColours.length] + + const removeInput = name => { + delete optionColors[name] + constraints.inclusion = constraints.inclusion.filter(opt => opt !== name) + options = options.filter(opt => opt.name !== name) colorPopovers.pop(undefined) anchors.pop(undefined) } const addNewInput = async () => { - const newName = `Option ${constraints.inclusion.length + 1}` - const id = Math.random() - options = [...options, { name: newName, id }] + const newName = getSequentialName(constraints.inclusion, "Option ", { + numberFirstItem: true, + }) + const newId = Math.random() + options = [...options, { name: newName, id: newId }] constraints.inclusion = [...constraints.inclusion, newName] - optionColors[newName] = - OptionColours[(options.length - 1) % OptionColours.length] + optionColors[newName] = defaultColor(options.length - 1) colorPopovers.push(undefined) anchors.push(undefined) await tick() - document.getElementById(`option-${id}`)?.focus() + document.getElementById(`option-${newId}`)?.focus() } const handleDndConsider = e => { @@ -44,16 +53,29 @@ constraints.inclusion = options.map(option => option.name) } - const handleColorChange = (optionName, color, idx) => { - optionColors[optionName] = color + const handleColorChange = (name, color, idx) => { + optionColors[name] = color colorPopovers[idx].hide() } - const handleNameChange = (optionName, idx, value) => { - constraints.inclusion[idx] = value - options[idx].name = value - optionColors[value] = optionColors[optionName] - delete optionColors[optionName] + const handleNameChange = (name, idx, newName) => { + // Check we don't already have this option + const existing = options.some((option, optionIdx) => { + return newName === option.name && idx !== optionIdx + }) + const invalid = !newName || existing + options.find(option => option.name === name).invalid = invalid + options = options.slice() + + // Stop if invalid or no change + if (invalid || name === newName) { + return + } + + constraints.inclusion[idx] = newName + options[idx].name = newName + optionColors[newName] = optionColors[name] + delete optionColors[name] } const openColorPickerPopover = optionIdx => { @@ -66,79 +88,84 @@ } } - const getOptionColor = (name, idx) => { - return optionColors?.[name] || OptionColours[idx % OptionColours.length] - } - onMount(() => { // Initialize anchor arrays on mount, assuming 'options' is already populated colorPopovers = constraints.inclusion.map(() => undefined) anchors = constraints.inclusion.map(() => undefined) - options = constraints.inclusion.map(value => ({ - name: value, id: Math.random(), + name: value, })) }) -