Browse Source

Merge pull request #3209 from nocodb/feat/gui-v2-is-locked

feat(gui-v2): isLocked
pull/3221/head
Raju Udava 2 years ago committed by GitHub
parent
commit
40f1cd1941
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      packages/nc-gui-v2/assets/style-v2.scss
  2. 2
      packages/nc-gui-v2/components/cell/YearPicker.vue
  3. 14
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  4. 9
      packages/nc-gui-v2/components/smartsheet-header/Menu.vue
  5. 8
      packages/nc-gui-v2/components/smartsheet-toolbar/MoreActions.vue
  6. 22
      packages/nc-gui-v2/components/smartsheet/Cell.vue
  7. 15
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  8. 6
      packages/nc-gui-v2/components/smartsheet/sidebar/RenameableMenuItem.vue
  9. 12
      packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/AddRow.vue
  10. 10
      packages/nc-gui-v2/components/tabs/Smartsheet.vue
  11. 4
      packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue
  12. 5
      packages/nc-gui-v2/components/virtual-cell/HasMany.vue
  13. 5
      packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue
  14. 6
      packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue
  15. 2
      packages/nc-gui-v2/composables/useSharedView.ts
  16. 2
      packages/nc-gui-v2/context/index.ts
  17. 5
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue

16
packages/nc-gui-v2/assets/style-v2.scss

@ -50,6 +50,10 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
// menu item styling
.nc-menu-item {
@apply cursor-pointer text-xs flex items-center gap-2 px-4 py-3 relative after:(content-[''] absolute top-0 left-0 bottom-0 w-full h-full right-0 bg-current opacity-0 transition transition-opactity duration-100) hover:(after:(opacity-5));
&.disabled {
@apply text-black text-opacity-25 bg-[#f5f5f5] cursor-not-allowed text-shadow-none box-shadow-none border-[#d9d9d9];
}
}
.nc-project-menu-item {
@ -74,9 +78,17 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
@apply ring shadow-2xl transform scale-110;
}
&.disabled-ring:hover::after {
@apply ring-0;
}
svg {
@apply z-1 text-xl p-1 text-gray-500;
}
.disabled {
@apply cursor-not-allowed border-none;
}
}
// show a dot badge if some change present
@ -89,6 +101,10 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
@apply bg-primary/20 hover:(bg-primary/20);
}
.nc-locked-overlay {
@apply absolute h-full w-full z-2 top-0 left-0;
}
.ant-modal-wrap {
@apply !scrollbar-thin-dull;
}

