diff --git a/packages/nc-gui/components/cell/Json.vue b/packages/nc-gui/components/cell/Json.vue index e39ff2e06c..7b2270d6b5 100644 --- a/packages/nc-gui/components/cell/Json.vue +++ b/packages/nc-gui/components/cell/Json.vue @@ -184,6 +184,7 @@ watch(isExpanded, () => { :class="{ 'expanded-editor': isExpanded, 'editor': !isExpanded }" :hide-minimap="true" :disable-deep-compare="true" + :auto-focus="!isForm" @update:model-value="localValue = $event" @keydown.enter.stop /> diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index 5c5a205f2a..333a35c01f 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -394,7 +394,7 @@ const onFocus = () => { @click="toggleMenu" >
- + { @keydown.enter.stop.prevent="toggleMenu" >
- + { import type { ColumnType, KanbanType, ViewType } from 'nocodb-sdk' import { ViewTypes } from 'nocodb-sdk' -import { useMetas } from '#imports' +import { PreFilledMode, useMetas } from '#imports' const { view: _view, $api } = useSmartsheetStoreOrThrow() const { $e } = useNuxtApp() @@ -44,6 +44,10 @@ const activeView = computed<(ViewType & { meta: object & Record }) }, }) +const isPublicShared = computed(() => { + return !!activeView.value?.uuid +}) + const url = computed(() => { return sharedViewUrl() ?? '' }) @@ -145,6 +149,37 @@ const surveyMode = computed({ }, }) +const formPreFill = computed({ + get: () => ({ + preFillEnabled: parseProp(activeView.value?.meta)?.preFillEnabled ?? false, + preFilledMode: parseProp(activeView.value?.meta)?.preFilledMode || PreFilledMode.Default, + }), + set: (value) => { + if (!activeView.value?.meta) return + + if (formPreFill.value.preFillEnabled !== value.preFillEnabled) { + $e(`a:view:share:prefilled-mode-${value.preFillEnabled ? 'enabled' : 'disabled'}`) + } + + if (formPreFill.value.preFilledMode !== value.preFilledMode) { + $e(`a:view:share:${value.preFilledMode}-prefilled-mode`) + } + + activeView.value.meta = { + ...activeView.value.meta, + ...value, + } + savePreFilledMode() + }, +}) + +const handleChangeFormPreFill = (value: { preFillEnabled?: boolean; preFilledMode?: PreFilledMode }) => { + formPreFill.value = { + ...formPreFill.value, + ...value, + } +} + function sharedViewUrl() { if (!activeView.value) return @@ -177,7 +212,11 @@ function sharedViewUrl() { dashboardUrl1 = `${baseUrl}${appInfo.value?.dashboardPath}` } - return encodeURI(`${dashboardUrl1}#/nc/${viewType}/${activeView.value.uuid}`) + return encodeURI( + `${dashboardUrl1}#/nc/${viewType}/${activeView.value.uuid}${surveyMode.value ? '/survey' : ''}${ + viewStore.preFillFormSearchParams && formPreFill.value.preFillEnabled ? `?${viewStore.preFillFormSearchParams}` : '' + }`, + ) } const toggleViewShare = async () => { @@ -254,9 +293,11 @@ async function updateSharedView() { return true } -const isPublicShared = computed(() => { - return !!activeView.value?.uuid -}) +async function savePreFilledMode() { + await updateSharedView() +} + +watchEffect(() => {})
@@ -367,4 +459,18 @@ const isPublicShared = computed(() => { line-height: 1rem !important; } } + +.nc-modal-share-view-preFillMode { + @apply flex flex-col; + + .ant-radio-wrapper { + @apply !m-0 !flex !items-center w-full px-2 py-1 rounded-lg hover:bg-gray-100; + .ant-radio { + @apply !top-0; + } + .ant-radio + span { + @apply !flex !pl-4; + } + } +} diff --git a/packages/nc-gui/components/general/ColorPicker.vue b/packages/nc-gui/components/general/ColorPicker.vue index dffd7c9bc5..c3e748a653 100644 --- a/packages/nc-gui/components/general/ColorPicker.vue +++ b/packages/nc-gui/components/general/ColorPicker.vue @@ -104,15 +104,11 @@ watch(picked, (n, _o) => { -webkit-text-stroke-width: 1px; -webkit-text-stroke-color: white; } -.color-selector:not(.new-design):hover { +.color-selector:hover { filter: brightness(90%); -webkit-filter: brightness(90%); } -.color-selector.new-design:hover { - box-shadow: 0px 0px 0px 2px #fff, 0px 0px 0px 4px #3069fe; -} - .color-selector.selected:not(.new-design) { filter: brightness(90%); -webkit-filter: brightness(90%); diff --git a/packages/nc-gui/components/monaco/Editor.vue b/packages/nc-gui/components/monaco/Editor.vue index a353cbca43..706f700e9a 100644 --- a/packages/nc-gui/components/monaco/Editor.vue +++ b/packages/nc-gui/components/monaco/Editor.vue @@ -12,12 +12,20 @@ interface Props { validate?: boolean disableDeepCompare?: boolean readOnly?: boolean + autoFocus?: boolean } -const { hideMinimap, lang = 'json', validate = true, disableDeepCompare = false, modelValue, readOnly } = defineProps() +const props = withDefaults(defineProps(), { + lang: 'json', + validate: true, + disableDeepCompare: false, + autoFocus: true, +}) const emits = defineEmits(['update:modelValue']) +const { hideMinimap, lang, validate, disableDeepCompare, modelValue, readOnly, autoFocus } = props + const vModel = computed({ get: () => { if (typeof modelValue === 'object') { @@ -120,7 +128,7 @@ onMounted(async () => { } }) - if (!isDrawerOrModalExist()) { + if (!isDrawerOrModalExist() && autoFocus) { // auto focus on json cells only editor.focus() } diff --git a/packages/nc-gui/components/smartsheet/Cell.vue b/packages/nc-gui/components/smartsheet/Cell.vue index 56165395fc..22b8d300af 100644 --- a/packages/nc-gui/components/smartsheet/Cell.vue +++ b/packages/nc-gui/components/smartsheet/Cell.vue @@ -29,6 +29,7 @@ import { isJSON, isManualSaved, isMultiSelect, + isNumericFieldType, isPercent, isPhoneNumber, isPrimary, @@ -142,14 +143,7 @@ const navigate = (dir: NavigateDir, e: KeyboardEvent) => { } const isNumericField = computed(() => { - return ( - isInt(column.value, abstractType.value) || - isFloat(column.value, abstractType.value) || - isDecimal(column.value) || - isCurrency(column.value) || - isPercent(column.value) || - isDuration(column.value) - ) + return isNumericFieldType(column.value, abstractType.value) }) // disable contexxtmenu event propagation when cell is in diff --git a/packages/nc-gui/components/smartsheet/Form.vue b/packages/nc-gui/components/smartsheet/Form.vue index 9dbb379ce3..d44cf5d982 100644 --- a/packages/nc-gui/components/smartsheet/Form.vue +++ b/packages/nc-gui/components/smartsheet/Form.vue @@ -31,7 +31,6 @@ import { onClickOutside, parseProp, provide, - reactive, ref, useDebounceFn, useEventListener, @@ -42,7 +41,7 @@ import { useRoles, useViewColumnsOrThrow, useViewData, - watch, + useViewsStore, } from '#imports' import type { ImageCropperConfig } from '~/lib' @@ -72,8 +71,6 @@ const enum NcForm { const { isMobileMode, user } = useGlobal() -const formRef = ref() - const { $api, $e } = useNuxtApp() const { isUIAllowed } = useRoles() @@ -82,7 +79,9 @@ const { base } = storeToRefs(useBase()) const { getPossibleAttachmentSrc } = useAttachment() -let formState = reactive>({}) +const formRef = ref() + +const formState = ref>({}) const secondsRemain = ref(0) @@ -98,6 +97,8 @@ const isPublic = inject(IsPublicInj, ref(false)) const { loadFormView, insertRow, formColumnData, formViewData, updateFormView } = useViewData(meta, view) +const { preFillFormSearchParams } = storeToRefs(useViewsStore()) + const reloadEventHook = inject(ReloadViewDataHookInj, createEventHook()) reloadEventHook.on(async () => { @@ -110,7 +111,7 @@ const { fields, showAll, hideAll } = useViewColumnsOrThrow() const { state, row } = useProvideSmartsheetRowStore( meta, ref({ - row: formState, + row: formState.value, oldRow: {}, rowMeta: { new: true }, }), @@ -193,6 +194,22 @@ const updateView = useDebounceFn( { maxWait: 2000 }, ) +const updatePreFillFormSearchParams = useDebounceFn(() => { + if (isLocked.value || !isUIAllowed('dataInsert')) return + + const preFilledData = { ...formState.value, ...state.value } + + const searchParams = new URLSearchParams() + + for (const c of visibleColumns.value) { + if (c.title && preFilledData[c.title] && !isVirtualCol(c) && !(UITypes.Attachment === c.uidt)) { + searchParams.append(c.title, preFilledData[c.title]) + } + } + + preFillFormSearchParams.value = searchParams.toString() +}, 250) + async function submitForm() { if (isLocked.value || !isUIAllowed('dataInsert')) return @@ -206,7 +223,7 @@ async function submitForm() { } await insertRow({ - row: { ...formState, ...state.value }, + row: { ...formState.value, ...state.value }, oldRow: {}, rowMeta: { new: true }, }) @@ -217,9 +234,9 @@ async function submitForm() { async function clearForm() { if (isLocked.value || !isUIAllowed('dataInsert')) return - formState = reactive>({}) + formState.value = {} state.value = {} - await formRef.value.clearValidate() + await formRef.value?.clearValidate() reloadEventHook.trigger() } @@ -321,13 +338,6 @@ async function showOrHideColumn(column: Record, show: boolean, isSi await $api.dbView.formColumnUpdate(column.id, column) fields.value[fieldIndex] = column as any - // await saveOrUpdate( - // { - // ...column, - // show, - // }, - // fieldIndex, - // ) reloadEventHook.trigger() @@ -554,6 +564,8 @@ onMounted(async () => { URL.revokeObjectURL(imageCropperData.value.imageConfig.src) } + preFillFormSearchParams.value = '' + isLoadingFormView.value = true await loadFormView() setFormData() @@ -566,6 +578,7 @@ watch(submitted, (v) => { const intvl = setInterval(() => { if (--secondsRemain.value < 0) { submitted.value = false + clearForm() clearInterval(intvl) } }, 1000) @@ -578,25 +591,35 @@ watch(view, (nextView) => { } }) -watch([formState, state.value], async () => { - for (const virtualField in state.value) { - if (!formState[virtualField]) { - formState[virtualField] = state.value[virtualField] +watch( + [formState, state], + async () => { + for (const virtualField in state.value) { + formState.value[virtualField] = state.value[virtualField] } - } - try { - await formRef.value?.validateFields([...Object.keys(formState)]) - } catch (e: any) { - e.errorFields.map((f: Record) => console.error(f.errors.join(','))) - } -}) + updatePreFillFormSearchParams() + + try { + await formRef.value?.validateFields([...Object.keys(formState.value)]) + } catch (e: any) { + e.errorFields.map((f: Record) => console.error(f.errors.join(','))) + } + }, + { + deep: true, + }, +) watch(activeRow, (newValue) => { if (newValue) { - document - .querySelector(`.nc-form-field-item-${newValue?.replaceAll(' ', '')}`) - ?.scrollIntoView({ behavior: 'smooth', block: 'center' }) + const field = document.querySelector(`.nc-form-field-item-${CSS.escape(newValue?.replaceAll(' ', ''))}`) + + if (field) { + setTimeout(() => { + field?.scrollIntoView({ behavior: 'smooth', block: 'center' }) + }, 50) + } } }) @@ -647,7 +670,7 @@ useEventListener( (e: KeyboardEvent) => { const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey - switch (e.key.toLowerCase()) { + switch (e.key?.toLowerCase()) { case 's': if ( cmdOrCtrl && @@ -736,7 +759,16 @@ useEventListener(
- + {{ $t('activity.submitAnotherForm') }}
@@ -1056,7 +1088,7 @@ useEventListener( -
+
@@ -1231,7 +1263,7 @@ useEventListener(
-
+
{{ $t('labels.showOnConditions') }}
@@ -1240,7 +1272,7 @@ useEventListener(
-
+
{{ $t('activity.clearForm') }} +
-
+