Browse Source

Merge branch 'develop' into qr-prototyping-cleanedup

pull/4468/head
Daniel Spaude 2 years ago
parent
commit
06fc3ac16d
No known key found for this signature in database
GPG Key ID: 654A3D1FA4F35FFE
  1. 82
      packages/nc-gui/components/smartsheet/Grid.vue
  2. 54
      packages/nc-gui/composables/useMultiSelect/cellRange.ts
  3. 29
      packages/nc-gui/composables/useMultiSelect/copyValue.ts
  4. 272
      packages/nc-gui/composables/useMultiSelect/index.ts
  5. 2
      packages/nc-gui/context/index.ts
  6. 3
      packages/nc-gui/lang/ar.json
  7. 3
      packages/nc-gui/lang/bn_IN.json
  8. 3
      packages/nc-gui/lang/da.json
  9. 3
      packages/nc-gui/lang/de.json
  10. 3
      packages/nc-gui/lang/en.json
  11. 3
      packages/nc-gui/lang/es.json
  12. 3
      packages/nc-gui/lang/fa.json
  13. 3
      packages/nc-gui/lang/fi.json
  14. 3
      packages/nc-gui/lang/fr.json
  15. 3
      packages/nc-gui/lang/he.json
  16. 3
      packages/nc-gui/lang/hi.json
  17. 3
      packages/nc-gui/lang/hr.json
  18. 3
      packages/nc-gui/lang/id.json
  19. 3
      packages/nc-gui/lang/it.json
  20. 3
      packages/nc-gui/lang/ja.json
  21. 3
      packages/nc-gui/lang/ko.json
  22. 3
      packages/nc-gui/lang/lv.json
  23. 3
      packages/nc-gui/lang/nl.json
  24. 3
      packages/nc-gui/lang/no.json
  25. 3
      packages/nc-gui/lang/pl.json
  26. 3
      packages/nc-gui/lang/pt.json
  27. 3
      packages/nc-gui/lang/pt_BR.json
  28. 3
      packages/nc-gui/lang/ru.json
  29. 3
      packages/nc-gui/lang/sl.json
  30. 3
      packages/nc-gui/lang/sv.json
  31. 3
      packages/nc-gui/lang/th.json
  32. 3
      packages/nc-gui/lang/tr.json
  33. 3
      packages/nc-gui/lang/uk.json
  34. 3
      packages/nc-gui/lang/vi.json
  35. 5
      packages/nc-gui/lang/zh-Hans.json
  36. 3
      packages/nc-gui/lang/zh-Hant.json
  37. 96
      packages/nc-gui/package-lock.json
  38. 2
      packages/nc-gui/package.json
  39. 3
      packages/nc-gui/utils/cell.ts
  40. 8
      packages/nc-gui/utils/parsers/ExcelTemplateAdapter.ts
  41. 16
      packages/nc-gui/utils/parsers/parserHelpers.ts
  42. 8
      packages/nc-gui/utils/urlUtils.ts
  43. 2
      packages/nc-gui/windi.config.ts
  44. 47
      packages/nocodb/package-lock.json

82
packages/nc-gui/components/smartsheet/Grid.vue

