mirror of
https://gitlab.com/bramw/baserow.git
synced 2024-11-22 15:52:34 +00:00
269 lines
7.4 KiB
Vue
269 lines
7.4 KiB
Vue
<template>
|
|
<form @submit.prevent="submit">
|
|
<div class="row margin-bottom-2">
|
|
<div class="col col-6">
|
|
<PageSettingsNameFormElement
|
|
ref="name"
|
|
v-model="values.name"
|
|
:disabled="!hasPermission"
|
|
:has-errors="fieldHasErrors('name')"
|
|
:validation-state="$v.values.name"
|
|
:is-creation="isCreation"
|
|
@blur="$v.values.name.$touch()"
|
|
/>
|
|
</div>
|
|
<div class="col col-6">
|
|
<PageSettingsPathFormElement
|
|
v-model="values.path"
|
|
:disabled="!hasPermission"
|
|
:has-errors="fieldHasErrors('path')"
|
|
:validation-state="$v.values.path"
|
|
@blur="onPathBlur"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col col-6">
|
|
<PageSettingsPathParamsFormElement
|
|
:disabled="!hasPermission"
|
|
:path-params="values.path_params"
|
|
@update="onPathParamUpdate"
|
|
/>
|
|
</div>
|
|
<div class="col col-6"></div>
|
|
</div>
|
|
<slot></slot>
|
|
</form>
|
|
</template>
|
|
|
|
<script>
|
|
import { maxLength, required } from 'vuelidate/lib/validators'
|
|
|
|
import form from '@baserow/modules/core/mixins/form'
|
|
import PageSettingsNameFormElement from '@baserow/modules/builder/components/page/settings/PageSettingsNameFormElement'
|
|
import PageSettingsPathFormElement from '@baserow/modules/builder/components/page/settings/PageSettingsPathFormElement'
|
|
import PageSettingsPathParamsFormElement from '@baserow/modules/builder/components/page/settings/PageSettingsPathParamsFormElement'
|
|
import {
|
|
getPathParams,
|
|
PATH_PARAM_REGEX,
|
|
ILLEGAL_PATH_SAMPLE_CHARACTER,
|
|
VALID_PATH_CHARACTERS,
|
|
} from '@baserow/modules/builder/utils/path'
|
|
import {
|
|
getNextAvailableNameInSequence,
|
|
slugify,
|
|
} from '@baserow/modules/core/utils/string'
|
|
|
|
export default {
|
|
name: 'PageSettingsForm',
|
|
components: {
|
|
PageSettingsPathParamsFormElement,
|
|
PageSettingsPathFormElement,
|
|
PageSettingsNameFormElement,
|
|
},
|
|
mixins: [form],
|
|
inject: ['workspace', 'builder', 'page'],
|
|
props: {
|
|
isCreation: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: false,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
values: {
|
|
name: '',
|
|
path: '',
|
|
path_params: [],
|
|
},
|
|
hasPathBeenEdited: false,
|
|
}
|
|
},
|
|
computed: {
|
|
hasPermission() {
|
|
if (this.isCreation) {
|
|
return this.$hasPermission(
|
|
'builder.create_page',
|
|
this.builder,
|
|
this.workspace.id
|
|
)
|
|
} else {
|
|
return this.$hasPermission(
|
|
'builder.page.update',
|
|
this.page,
|
|
this.workspace.id
|
|
)
|
|
}
|
|
},
|
|
defaultPathParamType() {
|
|
return this.$registry.getOrderedList('pathParamType')[0].getType()
|
|
},
|
|
defaultName() {
|
|
const baseName = this.$t('pageForm.defaultName')
|
|
return getNextAvailableNameInSequence(baseName, this.pageNames)
|
|
},
|
|
pages() {
|
|
return this.$store.getters['page/getVisiblePages'](this.builder)
|
|
},
|
|
pageNames() {
|
|
return this.pages.map((page) => page.name)
|
|
},
|
|
otherPagePaths() {
|
|
return this.pages
|
|
.filter((page) => page.id !== this.page?.id)
|
|
.map((page) => page.path)
|
|
},
|
|
currentPathParams() {
|
|
return getPathParams(this.values.path)
|
|
},
|
|
},
|
|
watch: {
|
|
// When the path change we want to update the value.path_params value
|
|
// but try to keep the previous configuration as much as possible
|
|
currentPathParams(paramNames, oldParamNames) {
|
|
const result = paramNames.map((param) => ({
|
|
name: param,
|
|
type: 'text',
|
|
}))
|
|
|
|
const pathParamIndexesByName = this.values.path_params.reduce(
|
|
(prev, { name }, index) => {
|
|
if (!prev[name]) {
|
|
prev[name] = []
|
|
}
|
|
prev[name].push(index)
|
|
return prev
|
|
},
|
|
{}
|
|
)
|
|
|
|
// List of used index of existing params to use them once only.
|
|
const usedIndex = []
|
|
// An index is ok if it has already been associated with an existing param
|
|
// to prevent double association.
|
|
const okIndex = []
|
|
|
|
// First match same names at same position
|
|
paramNames.forEach((paramName, index) => {
|
|
if (paramName === oldParamNames[index]) {
|
|
Object.assign(result[index], this.values.path_params[index])
|
|
pathParamIndexesByName[paramName] = pathParamIndexesByName[
|
|
paramName
|
|
].filter((i) => i !== index)
|
|
usedIndex.push(index)
|
|
okIndex.push(index)
|
|
}
|
|
})
|
|
|
|
// Then match previously existing names at another position
|
|
paramNames.forEach((paramName, index) => {
|
|
if (okIndex.includes(index)) {
|
|
return
|
|
}
|
|
if (pathParamIndexesByName[paramName]?.length) {
|
|
const paramIndex = pathParamIndexesByName[paramName].shift()
|
|
Object.assign(result[index], this.values.path_params[paramIndex])
|
|
usedIndex.push(paramIndex)
|
|
okIndex.push(index)
|
|
}
|
|
})
|
|
|
|
// Then match remaining existing params in same relative order
|
|
paramNames.forEach((paramName, index) => {
|
|
if (okIndex.includes(index)) {
|
|
return
|
|
}
|
|
const freeIndex = this.values.path_params.findIndex(
|
|
(_, index) => !usedIndex.includes(index)
|
|
)
|
|
if (freeIndex !== -1) {
|
|
Object.assign(result[index], this.values.path_params[freeIndex], {
|
|
name: paramName,
|
|
})
|
|
usedIndex.push(freeIndex)
|
|
}
|
|
})
|
|
|
|
this.values.path_params = result
|
|
},
|
|
'values.name': {
|
|
handler(value) {
|
|
if (!this.hasPathBeenEdited && this.isCreation) {
|
|
this.values.path = `/${slugify(value)}`
|
|
}
|
|
},
|
|
immediate: true,
|
|
},
|
|
},
|
|
created() {
|
|
if (this.isCreation) {
|
|
this.values.name = this.defaultName
|
|
}
|
|
},
|
|
mounted() {
|
|
if (this.isCreation) {
|
|
this.$refs.name.$refs.input.focus()
|
|
}
|
|
},
|
|
methods: {
|
|
generalisePath(path) {
|
|
return path.replace(PATH_PARAM_REGEX, ILLEGAL_PATH_SAMPLE_CHARACTER)
|
|
},
|
|
onPathBlur() {
|
|
this.$v.values.path.$touch()
|
|
this.hasPathBeenEdited = true
|
|
},
|
|
onPathParamUpdate(paramTypeName, paramType) {
|
|
this.values.path_params.forEach((pathParam) => {
|
|
if (pathParam.name === paramTypeName) {
|
|
pathParam.type = paramType
|
|
}
|
|
})
|
|
},
|
|
isNameUnique(name) {
|
|
return !this.pageNames.includes(name) || name === this.page?.name
|
|
},
|
|
isPathUnique(path) {
|
|
const pathGeneralised = this.generalisePath(path)
|
|
return (
|
|
!this.otherPagePaths.some(
|
|
(pathCurrent) => this.generalisePath(pathCurrent) === pathGeneralised
|
|
) || path === this.page?.path
|
|
)
|
|
},
|
|
pathStartsWithSlash(path) {
|
|
return path[0] === '/'
|
|
},
|
|
pathHasValidCharacters(path) {
|
|
return !path
|
|
.split('')
|
|
.some((letter) => !VALID_PATH_CHARACTERS.includes(letter))
|
|
},
|
|
arePathParamsUnique(path) {
|
|
const pathParams = getPathParams(path)
|
|
return new Set(pathParams).size === pathParams.length
|
|
},
|
|
},
|
|
validations() {
|
|
return {
|
|
values: {
|
|
name: {
|
|
required,
|
|
isUnique: this.isNameUnique,
|
|
maxLength: maxLength(255),
|
|
},
|
|
path: {
|
|
required,
|
|
isUnique: this.isPathUnique,
|
|
maxLength: maxLength(255),
|
|
startingSlash: this.pathStartsWithSlash,
|
|
validPathCharacters: this.pathHasValidCharacters,
|
|
uniquePathParams: this.arePathParamsUnique,
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
</script>
|