2
packages/nc-gui-v2/components/cell/YearPicker.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { computed, inject, onClickOutside, ref, watch } from '#imports'
import { computed, inject, onClickOutside, ref, watch, ReadonlyInj } from '#imports'
interface Props {
modelValue?: number | string | null

14
packages/nc-gui-v2/components/dashboard/TreeView.vue

@ -23,6 +23,8 @@ const { deleteTable } = useTable()
const { isUIAllowed } = useUIPermission()
const isLocked = inject('TreeViewIsLockedInj')
const tablesById = $computed<Record<string, TableType>>(() =>
tables?.value?.reduce((acc: Record<string, TableType>, table: TableType) => {
acc[table.id as string] = table
@ -199,7 +201,11 @@ const activeTable = computed(() => {
<div class="nc-tbl-title flex-1">{{ table.title }}</div>
<a-dropdown v-if="isUIAllowed('table-rename') || isUIAllowed('table-delete')" :trigger="['click']" @click.stop>
<a-dropdown
v-if="!isLocked && (isUIAllowed('table-rename') || isUIAllowed('table-delete'))"
:trigger="['click']"
@click.stop
>
<MdiMenuIcon class="transition-opacity opacity-0 group-hover:opacity-100" />
<template #overlay>
@ -209,7 +215,7 @@ const activeTable = computed(() => {
v-t="['c:table:rename']"
class="!text-xs"
@click="showRenameTableDlg(table)"
><div>Rename</div></a-menu-item
><div>{{ $t('general.rename') }}</div></a-menu-item
>
<a-menu-item
@ -218,7 +224,7 @@ const activeTable = computed(() => {
class="!text-xs"
@click="deleteTable(table)"
>
Delete</a-menu-item
{{ $t('general.delete') }}</a-menu-item
>
</a-menu>
</template>
@ -238,7 +244,7 @@ const activeTable = computed(() => {
</div>
</div>
<template #overlay>
<template v-if="!isLocked" #overlay>
<a-menu class="cursor-pointer">
<template v-if="contextMenuTarget.type === 'table'">
<a-menu-item

9
packages/nc-gui-v2/components/smartsheet-header/Menu.vue

@ -3,21 +3,22 @@ import { Modal, message } from 'ant-design-vue'
import { inject } from 'vue'
import { useI18n } from 'vue-i18n'
import { useNuxtApp } from '#app'
import { useMetas } from '#imports'
import { ColumnInj, MetaInj } from '~/context'
import { extractSdkResponseErrorMsg } from '~/utils'
import { useMetas, ColumnInj, MetaInj, IsLockedInj, extractSdkResponseErrorMsg } from '#imports'
import MdiEditIcon from '~icons/mdi/pencil'
import MdiStarIcon from '~icons/mdi/star'
import MdiDeleteIcon from '~icons/mdi/delete-outline'
import MdiMenuDownIcon from '~icons/mdi/menu-down'
const { virtual = false } = defineProps<{ virtual?: boolean }>()
const emit = defineEmits(['edit'])
const column = inject(ColumnInj)
const meta = inject(MetaInj)
const isLocked = inject(IsLockedInj)
const { $api, $e } = useNuxtApp()
const { t } = useI18n()
@ -53,7 +54,7 @@ const setAsPrimaryValue = async () => {
</script>
<template>
<a-dropdown placement="bottomRight" :trigger="['click']">
<a-dropdown v-if="!isLocked" placement="bottomRight" :trigger="['click']">
<MdiMenuDownIcon class="h-full text-grey nc-ui-dt-dropdown cursor-pointer outline-0" />
<template #overlay>
<a-menu class="shadow bg-white">

8
packages/nc-gui-v2/components/smartsheet-toolbar/MoreActions.vue

@ -7,6 +7,7 @@ import { message } from 'ant-design-vue'
import {
ActiveViewInj,
FieldsInj,
IsLockedInj,
IsPublicInj,
MetaInj,
extractSdkResponseErrorMsg,
@ -35,9 +36,13 @@ const { project } = useProject()
const { $api } = useNuxtApp()
const meta = inject(MetaInj)
const fields = inject(FieldsInj, ref([]))
const selectedView = inject(ActiveViewInj)
const isLocked = inject(IsLockedInj)
const showWebhookDrawer = ref(false)
const quickImportDialog = ref(false)
@ -124,7 +129,8 @@ const exportFile = async (exportType: ExportTypes) => {
v-if="isUIAllowed('csvImport') && !isView && !isPublicView"
v-t="['a:actions:upload-csv']"
class="nc-menu-item"
@click="quickImportDialog = true"
:class="{ disabled: isLocked }"
@click="!isLocked ? (quickImportDialog = true) : {}"
>
<MdiUploadOutline class="text-gray-500" />
<!-- Upload CSV -->

22
packages/nc-gui-v2/components/smartsheet/Cell.vue

@ -1,7 +1,20 @@
<script setup lang="ts">
import { UITypes } from 'nocodb-sdk'
import type { ColumnType } from 'nocodb-sdk'
import { ActiveCellInj, ColumnInj, EditModeInj, computed, provide, toRef, useColumn, useDebounceFn, useVModel } from '#imports'
import {
ActiveCellInj,
ColumnInj,
EditModeInj,
IsFormInj,
IsLockedInj,
IsPublicInj,
computed,
provide,
toRef,
useColumn,
useDebounceFn,
useVModel,
} from '#imports'
import { NavigateDir } from '~/lib'
interface Props {
@ -29,6 +42,12 @@ provide(EditModeInj, useVModel(props, 'editEnabled', emit))
provide(ActiveCellInj, active)
const isForm = inject(IsFormInj)
const isPublic = inject(IsPublicInj)
const isLocked = inject(IsLockedInj)
let changed = $ref(false)
const syncValue = useDebounceFn(function () {
@ -142,5 +161,6 @@ const syncAndNavigate = (dir: NavigateDir) => {
<CellText v-else-if="isString" v-model="vModel" />
<CellJson v-else-if="isJSON" v-model="vModel" />
<CellText v-else v-model="vModel" />
<div v-if="(isLocked || (isPublic && !isForm)) && !isAttachment" class="nc-locked-overlay" @click.stop.prevent />
</div>
</template>

15
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -347,7 +347,7 @@ const expandForm = (row: Row, state: Record<string, any>) => {
</div>
</th>
<th
v-if="!readOnly && isUIAllowed('add-column')"
v-if="!readOnly && !isLocked && isUIAllowed('add-column')"
v-t="['c:column:add']"
class="cursor-pointer"
@click.stop="addColumnDropdown = true"
@ -376,10 +376,11 @@ const expandForm = (row: Row, state: Record<string, any>) => {
<tr class="nc-grid-row">
<td key="row-index" class="caption nc-grid-cell pl-5 pr-1">
<div class="align-center flex gap-1 min-w-[55px]">
<div v-if="!readOnly" class="nc-row-no text-xs text-gray-500" :class="{ hidden: row.rowMeta.selected }">
{{ rowIndex + 1 }}
</div>
<div v-else class="text-xs text-gray-500">
<div
v-if="!readonly && !isLocked"
class="nc-row-no text-xs text-gray-500"
:class="{ hidden: row.rowMeta.selected }"
>
{{ rowIndex + 1 }}
</div>
<div
@ -390,7 +391,7 @@ const expandForm = (row: Row, state: Record<string, any>) => {
<a-checkbox v-model:checked="row.rowMeta.selected" />
</div>
<span class="flex-1" />
<div v-if="!readOnly" class="nc-expand" :class="{ 'nc-comment': row.rowMeta?.commentCount }">
<div v-if="!readonly && !isLocked" class="nc-expand" :class="{ 'nc-comment': row.rowMeta?.commentCount }">
<span
v-if="row.rowMeta?.commentCount"
class="py-1 px-3 rounded-full text-xs cursor-pointer select-none transform hover:(scale-110)"
@ -478,7 +479,7 @@ const expandForm = (row: Row, state: Record<string, any>) => {
</tr>
</tbody>
</table>
<template #overlay>
<template v-if="!isLocked" #overlay>
<a-menu class="bg-white shadow" @click="contextMenu = false">
<a-menu-item v-if="contextMenuTarget" @click="deleteRow(contextMenuTarget.row)"
><span class="text-xs">Delete row</span></a-menu-item

6
packages/nc-gui-v2/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -2,7 +2,7 @@
import type { ViewTypes } from 'nocodb-sdk'
import { message } from 'ant-design-vue'
import { viewIcons } from '~/utils'
import { onKeyStroke, useDebounceFn, useNuxtApp, useUIPermission, useVModel } from '#imports'
import { IsLockedInj, onKeyStroke, useDebounceFn, useNuxtApp, useUIPermission, useVModel } from '#imports'
interface Props {
view: Record<string, any>
@ -27,6 +27,8 @@ const { $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const isLocked = inject(IsLockedInj)
/** Is editing the view name enabled */
let isEditing = $ref<boolean>(false)
@ -168,7 +170,7 @@ function onStopEdit() {
<div class="flex-1" />
<template v-if="!isEditing && isUIAllowed('virtualViewsCreateOrEdit')">
<template v-if="!isEditing && !isLocked && isUIAllowed('virtualViewsCreateOrEdit')">
<div class="flex items-center gap-1">
<a-tooltip placement="left">
<template #title>

12
packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/AddRow.vue

@ -1,14 +1,20 @@
<script setup lang="ts">
const emits = defineEmits(['addRow'])
const { isOpen } = useSidebar({ storageKey: 'nc-right-sidebar' })
const isLocked = inject(IsLockedInj)
</script>
<template>
<a-tooltip :placement="isOpen ? 'bottomRight' : 'left'">
<template #title> {{ $t('activity.addRow') }} </template>
<div class="nc-sidebar-right-item hover:after:bg-primary/75 group nc-sidebar-add-row">
<MdiPlusOutline class="cursor-pointer group-hover:(!text-white)" @click="emits('addRow')" />
<div
:class="{ 'hover:after:bg-primary/75 group': !isLocked, 'disabled-ring': isLocked }"
class="nc-sidebar-right-item nc-sidebar-add-row"
>
<MdiPlusOutline
:class="{ 'cursor-pointer group-hover:(!text-white)': !isLocked, 'disabled': isLocked }"
@click="!isLocked ? emits('addRow') : {}"
/>
</div>
</a-tooltip>
</template>

10
packages/nc-gui-v2/components/tabs/Smartsheet.vue

@ -5,6 +5,7 @@ import SmartsheetGrid from '../smartsheet/Grid.vue'
import {
ActiveViewInj,
FieldsInj,
IsFormInj,
IsLockedInj,
MetaInj,
ReloadViewDataHookInj,
@ -12,6 +13,7 @@ import {
computed,
inject,
provide,
provideSidebar,
useMetas,
useProvideSmartsheetStore,
watch,
@ -41,7 +43,7 @@ watchEffect(async () => {
const reloadEventHook = createEventHook<void>()
const { isGallery, isGrid, isForm } = useProvideSmartsheetStore(activeView as Ref<TableType>, meta)
const { isGallery, isGrid, isForm, isLocked } = useProvideSmartsheetStore(activeView as Ref<TableType>, meta)
// provide the sidebar injection state
provideSidebar({ storageKey: 'nc-right-sidebar' })
@ -50,14 +52,18 @@ provideSidebar({ storageKey: 'nc-right-sidebar' })
provide(MetaInj, meta)
provide(TabMetaInj, tabMeta)
provide(ActiveViewInj, activeView)
provide(IsLockedInj, false)
provide(IsLockedInj, isLocked)
provide(ReloadViewDataHookInj, reloadEventHook)
provide(FieldsInj, fields)
provide(IsFormInj, isForm)
const treeViewIsLockedInj = inject('TreeViewIsLockedInj', ref(false))
watch(tabMeta, async (newTabMeta, oldTabMeta) => {
if (newTabMeta !== oldTabMeta && newTabMeta?.id) await getMeta(newTabMeta.id)
})
watch(isLocked, (nextValue) => (treeViewIsLockedInj.value = nextValue), { immediate: true })
</script>
<template>

4
packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue

@ -33,6 +33,8 @@ const active = inject(ActiveCellInj)!
const readonly = inject(ReadonlyInj, false)
const isLocked = inject(IsLockedInj)
const listItemsDlg = ref(false)
const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow()
@ -72,7 +74,7 @@ const unlinkRef = async (rec: Record<string, any>) => {
<ItemChip :item="value" :value="value[relatedTablePrimaryValueProp]" @unlink="unlinkRef(value)" />
</template>
</div>
<div v-if="!readonly" class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<div v-if="!readonly || !isLocked" class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<component
:is="addIcon"
class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 select-none group-hover:(text-gray-500) nc-plus"

5
packages/nc-gui-v2/components/virtual-cell/HasMany.vue

@ -5,6 +5,7 @@ import {
CellValueInj,
ColumnInj,
IsFormInj,
IsLockedInj,
ReadonlyInj,
ReloadViewDataHookInj,
RowInj,
@ -34,6 +35,8 @@ const isForm = inject(IsFormInj)
const readonly = inject(ReadonlyInj, false)
const isLocked = inject(IsLockedInj)
const listItemsDlg = ref(false)
const childListDlg = ref(false)
@ -88,7 +91,7 @@ const unlinkRef = async (rec: Record<string, any>) => {
</span>
</template>
</div>
<div class="flex-grow flex justify-end gap-1 min-h-[30px] align-center">
<div v-if="!isLocked" class="flex-grow flex justify-end gap-1 min-h-[30px] align-center">
<MdiArrowExpand
class="select-none transform text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-arrow-expand"
@click="childListDlg = true"

5
packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue

@ -5,6 +5,7 @@ import {
CellValueInj,
ColumnInj,
IsFormInj,
IsLockedInj,
ReadonlyInj,
ReloadViewDataHookInj,
RowInj,
@ -33,6 +34,8 @@ const isForm = inject(IsFormInj)
const readonly = inject(ReadonlyInj, false)
const isLocked = inject(IsLockedInj)
const listItemsDlg = ref(false)
const childListDlg = ref(false)
@ -88,7 +91,7 @@ const unlinkRef = async (rec: Record<string, any>) => {
</template>
</div>
<div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<div v-if="!isLocked" class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<MdiArrowExpand
class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-arrow-expand"
@click="childListDlg = true"

6
packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue

@ -20,6 +20,8 @@ const active = inject(ActiveCellInj, ref(false))
const isForm = inject(IsFormInj)!
const isLocked = inject(IsLockedInj, ref(false))
const expandedFormDlg = ref(false)
</script>
@ -37,13 +39,13 @@ export default {
>
<span class="name">{{ value }}</span>
<div v-show="active || isForm" v-if="!readonly" class="flex align-center">
<div v-show="active || isForm" v-if="!readonly && !isLocked" class="flex align-center">
<MdiCloseThick class="unlink-icon text-xs text-gray-500/50 group-hover:text-gray-500" @click.stop="emit('unlink')" />
</div>
<Suspense>
<ExpandedForm
v-if="!readonly && expandedFormDlg"
v-if="!readonly && !isLocked && expandedFormDlg"
v-model="expandedFormDlg"
:row="{ row: item }"
:meta="relatedTableMeta"

2
packages/nc-gui-v2/composables/useSharedView.ts

@ -1,4 +1,4 @@
import type { ColumnType, ExportTypes, FilterType, PaginatedType, SortType, TableType, ViewType } from 'nocodb-sdk'
import type { ExportTypes, FilterType, PaginatedType, SortType, TableType, ViewType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import { useNuxtApp } from '#app'

2
packages/nc-gui-v2/context/index.ts

@ -16,7 +16,7 @@ export const PaginationDataInj: InjectionKey<ReturnType<typeof useViewData>['pag
export const ChangePageInj: InjectionKey<ReturnType<typeof useViewData>['changePage']> = Symbol('pagination-data-injection')
export const IsFormInj: InjectionKey<Ref<boolean>> = Symbol('is-form-injection')
export const IsGridInj: InjectionKey<boolean> = Symbol('is-grid-injection')
export const IsLockedInj: InjectionKey<boolean> = Symbol('is-locked-injection')
export const IsLockedInj: InjectionKey<Ref<boolean>> = Symbol('is-locked-injection')
export const CellValueInj: InjectionKey<Ref<any>> = Symbol('cell-value-injection')
export const ActiveViewInj: InjectionKey<Ref<ViewType>> = Symbol('active-view-injection')
export const ReadonlyInj: InjectionKey<boolean> = Symbol('readonly-injection')

5
packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue

@ -4,6 +4,7 @@ import {
navigateTo,
onKeyStroke,
openLink,
provide,
provideSidebar,
ref,
useClipboard,
@ -28,6 +29,10 @@ const { isUIAllowed } = useUIPermission()
const { copy } = useClipboard()
const isLocked = ref(false)
provide('TreeViewIsLockedInj', isLocked)
// create a new sidebar state
const { isOpen, toggle } = provideSidebar({ isOpen: true })

Loading…
Cancel
Save