support dropdown field option

This commit is contained in:
lightling 2025-03-04 22:32:28 -05:00
parent 8b191a93b8
commit 7a0b69c1c1
Signed by: lightling
GPG key ID: F1F29650D537C773
4 changed files with 140 additions and 10 deletions

View file

@ -0,0 +1,67 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import Button from 'primevue/button'
import Chip from 'primevue/chip'
import InputGroup from 'primevue/inputgroup'
import InputText from 'primevue/inputtext'
const model = defineModel<string[]>()
const entry = ref('')
const preventSubmit = computed(() => entry.value === '' || model.value?.includes(entry.value))
const emits = defineEmits<{
(e: 'dirty'): void
}>()
const onRemove = (chip: string) => {
if (!model.value) {
return
}
const index = model.value.findIndex(other => other === chip)
if (index > -1) {
model.value?.splice(index, 1)
emits('dirty')
}
}
const onSubmit = (event: Event) => {
event.preventDefault()
if (preventSubmit.value) {
return
}
if (!Array.isArray(model.value)) {
model.value = []
}
model.value.push(entry.value)
entry.value = ''
emits('dirty')
}
</script>
<template lang="pug">
.input-chips
.chips
Chip(
v-for='chip in model'
:label='chip'
:onRemove='(e) => onRemove(chip)'
removable
)
InputGroup
InputText(
v-model='entry'
v-on:keyup.enter='onSubmit'
fluid
)
Button(
icon='pi pi-plus'
label='Add Option'
type='submit'
:disabled='preventSubmit'
@click='onSubmit'
)
</template>
<style scoped lang="sass">
</style>

View file

@ -1,3 +1,7 @@
/**
* Defines the available types of fields that can be edited by the data editor,
* set by a column's field type.
*/
export const FieldTypes = [
'text',
'image',
@ -6,6 +10,9 @@ export const FieldTypes = [
'toggle',
] as const
/**
* Specifies the type of field for editing in the data editor.
*/
export type FieldType = typeof FieldTypes[number]
export interface Image {
@ -13,18 +20,35 @@ export interface Image {
alt?: string
}
/**
* The structure of a column. Columns define the available fields to edit for every row
* and control how they are edited.
*/
export interface Column {
/** the name of the field; this represents the key in the columns record */
name: string
/** the type of the field; this controls how it is edited in the data editor */
type: FieldType
/** whether the field is secondary or not; if it is, it is hidden from the quick editing view in the data editor */
secondary?: boolean
/** options for the field; applies only to certain field types such as dropdowns */
options?: any
}
export type Columns = Column[]
/**
* The structure of a Row. All rows at minimum should have a private _id field;
* beyond that, it is simply a record of key-value pairs
* where the key corresponds to a column by name
*/
export type Row = {
_id: string
} & Record<string, string | any>
/**
* The structure of an inventory. An inventory is simply a set of columns (fields) and rows (data),
* where each row has key-value pairs where the keys correspond to a column by name,
* and the columns define how the rows can be edited
*/
export interface Inventory {
columns: Column[]
rows: Row[]

View file

@ -3,10 +3,11 @@ import { computed, ref } from 'vue'
import { v7 as uuidv7 } from 'uuid'
import Button from 'primevue/button'
import Checkbox from 'primevue/checkbox'
import DataTable from 'primevue/datatable'
import Column from 'primevue/column'
import DataTable from 'primevue/datatable'
import Image from 'primevue/image'
import InputText from 'primevue/inputtext'
import Select from 'primevue/select'
import {
type Row as DataRow,
@ -143,7 +144,12 @@ DataTable.data-editor(
div(
v-else
)
span {{ slotProps.data[col.name] }}
span(
v-if='slotProps.data[col.name]'
) {{ slotProps.data[col.name] }}
span.empty(
v-else
) not set
template(
#editor='slotProps'
)
@ -168,6 +174,17 @@ DataTable.data-editor(
:name='`toggle for ${slotProps.data.name || slotProps.data._id} ${col.name}`'
binary
)
div(
v-else-if='col.type === "dropdown"'
)
Select(
v-model='slotProps.data[slotProps.field]'
:options='col.options'
placeholder='Select from Options'
filter
showClear
fluid
)
div(
v-else
)
@ -228,7 +245,7 @@ DataTable.data-editor(
)
p.empty(
v-if='!slotProps.data[field]'
) {{ field }} is empty
) {{ field }} is not set
.info-image(
v-else-if='column.type === "image"'
)
@ -262,13 +279,14 @@ Button(
img
max-width: 100%
max-height: 8rem
:deep(td:last-of-type)
:deep(tr:not(.p-datatable-row-expansion) td:last-of-type)
padding-left: 0
button:first-of-type
margin-left: -16px
.info :deep(.p-image) img
max-width: 16rem
max-height: 16rem
p.empty
p.empty,
span.empty
font-style: italic
</style>

View file

@ -12,12 +12,14 @@ import {
FieldTypes,
type Column as DataColumn,
} from 'src/types/data'
import InputChips from 'src/components/InputChips.vue'
const emits = defineEmits<{
(e: 'dirty'): void
}>()
const model = defineModel<DataColumn[]>({ required: true })
const editingFields = ref([])
const expandedRows = ref([])
const onFieldEditSave = (event: { newData: any, index: number }) => {
let { newData, index } = event
@ -49,9 +51,14 @@ const onDeleteField = (event: Event, slotProps: { index: number }) => {
DataTable.field-editor(
@row-edit-save='onFieldEditSave'
v-model:editingRows='editingFields'
v-model:expandedRows='expandedRows'
:value='model'
editMode='row'
tableStyle='min-width: 50rem'
)
Column(
expander
style='width:5rem;'
)
Column(
field='name'
@ -156,6 +163,19 @@ DataTable.field-editor(
slotProps.editorCancelCallback(e)
}`
)
template(
#expansion='slotProps'
)
div(
v-if='slotProps.data.type === "dropdown"'
)
InputChips(
v-model='slotProps.data.options'
@dirty='$emit("dirty")'
)
p(
v-else
) there are no additional properties for this field type
Button(
icon='pi pi-plus'
aria-label='Add New Field'
@ -165,7 +185,8 @@ Button(
</template>
<style scoped lang="sass">
:deep(td:last-of-type)
display: flex
padding-left: 8px
:deep(tr:not(.p-datatable-row-expansion) td:last-of-type)
padding-left: 0
button:first-of-type
margin-left: -16px
</style>