@ -162,15 +162,8 @@ const getContainerScrollForElement = (
return scroll return scroll
} }
const { selectCell, selectBlock, selectedRange, clearRangeRows, startSelectRange, selected } = useMultiSelect( const { selectCell, startSelectRange, endSelectRange, clearSelectedRange, copyValue, isCellSelected, selectedCell } =
fields, useMultiSelect(fields, data, $$(editEnabled), isPkAvail, clearCell, makeEditable, scrollToCell, (e: KeyboardEvent) => {
data,
$$(editEnabled),
isPkAvail,
clearCell,
makeEditable,
scrollToCell,
(e: KeyboardEvent) => {
// ignore navigating if picker(Date, Time, DateTime, Year) // ignore navigating if picker(Date, Time, DateTime, Year)
// or single/multi select options is open // or single/multi select options is open
const activePickerOrDropdownEl = document.querySelector( const activePickerOrDropdownEl = document.querySelector(
@ -188,9 +181,9 @@ const { selectCell, selectBlock, selectedRange, clearRangeRows, startSelectRange
const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey
if (e.key === ' ') { if (e.key === ' ') {
if (selected.row !== null && !editEnabled) { if (selectedCell.row !== null && !editEnabled) {
e.preventDefault() e.preventDefault()
const row = data.value[selected.row] const row = data.value[selectedCell.row]
expandForm(row) expandForm(row)
return true return true
} }
@ -208,37 +201,37 @@ const { selectCell, selectBlock, selectedRange, clearRangeRows, startSelectRange
if (cmdOrCtrl) { if (cmdOrCtrl) {
switch (e.key) { switch (e.key) {
case 'ArrowUp': case 'ArrowUp':
selected.row = 0 selectedCell.row = 0
selected.col = selected.col ?? 0 selectedCell.col = selectedCell.col ?? 0
scrollToCell?.() scrollToCell?.()
editEnabled = false editEnabled = false
return true return true
case 'ArrowDown': case 'ArrowDown':
selected.row = data.value.length - 1 selectedCell.row = data.value.length - 1
selected.col = selected.col ?? 0 selectedCell.col = selectedCell.col ?? 0
scrollToCell?.() scrollToCell?.()
editEnabled = false editEnabled = false
return true return true
case 'ArrowRight': case 'ArrowRight':
selected.row = selected.row ?? 0 selectedCell.row = selectedCell.row ?? 0
selected.col = fields.value?.length - 1 selectedCell.col = fields.value?.length - 1
scrollToCell?.() scrollToCell?.()
editEnabled = false editEnabled = false
return true return true
case 'ArrowLeft': case 'ArrowLeft':
selected.row = selected.row ?? 0 selectedCell.row = selectedCell.row ?? 0
selected.col = 0 selectedCell.col = 0
scrollToCell?.() scrollToCell?.()
editEnabled = false editEnabled = false
return true return true
} }
} }
}, })
)
function scrollToCell(row?: number | null, col?: number | null) { function scrollToCell(row?: number | null, col?: number | null) {
row = row ?? selected.row row = row ?? selectedCell.row
col = col ?? selected.col col = col ?? selectedCell.col
if (row !== undefined && col !== undefined && row !== null && col !== null) { if (row !== undefined && col !== undefined && row !== null && col !== null) {
// get active cell // get active cell
const rows = tbodyEl.value?.querySelectorAll('tr') const rows = tbodyEl.value?.querySelectorAll('tr')
@ -393,10 +386,12 @@ useEventListener(document, 'keyup', async (e: KeyboardEvent) => {
/** On clicking outside of table reset active cell */ /** On clicking outside of table reset active cell */
const smartTable = ref(null) const smartTable = ref(null)
onClickOutside(smartTable, (e) => { onClickOutside(smartTable, (e) => {
clearRangeRows() // do nothing if context menu was open
if (selected.col === null) return if (contextMenu.value) return
clearSelectedRange()
if (selectedCell.col === null) return
const activeCol = fields.value[selected.col] const activeCol = fields.value[selectedCell.col]
if (editEnabled && (isVirtualCol(activeCol) || activeCol.uidt === UITypes.JSON)) return if (editEnabled && (isVirtualCol(activeCol) || activeCol.uidt === UITypes.JSON)) return
@ -417,25 +412,25 @@ onClickOutside(smartTable, (e) => {
return return
} }
selected.row = null selectedCell.row = null
selected.col = null selectedCell.col = null
}) })
const onNavigate = (dir: NavigateDir) => { const onNavigate = (dir: NavigateDir) => {
if (selected.row === null || selected.col === null) return if (selectedCell.row === null || selectedCell.col === null) return
editEnabled = false editEnabled = false
switch (dir) { switch (dir) {
case NavigateDir.NEXT: case NavigateDir.NEXT:
if (selected.row < data.value.length - 1) { if (selectedCell.row < data.value.length - 1) {
selected.row++ selectedCell.row++
} else { } else {
addEmptyRow() addEmptyRow()
selected.row++ selectedCell.row++
} }
break break
case NavigateDir.PREV: case NavigateDir.PREV:
if (selected.row > 0) { if (selectedCell.row > 0) {
selected.row-- selectedCell.row--
} }
break break
} }
@ -696,9 +691,7 @@ watch(
:key="columnObj.id" :key="columnObj.id"
class="cell relative cursor-pointer nc-grid-cell" class="cell relative cursor-pointer nc-grid-cell"
:class="{ :class="{
'active': 'active': hasEditPermission && isCellSelected(rowIndex, colIndex),
(hasEditPermission && selected.col === colIndex && selected.row === rowIndex) ||
(hasEditPermission && selectedRange(rowIndex, colIndex)),
'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row), 'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row),
}" }"
:data-testid="`cell-${columnObj.title}-${rowIndex}`" :data-testid="`cell-${columnObj.title}-${rowIndex}`"
@ -708,7 +701,7 @@ watch(
@click="selectCell(rowIndex, colIndex)" @click="selectCell(rowIndex, colIndex)"
@dblclick="makeEditable(row, columnObj)" @dblclick="makeEditable(row, columnObj)"
@mousedown="startSelectRange($event, rowIndex, colIndex)" @mousedown="startSelectRange($event, rowIndex, colIndex)"
@mouseover="selectBlock(rowIndex, colIndex)" @mouseover="endSelectRange(rowIndex, colIndex)"
@contextmenu="showContextMenu($event, { row: rowIndex, col: colIndex })" @contextmenu="showContextMenu($event, { row: rowIndex, col: colIndex })"
> >
<div v-if="!switchingTab" class="w-full h-full"> <div v-if="!switchingTab" class="w-full h-full">
@ -716,7 +709,7 @@ watch(
v-if="isVirtualCol(columnObj)" v-if="isVirtualCol(columnObj)"
v-model="row.row[columnObj.title]" v-model="row.row[columnObj.title]"
:column="columnObj" :column="columnObj"
:active="selected.col === colIndex && selected.row === rowIndex" :active="selectedCell.col === colIndex && selectedCell.row === rowIndex"
:row="row" :row="row"
@navigate="onNavigate" @navigate="onNavigate"
/> />
@ -726,10 +719,10 @@ watch(
v-model="row.row[columnObj.title]" v-model="row.row[columnObj.title]"
:column="columnObj" :column="columnObj"
:edit-enabled=" :edit-enabled="
!!hasEditPermission && !!editEnabled && selected.col === colIndex && selected.row === rowIndex !!hasEditPermission && !!editEnabled && selectedCell.col === colIndex && selectedCell.row === rowIndex
" "
:row-index="rowIndex" :row-index="rowIndex"
:active="selected.col === colIndex && selected.row === rowIndex" :active="selectedCell.col === colIndex && selectedCell.row === rowIndex"
@update:edit-enabled="editEnabled = $event" @update:edit-enabled="editEnabled = $event"
@save="updateOrSaveRow(row, columnObj.title, state)" @save="updateOrSaveRow(row, columnObj.title, state)"
@navigate="onNavigate" @navigate="onNavigate"
@ -794,6 +787,13 @@ watch(
{{ $t('activity.insertRow') }} {{ $t('activity.insertRow') }}
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="contextMenuTarget" @click="copyValue(contextMenuTarget)">
<div v-e="['a:row:copy']" class="nc-project-menu-item">
<!-- Copy -->
{{ $t('general.copy') }}
</div>
</a-menu-item>
</a-menu> </a-menu>
</template> </template>
</a-dropdown> </a-dropdown>

54
packages/nc-gui/composables/useMultiSelect/cellRange.ts

@ -0,0 +1,54 @@
export interface Cell {
row: number | null
col: number | null
}
export class CellRange {
_start: Cell | null
_end: Cell | null
constructor(start = null, end = null) {
this._start = start
this._end = end ?? this._start
}
get start() {
return {
row: Math.min(this._start?.row ?? NaN, this._end?.row ?? NaN),
col: Math.min(this._start?.col ?? NaN, this._end?.col ?? NaN),
}
}
get end() {
return {
row: Math.max(this._start?.row ?? NaN, this._end?.row ?? NaN),
col: Math.max(this._start?.col ?? NaN, this._end?.col ?? NaN),
}
}
startRange(value: Cell) {
if (value == null) {
return
}
this._start = value
this._end = value
}
endRange(value: Cell) {
if (value == null) {
return
}
this._end = value
}
clear() {
this._start = null
this._end = null
}
isEmpty() {
return this._start == null || this._end == null
}
}

29
packages/nc-gui/composables/useMultiSelect/copyValue.ts

@ -0,0 +1,29 @@
import { UITypes } from '../../../nocodb-sdk'
import type { ColumnType } from '../../../nocodb-sdk'
import type { Row } from '~/lib'
export const copyTable = async (rows: Row[], cols: ColumnType[]) => {
let copyHTML = '<table>'
let copyPlainText = ''
rows.forEach((row) => {
let copyRow = '<tr>'
cols.forEach((col) => {
let value = (col.title && row.row[col.title]) ?? ''
if (typeof value === 'object') {
value = JSON.stringify(value)
}
copyRow += `<td>${value}</td>`
copyPlainText = `${copyPlainText} ${value} \t`
})
copyHTML += `${copyRow}</tr>`
copyPlainText = `${copyPlainText.trim()}\n`
})
copyHTML += '</table>'
copyPlainText.trim()
const blobHTML = new Blob([copyHTML], { type: 'text/html' })
const blobPlainText = new Blob([copyPlainText], { type: 'text/plain' })
return navigator.clipboard.write([new ClipboardItem({ [blobHTML.type]: blobHTML, [blobPlainText.type]: blobPlainText })])
}

272
packages/nc-gui/composables/useMultiSelect/index.ts

@ -1,18 +1,16 @@
import type { MaybeRef } from '@vueuse/core' import type { MaybeRef } from '@vueuse/core'
import { UITypes } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import { message, reactive, ref, unref, useCopy, useEventListener, useI18n } from '#imports' import type { Cell } from './cellRange'
import { CellRange } from './cellRange'
interface SelectedBlock { import { copyTable, message, reactive, ref, unref, useCopy, useEventListener, useI18n } from '#imports'
row: number | null import type { Row } from '~/lib'
col: number | null
}
/** /**
* Utility to help with multi-selecting rows/cells in the smartsheet * Utility to help with multi-selecting rows/cells in the smartsheet
*/ */
export function useMultiSelect( export function useMultiSelect(
fields: MaybeRef<any[]>, fields: MaybeRef<ColumnType[]>,
data: MaybeRef<any[]>, data: MaybeRef<Row[]>,
_editEnabled: MaybeRef<boolean>, _editEnabled: MaybeRef<boolean>,
isPkAvail: MaybeRef<boolean | undefined>, isPkAvail: MaybeRef<boolean | undefined>,
clearCell: Function, clearCell: Function,
@ -26,99 +24,92 @@ export function useMultiSelect(
const editEnabled = ref(_editEnabled) const editEnabled = ref(_editEnabled)
const selected = reactive<SelectedBlock>({ row: null, col: null }) const selectedCell = reactive<Cell>({ row: null, col: null })
const selectedRange = reactive(new CellRange())
// save the first and the last column where the mouse is down while the value isSelectedRow is true let isMouseDown = $ref(false)
const selectedRows = reactive({ startCol: NaN, endCol: NaN, startRow: NaN, endRow: NaN })
// calculate the min and the max column where the mouse is down while the value isSelectedRow is true
const rangeRows = reactive({ minRow: NaN, maxRow: NaN, minCol: NaN, maxCol: NaN })
// check if mouse is down or up false=mouseup and true=mousedown
let isSelectedBlock = $ref(false)
const columnLength = $computed(() => unref(fields)?.length) const columnLength = $computed(() => unref(fields)?.length)
async function copyValue(ctx?: Cell) {
try {
if (!selectedRange.isEmpty()) {
const cprows = unref(data).slice(selectedRange.start.row, selectedRange.end.row + 1) // slice the selected rows for copy
const cpcols = unref(fields).slice(selectedRange.start.col, selectedRange.end.col + 1) // slice the selected cols for copy
await copyTable(cprows, cpcols)
message.success(t('msg.info.copiedToClipboard'))
} else {
// if copy was called with context (right click position) - copy value from context
// else if there is just one selected cell, copy it's value
const cpRow = ctx?.row ?? selectedCell?.row
const cpCol = ctx?.col ?? selectedCell?.col
if (cpRow != null && cpCol != null) {
const rowObj = unref(data)[cpRow]
const columnObj = unref(fields)[cpCol]
let textToCopy = (columnObj.title && rowObj.row[columnObj.title]) || ''
if (typeof textToCopy === 'object') {
textToCopy = JSON.stringify(textToCopy)
}
await copy(textToCopy)
message.success(t('msg.info.copiedToClipboard'))
}
}
} catch {
message.error(t('msg.error.copyToClipboardError'))
}
}
function selectCell(row: number, col: number) { function selectCell(row: number, col: number) {
clearRangeRows() selectedRange.clear()
if (selected.row === row && selected.col === col) return if (selectedCell.row === row && selectedCell.col === col) return
editEnabled.value = false editEnabled.value = false
selected.row = row selectedCell.row = row
selected.col = col selectedCell.col = col
} }
function selectBlock(row: number, col: number) { function endSelectRange(row: number, col: number) {
// if selected.col and selected.row are null and isSelectedBlock is true that means you are selecting a block if (!isMouseDown) {
if (selected.col === null || selected.row === null) { return
if (isSelectedBlock) {
// save the next value after the selectionStart
selectedRows.endCol = col
selectedRows.endRow = row
}
} else if (selected.col !== col || selected.row !== row) {
// if selected.col and selected.row is not null but the selected col and row is not equal at the row and col where the mouse is clicking
// and isSelectedBlock is true that means you are selecting a block
if (isSelectedBlock) {
selected.col = null
selected.row = null
// save the next value after the selectionStart
selectedRows.endCol = col
selectedRows.endRow = row
}
} }
selectedCell.row = null
selectedCell.col = null
selectedRange.endRange({ row, col })
} }
function selectedRange(row: number, col: number) { function isCellSelected(row: number, col: number) {
if ( if (selectedCell?.row === row && selectedCell?.col === col) {
!isNaN(selectedRows.startRow) && return true
!isNaN(selectedRows.startCol) && }
!isNaN(selectedRows.endRow) &&
!isNaN(selectedRows.endCol)
) {
// check if column selection is up or down
rangeRows.minRow = Math.min(selectedRows.startRow, selectedRows.endRow)
rangeRows.maxRow = Math.max(selectedRows.startRow, selectedRows.endRow)
rangeRows.minCol = Math.min(selectedRows.startCol, selectedRows.endCol)
rangeRows.maxCol = Math.max(selectedRows.startCol, selectedRows.endCol)
// return if the column is in between the selection if (selectedRange.isEmpty()) {
return col >= rangeRows.minCol && col <= rangeRows.maxCol && row >= rangeRows.minRow && row <= rangeRows.maxRow
} else {
return false return false
} }
return (
col >= selectedRange.start.col &&
col <= selectedRange.end.col &&
row >= selectedRange.start.row &&
row <= selectedRange.end.row
)
} }
function startSelectRange(event: MouseEvent, row: number, col: number) { function startSelectRange(event: MouseEvent, row: number, col: number) {
// if editEnabled but the selected col or the selected row is not equal like the actual row or col, enabled selected multiple rows // if there was a right click on selected range, don't restart the selection
if (unref(editEnabled) && (selected.col !== col || selected.row !== row)) { const leftClickButton = 0
event.preventDefault() if (event?.button !== leftClickButton && isCellSelected(row, col)) {
} else if (!unref(editEnabled)) { return
// if editEnabled is not true, enabled selected multiple rows
event.preventDefault()
} }
// clear the selection when the mouse is down if (unref(editEnabled)) {
selectedRows.startCol = NaN event.preventDefault()
selectedRows.endCol = NaN return
selectedRows.startRow = NaN }
selectedRows.endRow = NaN
// asing where the selection start
selectedRows.startCol = col
selectedRows.startRow = row
isSelectedBlock = true
}
function clearRangeRows() { isMouseDown = true
// when the selection starts or ends or when enter/arrow/tab is pressed selectedRange.clear()
// this clear the previous selection selectedRange.startRange({ row, col })
rangeRows.minCol = NaN
rangeRows.maxCol = NaN
rangeRows.minRow = NaN
rangeRows.maxRow = NaN
selectedRows.startRow = NaN
selectedRows.startCol = NaN
selectedRows.endRow = NaN
selectedRows.endCol = NaN
} }
useEventListener(document, 'mouseup', (e) => { useEventListener(document, 'mouseup', (e) => {
@ -127,7 +118,7 @@ export function useMultiSelect(
e.preventDefault() e.preventDefault()
} }
isSelectedBlock = false isMouseDown = false
}) })
const onKeyDown = async (e: KeyboardEvent) => { const onKeyDown = async (e: KeyboardEvent) => {
@ -136,41 +127,36 @@ export function useMultiSelect(
return true return true
} }
if ( if (!selectedRange.isEmpty()) {
!isNaN(selectedRows.startRow) &&
!isNaN(selectedRows.startCol) &&
!isNaN(selectedRows.endRow) &&
!isNaN(selectedRows.endCol)
) {
// In case the user press tabs or arrows keys // In case the user press tabs or arrows keys
selected.row = selectedRows.startRow selectedCell.row = selectedRange.start.row
selected.col = selectedRows.startCol selectedCell.col = selectedRange.start.col
} }
if (selected.row === null || selected.col === null) return if (selectedCell.row === null || selectedCell.col === null) return
/** on tab key press navigate through cells */ /** on tab key press navigate through cells */
switch (e.key) { switch (e.key) {
case 'Tab': case 'Tab':
e.preventDefault() e.preventDefault()
clearRangeRows() selectedRange.clear()
if (e.shiftKey) { if (e.shiftKey) {
if (selected.col > 0) { if (selectedCell.col > 0) {
selected.col-- selectedCell.col--
editEnabled.value = false editEnabled.value = false
} else if (selected.row > 0) { } else if (selectedCell.row > 0) {
selected.row-- selectedCell.row--
selected.col = unref(columnLength) - 1 selectedCell.col = unref(columnLength) - 1
editEnabled.value = false editEnabled.value = false
} }
} else { } else {
if (selected.col < unref(columnLength) - 1) { if (selectedCell.col < unref(columnLength) - 1) {
selected.col++ selectedCell.col++
editEnabled.value = false editEnabled.value = false
} else if (selected.row < unref(data).length - 1) { } else if (selectedCell.row < unref(data).length - 1) {
selected.row++ selectedCell.row++
selected.col = 0 selectedCell.col = 0
editEnabled.value = false editEnabled.value = false
} }
} }
@ -179,90 +165,63 @@ export function useMultiSelect(
/** on enter key press make cell editable */ /** on enter key press make cell editable */
case 'Enter': case 'Enter':
e.preventDefault() e.preventDefault()
clearRangeRows() selectedRange.clear()
makeEditable(unref(data)[selected.row], unref(fields)[selected.col]) makeEditable(unref(data)[selectedCell.row], unref(fields)[selectedCell.col])
break break
/** on delete key press clear cell */ /** on delete key press clear cell */
case 'Delete': case 'Delete':
e.preventDefault() e.preventDefault()
clearRangeRows() selectedRange.clear()
await clearCell(selected as { row: number; col: number }) await clearCell(selectedCell as { row: number; col: number })
break break
/** on arrow key press navigate through cells */ /** on arrow key press navigate through cells */
case 'ArrowRight': case 'ArrowRight':
e.preventDefault() e.preventDefault()
clearRangeRows() selectedRange.clear()
if (selected.col < unref(columnLength) - 1) { if (selectedCell.col < unref(columnLength) - 1) {
selected.col++ selectedCell.col++
scrollToActiveCell?.() scrollToActiveCell?.()
editEnabled.value = false editEnabled.value = false
} }
break break
case 'ArrowLeft': case 'ArrowLeft':
clearRangeRows() selectedRange.clear()
e.preventDefault() e.preventDefault()
clearRangeRows() if (selectedCell.col > 0) {
if (selected.col > 0) { selectedCell.col--
selected.col--
scrollToActiveCell?.() scrollToActiveCell?.()
editEnabled.value = false editEnabled.value = false
} }
break break
case 'ArrowUp': case 'ArrowUp':
clearRangeRows() selectedRange.clear()
e.preventDefault() e.preventDefault()
clearRangeRows() if (selectedCell.row > 0) {
if (selected.row > 0) { selectedCell.row--
selected.row--
scrollToActiveCell?.() scrollToActiveCell?.()
editEnabled.value = false editEnabled.value = false
} }
break break
case 'ArrowDown': case 'ArrowDown':
clearRangeRows() selectedRange.clear()
e.preventDefault() e.preventDefault()
clearRangeRows() if (selectedCell.row < unref(data).length - 1) {
if (selected.row < unref(data).length - 1) { selectedCell.row++
selected.row++
scrollToActiveCell?.() scrollToActiveCell?.()
editEnabled.value = false editEnabled.value = false
} }
break break
default: default:
{ {
const rowObj = unref(data)[selected.row] const rowObj = unref(data)[selectedCell.row]
const columnObj = unref(fields)[selected.col] const columnObj = unref(fields)[selectedCell.col]
let cptext = '' // variable for save the text to be copy
if (!isNaN(rangeRows.minRow) && !isNaN(rangeRows.maxRow) && !isNaN(rangeRows.minCol) && !isNaN(rangeRows.maxCol)) {
const cprows = unref(data).slice(rangeRows.minRow, rangeRows.maxRow + 1) // slice the selected rows for copy
const cpcols = unref(fields).slice(rangeRows.minCol, rangeRows.maxCol + 1) // slice the selected cols for copy
cprows.forEach((row) => {
cpcols.forEach((col) => {
// todo: JSON stringify the attachment cell and LTAR contents for copy
// filter attachment cells and LATR cells from copy
if (col.uidt !== UITypes.Attachment && col.uidt !== UITypes.LinkToAnotherRecord) {
cptext = `${cptext} ${row.row[col.title]} \t`
}
})
cptext = `${cptext.trim()}\n`
})
cptext.trim()
} else {
cptext = rowObj.row[columnObj.title] || ''
}
if ((!unref(editEnabled) && e.metaKey) || e.ctrlKey) { if ((!unref(editEnabled) && e.metaKey) || e.ctrlKey) {
switch (e.keyCode) { switch (e.keyCode) {
// copy - ctrl/cmd +c // copy - ctrl/cmd +c
case 67: case 67:
await copy(cptext) await copyValue()
break break
} }
} }
@ -277,7 +236,7 @@ export function useMultiSelect(
// Update not allowed for table which doesn't have primary Key // Update not allowed for table which doesn't have primary Key
return message.info(t('msg.info.updateNotAllowedWithoutPK')) return message.info(t('msg.info.updateNotAllowedWithoutPK'))
} }
if (makeEditable(rowObj, columnObj)) { if (makeEditable(rowObj, columnObj) && columnObj.title) {
rowObj.row[columnObj.title] = '' rowObj.row[columnObj.title] = ''
} }
// editEnabled = true // editEnabled = true
@ -291,12 +250,11 @@ export function useMultiSelect(
return { return {
selectCell, selectCell,
selectBlock,
selectedRange,
clearRangeRows,
startSelectRange, startSelectRange,
selected, endSelectRange,
selectedRows, clearSelectedRange: selectedRange.clear.bind(selectedRange),
rangeRows, copyValue,
isCellSelected,
selectedCell,
} }
} }

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

@ -26,7 +26,7 @@ export const ReloadViewDataHookInj: InjectionKey<EventHook<boolean | void>> = Sy
export const ReloadViewMetaHookInj: InjectionKey<EventHook<boolean | void>> = Symbol('reload-view-meta-injection') export const ReloadViewMetaHookInj: InjectionKey<EventHook<boolean | void>> = Symbol('reload-view-meta-injection')
export const ReloadRowDataHookInj: InjectionKey<EventHook<boolean | void>> = Symbol('reload-row-data-injection') export const ReloadRowDataHookInj: InjectionKey<EventHook<boolean | void>> = Symbol('reload-row-data-injection')
export const OpenNewRecordFormHookInj: InjectionKey<EventHook<void>> = Symbol('open-new-record-form-injection') export const OpenNewRecordFormHookInj: InjectionKey<EventHook<void>> = Symbol('open-new-record-form-injection')
export const FieldsInj: InjectionKey<Ref<any[]>> = Symbol('fields-injection') export const FieldsInj: InjectionKey<Ref<ColumnType[]>> = Symbol('fields-injection')
export const EditModeInj: InjectionKey<Ref<boolean>> = Symbol('edit-mode-injection') export const EditModeInj: InjectionKey<Ref<boolean>> = Symbol('edit-mode-injection')
export const SharedViewPasswordInj: InjectionKey<Ref<string | null>> = Symbol('shared-view-password-injection') export const SharedViewPasswordInj: InjectionKey<Ref<string | null>> = Symbol('shared-view-password-injection')
export const CellUrlDisableOverlayInj: InjectionKey<Ref<boolean>> = Symbol('cell-url-disable-url') export const CellUrlDisableOverlayInj: InjectionKey<Ref<boolean>> = Symbol('cell-url-disable-url')

3
packages/nc-gui/lang/ar.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "تصدير البيانات الوصفية للمشروع بنجاح", "exportMetadata": "تصدير البيانات الوصفية للمشروع بنجاح",

3
packages/nc-gui/lang/bn_IN.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "পরকলপ ম সফলভ রফতি কর", "exportMetadata": "পরকলপ ম সফলভ রফতি কর",

3
packages/nc-gui/lang/da.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Project Metadata eksporteres med succes", "exportMetadata": "Project Metadata eksporteres med succes",

3
packages/nc-gui/lang/de.json

@ -667,7 +667,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Projektmetadaten erfolgreich exportiert", "exportMetadata": "Projektmetadaten erfolgreich exportiert",

3
packages/nc-gui/lang/en.json

@ -674,7 +674,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Project metadata exported successfully", "exportMetadata": "Project metadata exported successfully",

3
packages/nc-gui/lang/es.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Metadatos del proyecto exportados con éxito.", "exportMetadata": "Metadatos del proyecto exportados con éxito.",

3
packages/nc-gui/lang/fa.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "فراداده پروژه با موفقیت خارج شد", "exportMetadata": "فراداده پروژه با موفقیت خارج شد",

3
packages/nc-gui/lang/fi.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Project Metadata viedään onnistuneesti", "exportMetadata": "Project Metadata viedään onnistuneesti",

3
packages/nc-gui/lang/fr.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Les métadonnées de projet sont exportées avec succès", "exportMetadata": "Les métadonnées de projet sont exportées avec succès",

3
packages/nc-gui/lang/he.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "פרויקט Metadata מיוצא בהצלחה", "exportMetadata": "פרויקט Metadata מיוצא בהצלחה",

3
packages/nc-gui/lang/hi.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "परिजन सफलतवक नित क गई", "exportMetadata": "परिजन सफलतवक नित क गई",

3
packages/nc-gui/lang/hr.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Projektni metapodaci su uspješno izvozili", "exportMetadata": "Projektni metapodaci su uspješno izvozili",

3
packages/nc-gui/lang/id.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Metadata proyek berhasil diekspor", "exportMetadata": "Metadata proyek berhasil diekspor",

3
packages/nc-gui/lang/it.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Metadati del progetto esportati con successo", "exportMetadata": "Metadati del progetto esportati con successo",

3
packages/nc-gui/lang/ja.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "プロジェクトメタデータは正常にエクスポートされました", "exportMetadata": "プロジェクトメタデータは正常にエクスポートされました",

3
packages/nc-gui/lang/ko.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "프로젝트 메타 데이터를 성공적으로 내보냈습니다.", "exportMetadata": "프로젝트 메타 데이터를 성공적으로 내보냈습니다.",

3
packages/nc-gui/lang/lv.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Projekta metadati eksportēti veiksmīgi", "exportMetadata": "Projekta metadati eksportēti veiksmīgi",

3
packages/nc-gui/lang/nl.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Project metadata met succes geëxporteerd", "exportMetadata": "Project metadata met succes geëxporteerd",

3
packages/nc-gui/lang/no.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Prosjektmetadata eksporteres vellykket", "exportMetadata": "Prosjektmetadata eksporteres vellykket",

3
packages/nc-gui/lang/pl.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Klucz parametru nie może być pusty", "parameterKeyCannotBeEmpty": "Klucz parametru nie może być pusty",
"duplicateParameterKeysAreNotAllowed": "Zduplikowane klucze parametrów są niedozwolone", "duplicateParameterKeysAreNotAllowed": "Zduplikowane klucze parametrów są niedozwolone",
"fieldRequired": "{value} nie może być puste.", "fieldRequired": "{value} nie może być puste.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Pomyślnie wyeksportowano metadane projektu", "exportMetadata": "Pomyślnie wyeksportowano metadane projektu",

3
packages/nc-gui/lang/pt.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Metadados do projeto exportado com sucesso", "exportMetadata": "Metadados do projeto exportado com sucesso",

3
packages/nc-gui/lang/pt_BR.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Metadados do projeto exportado com sucesso", "exportMetadata": "Metadados do projeto exportado com sucesso",

3
packages/nc-gui/lang/ru.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Метаданные проекта успешно экспортированы", "exportMetadata": "Метаданные проекта успешно экспортированы",

3
packages/nc-gui/lang/sl.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Projekt Metapodatki se je uspešno izvozil", "exportMetadata": "Projekt Metapodatki se je uspešno izvozil",

3
packages/nc-gui/lang/sv.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Projektmetadata exporterades framgångsrikt", "exportMetadata": "Projektmetadata exporterades framgångsrikt",

3
packages/nc-gui/lang/th.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "ขอมลเมตาของโครงการสงออกเรยบรอยแลว", "exportMetadata": "ขอมลเมตาของโครงการสงออกเรยบรอยแลว",

3
packages/nc-gui/lang/tr.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Proje metaverileri başarıyla dışa aktarıldı", "exportMetadata": "Proje metaverileri başarıyla dışa aktarıldı",

3
packages/nc-gui/lang/uk.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Метадані проекту успішно експортується", "exportMetadata": "Метадані проекту успішно експортується",

3
packages/nc-gui/lang/vi.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "Metadata dự án xuất khẩu thành công", "exportMetadata": "Metadata dự án xuất khẩu thành công",

5
packages/nc-gui/lang/zh-Hans.json

@ -368,7 +368,7 @@
"setPrimary": "设置为主要值", "setPrimary": "设置为主要值",
"addRow": "添加新行", "addRow": "添加新行",
"saveRow": "保存行", "saveRow": "保存行",
"saveAndExit": "Save & Exit", "saveAndExit": "保存并退出",
"saveAndStay": "Save & Stay", "saveAndStay": "Save & Stay",
"insertRow": "插入新行", "insertRow": "插入新行",
"deleteRow": "删除行", "deleteRow": "删除行",
@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "参数键不能为空", "parameterKeyCannotBeEmpty": "参数键不能为空",
"duplicateParameterKeysAreNotAllowed": "不允许重复的参数键", "duplicateParameterKeysAreNotAllowed": "不允许重复的参数键",
"fieldRequired": "{value} 不能为空。", "fieldRequired": "{value} 不能为空。",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "项目元数据成功导出", "exportMetadata": "项目元数据成功导出",

3
packages/nc-gui/lang/zh-Hant.json

@ -666,7 +666,8 @@
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty", "parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed", "duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.", "fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible" "projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
}, },
"toast": { "toast": {
"exportMetadata": "專案中繼資料已成功匯出", "exportMetadata": "專案中繼資料已成功匯出",

96
packages/nc-gui/package-lock.json generated

@ -33,6 +33,7 @@
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"tinycolor2": "^1.4.2", "tinycolor2": "^1.4.2",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"validator": "^13.7.0",
"vue-dompurify-html": "^3.0.0", "vue-dompurify-html": "^3.0.0",
"vue-github-button": "^3.0.3", "vue-github-button": "^3.0.3",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
@ -63,6 +64,7 @@
"@types/papaparse": "^5.3.2", "@types/papaparse": "^5.3.2",
"@types/sortablejs": "^1.13.0", "@types/sortablejs": "^1.13.0",
"@types/tinycolor2": "^1.4.3", "@types/tinycolor2": "^1.4.3",
"@types/validator": "^13.7.10",
"@vitest/ui": "^0.18.0", "@vitest/ui": "^0.18.0",
"@vue/compiler-sfc": "^3.2.37", "@vue/compiler-sfc": "^3.2.37",
"@vue/test-utils": "^2.0.2", "@vue/test-utils": "^2.0.2",
@ -85,6 +87,28 @@
"windicss": "^3.5.6" "windicss": "^3.5.6"
} }
}, },
"../nocodb-sdk": {
"version": "0.99.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^1.3.6"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"cspell": "^4.1.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.1",
"typescript": "^4.0.2"
}
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@ -3078,6 +3102,12 @@
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
"dev": true "dev": true
}, },
"node_modules/@types/validator": {
"version": "13.7.10",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz",
"integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==",
"dev": true
},
"node_modules/@types/web-bluetooth": { "node_modules/@types/web-bluetooth": {
"version": "0.0.15", "version": "0.0.15",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz",
@ -8383,6 +8413,7 @@
"version": "1.15.1", "version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
"devOptional": true,
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@ -11738,21 +11769,8 @@
} }
}, },
"node_modules/nocodb-sdk": { "node_modules/nocodb-sdk": {
"version": "0.99.2", "resolved": "../nocodb-sdk",
"resolved": "file:../nocodb-sdk", "link": true
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^1.3.6"
}
},
"node_modules/nocodb-sdk/node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
}, },
"node_modules/node-abi": { "node_modules/node-abi": {
"version": "3.23.0", "version": "3.23.0",
@ -16047,6 +16065,14 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"node_modules/validator": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "2.9.15", "version": "2.9.15",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.9.15.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.15.tgz",
@ -19769,6 +19795,12 @@
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
"dev": true "dev": true
}, },
"@types/validator": {
"version": "13.7.10",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz",
"integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==",
"dev": true
},
"@types/web-bluetooth": { "@types/web-bluetooth": {
"version": "0.0.15", "version": "0.0.15",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz",
@ -23561,7 +23593,8 @@
"follow-redirects": { "follow-redirects": {
"version": "1.15.1", "version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
"devOptional": true
}, },
"form-data": { "form-data": {
"version": "4.0.0", "version": "4.0.0",
@ -26029,20 +26062,22 @@
} }
}, },
"nocodb-sdk": { "nocodb-sdk": {
"version": "0.99.2", "version": "file:../nocodb-sdk",
"requires": { "requires": {
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"axios": "^0.21.1", "axios": "^0.21.1",
"jsep": "^1.3.6" "cspell": "^4.1.0",
}, "eslint": "^7.8.0",
"dependencies": { "eslint-config-prettier": "^6.11.0",
"axios": { "eslint-plugin-eslint-comments": "^3.2.0",
"version": "0.21.4", "eslint-plugin-functional": "^3.0.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "eslint-plugin-import": "^2.22.0",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "eslint-plugin-prettier": "^4.0.0",
"requires": { "jsep": "^1.3.6",
"follow-redirects": "^1.14.0" "npm-run-all": "^4.1.5",
} "prettier": "^2.1.1",
} "typescript": "^4.0.2"
} }
}, },
"node-abi": { "node-abi": {
@ -29245,6 +29280,11 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"validator": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw=="
},
"vite": { "vite": {
"version": "2.9.15", "version": "2.9.15",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.9.15.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.15.tgz",

2
packages/nc-gui/package.json

@ -56,6 +56,7 @@
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"tinycolor2": "^1.4.2", "tinycolor2": "^1.4.2",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"validator": "^13.7.0",
"vue-dompurify-html": "^3.0.0", "vue-dompurify-html": "^3.0.0",
"vue-github-button": "^3.0.3", "vue-github-button": "^3.0.3",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
@ -86,6 +87,7 @@
"@types/papaparse": "^5.3.2", "@types/papaparse": "^5.3.2",
"@types/sortablejs": "^1.13.0", "@types/sortablejs": "^1.13.0",
"@types/tinycolor2": "^1.4.3", "@types/tinycolor2": "^1.4.3",
"@types/validator": "^13.7.10",
"@vitest/ui": "^0.18.0", "@vitest/ui": "^0.18.0",
"@vue/compiler-sfc": "^3.2.37", "@vue/compiler-sfc": "^3.2.37",
"@vue/test-utils": "^2.0.2", "@vue/test-utils": "^2.0.2",

3
packages/nc-gui/utils/cell.ts

@ -46,8 +46,7 @@ export const isAutoSaved = (column: ColumnType) =>
UITypes.Duration, UITypes.Duration,
].includes(column.uidt as UITypes) ].includes(column.uidt as UITypes)
export const isManualSaved = (column: ColumnType) => export const isManualSaved = (column: ColumnType) => [UITypes.Currency].includes(column.uidt as UITypes)
[UITypes.Currency, UITypes.Year, UITypes.Time].includes(column.uidt as UITypes)
export const isPrimary = (column: ColumnType) => !!column.pv export const isPrimary = (column: ColumnType) => !!column.pv

8
packages/nc-gui/utils/parsers/ExcelTemplateAdapter.ts

@ -132,15 +132,15 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
if (column.uidt === UITypes.SingleLineText) { if (column.uidt === UITypes.SingleLineText) {
// check for long text // check for long text
if (isMultiLineTextType(rows)) { if (isMultiLineTextType(rows, col)) {
column.uidt = UITypes.LongText column.uidt = UITypes.LongText
} }
if (isEmailType(rows)) { if (isEmailType(rows, col)) {
column.uidt = UITypes.Email column.uidt = UITypes.Email
} }
if (isUrlType(rows)) { if (isUrlType(rows, col)) {
column.uidt = UITypes.URL column.uidt = UITypes.URL
} else { } else {
const vals = rows const vals = rows
@ -148,7 +148,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
.map((r: any) => r[col]) .map((r: any) => r[col])
.filter((v: any) => v !== null && v !== undefined && v.toString().trim() !== '') .filter((v: any) => v !== null && v !== undefined && v.toString().trim() !== '')
const checkboxType = isCheckboxType(vals) const checkboxType = isCheckboxType(vals, col)
if (checkboxType.length === 1) { if (checkboxType.length === 1) {
column.uidt = UITypes.Checkbox column.uidt = UITypes.Checkbox
} else { } else {

16
packages/nc-gui/utils/parsers/parserHelpers.ts

@ -20,11 +20,11 @@ const booleanOptions = [
] ]
const aggBooleanOptions: any = booleanOptions.reduce((obj, o) => ({ ...obj, ...o }), {}) const aggBooleanOptions: any = booleanOptions.reduce((obj, o) => ({ ...obj, ...o }), {})
const getColVal = (row: any, col = null) => { const getColVal = (row: any, col?: number) => {
return row && col ? row[col] : row return row && col ? row[col] : row
} }
export const isCheckboxType: any = (values: [], col = null) => { export const isCheckboxType: any = (values: [], col?: number) => {
let options = booleanOptions let options = booleanOptions
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
const val = getColVal(values[i], col) const val = getColVal(values[i], col)
@ -45,7 +45,7 @@ export const getCheckboxValue = (value: any) => {
return value && aggBooleanOptions[value] return value && aggBooleanOptions[value]
} }
export const isMultiLineTextType = (values: [], col = null) => { export const isMultiLineTextType = (values: [], col?: number) => {
return values.some( return values.some(
(r) => (getColVal(r, col) || '').toString().match(/[\r\n]/) || (getColVal(r, col) || '').toString().length > 255, (r) => (getColVal(r, col) || '').toString().match(/[\r\n]/) || (getColVal(r, col) || '').toString().length > 255,
) )
@ -107,13 +107,15 @@ export const isDecimalType = (colData: []) =>
return v && parseInt(v) !== +v return v && parseInt(v) !== +v
}) })
export const isEmailType = (colData: []) => export const isEmailType = (colData: [], col?: number) =>
colData.some((v: any) => { colData.some((r: any) => {
const v = getColVal(r, col)
return v && validateEmail(v) return v && validateEmail(v)
}) })
export const isUrlType = (colData: []) => export const isUrlType = (colData: [], col?: number) =>
colData.some((v: any) => { colData.some((r: any) => {
const v = getColVal(r, col)
return v && isValidURL(v) return v && isValidURL(v)
}) })

8
packages/nc-gui/utils/urlUtils.ts

@ -1,3 +1,5 @@
import isURL from 'validator/lib/isURL'
export const replaceUrlsWithLink = (text: string): boolean | string => { export const replaceUrlsWithLink = (text: string): boolean | string => {
if (!text) { if (!text) {
return false return false
@ -17,12 +19,8 @@ export const replaceUrlsWithLink = (text: string): boolean | string => {
return found && out return found && out
} }
// ref : https://stackoverflow.com/a/5717133
export const isValidURL = (str: string) => { export const isValidURL = (str: string) => {
const pattern = return isURL(str)
/^(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00A1-\uFFFF0-9]-*)*[a-z\u00A1-\uFFFF0-9]+)(?:\.(?:[a-z\u00A1-\uFFFF0-9]-*)*[a-z\u00A1-\uFFFF0-9]+)*(?:\.(?:[a-z\u00A1-\uFFFF]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
return pattern.test(str)
} }
export const openLink = (path: string, baseURL?: string, target = '_blank') => { export const openLink = (path: string, baseURL?: string, target = '_blank') => {

2
packages/nc-gui/windi.config.ts

@ -20,7 +20,7 @@ export default defineConfig({
}, },
darkMode: 'class', darkMode: 'class',
safelist: ['text-yellow-500', 'text-sky-500', 'text-red-500'],
plugins: [ plugins: [
scrollbar, scrollbar,
animations, animations,

47
packages/nocodb/package-lock.json generated

@ -151,6 +151,28 @@
"vuedraggable": "^2.24.3" "vuedraggable": "^2.24.3"
} }
}, },
"../nocodb-sdk": {
"version": "0.99.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^1.3.6"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"cspell": "^4.1.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.1",
"typescript": "^4.0.2"
}
},
"node_modules/@azure/abort-controller": { "node_modules/@azure/abort-controller": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
@ -10634,13 +10656,8 @@
"dev": true "dev": true
}, },
"node_modules/nocodb-sdk": { "node_modules/nocodb-sdk": {
"version": "0.99.2", "resolved": "../nocodb-sdk",
"resolved": "file:../nocodb-sdk", "link": true
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^1.3.6"
}
}, },
"node_modules/node-abort-controller": { "node_modules/node-abort-controller": {
"version": "3.0.1", "version": "3.0.1",
@ -26042,10 +26059,22 @@
"dev": true "dev": true
}, },
"nocodb-sdk": { "nocodb-sdk": {
"version": "0.99.2", "version": "file:../nocodb-sdk",
"requires": { "requires": {
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"axios": "^0.21.1", "axios": "^0.21.1",
"jsep": "^1.3.6" "cspell": "^4.1.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"jsep": "^1.3.6",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.1",
"typescript": "^4.0.2"
} }
}, },
"node-abort-controller": { "node-abort-controller": {

Loading…
Cancel
Save