Support ODS, XLS, and other spreadsheet formats in the attachment editor.
Detect editable attachments by extension and write back in the original book type instead of always forcing xlsx. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+2
-5
@@ -4,6 +4,7 @@
|
|||||||
import { parseRoute, buildHash, currentHash } from './lib/route.js'
|
import { parseRoute, buildHash, currentHash } from './lib/route.js'
|
||||||
import MarkdownEditor from './lib/MarkdownEditor.svelte'
|
import MarkdownEditor from './lib/MarkdownEditor.svelte'
|
||||||
import ExcelEditor from './lib/ExcelEditor.svelte'
|
import ExcelEditor from './lib/ExcelEditor.svelte'
|
||||||
|
import { isSpreadsheetName } from './lib/spreadsheet.js'
|
||||||
|
|
||||||
let user = $state(null)
|
let user = $state(null)
|
||||||
let loading = $state(true)
|
let loading = $state(true)
|
||||||
@@ -398,10 +399,6 @@
|
|||||||
return `${(n / (1024 * 1024)).toFixed(1)} MB`
|
return `${(n / (1024 * 1024)).toFixed(1)} MB`
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExcelName(name) {
|
|
||||||
return /\.xlsx$/i.test(name || '')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onAttachFile(e) {
|
async function onAttachFile(e) {
|
||||||
const file = e.target?.files?.[0]
|
const file = e.target?.files?.[0]
|
||||||
e.target.value = ''
|
e.target.value = ''
|
||||||
@@ -963,7 +960,7 @@
|
|||||||
<span class="muted">{formatBytes(a.sizeBytes)}</span>
|
<span class="muted">{formatBytes(a.sizeBytes)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="width: auto; justify-content: flex-end;">
|
<div class="row" style="width: auto; justify-content: flex-end;">
|
||||||
{#if isExcelName(a.filename)}
|
{#if isSpreadsheetName(a.filename)}
|
||||||
<button class="primary" onclick={() => (excelAtt = a)}>Edit</button>
|
<button class="primary" onclick={() => (excelAtt = a)}>Edit</button>
|
||||||
{/if}
|
{/if}
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { api } from './api.js'
|
import { api } from './api.js'
|
||||||
|
import { spreadsheetWriteOptions } from './spreadsheet.js'
|
||||||
|
|
||||||
let {
|
let {
|
||||||
projectId,
|
projectId,
|
||||||
@@ -87,11 +88,9 @@
|
|||||||
try {
|
try {
|
||||||
await ensureXlsx()
|
await ensureXlsx()
|
||||||
workbook.Sheets[activeSheet] = gridToSheet(grid)
|
workbook.Sheets[activeSheet] = gridToSheet(grid)
|
||||||
const out = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' })
|
const { bookType, contentType } = spreadsheetWriteOptions(attachment.filename)
|
||||||
await api.putAttachment(projectId, ticketId, attachment.id, out, {
|
const out = XLSX.write(workbook, { bookType, type: 'array' })
|
||||||
contentType:
|
await api.putAttachment(projectId, ticketId, attachment.id, out, { contentType })
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
||||||
})
|
|
||||||
onsaved()
|
onsaved()
|
||||||
msgOk()
|
msgOk()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/** Extensions SheetJS can read/write for simple cell editing. */
|
||||||
|
const FORMATS = {
|
||||||
|
xlsx: {
|
||||||
|
bookType: 'xlsx',
|
||||||
|
contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
},
|
||||||
|
xlsm: {
|
||||||
|
bookType: 'xlsm',
|
||||||
|
contentType: 'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||||
|
},
|
||||||
|
xlsb: {
|
||||||
|
bookType: 'xlsb',
|
||||||
|
contentType: 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||||
|
},
|
||||||
|
xls: {
|
||||||
|
bookType: 'xls',
|
||||||
|
contentType: 'application/vnd.ms-excel',
|
||||||
|
},
|
||||||
|
ods: {
|
||||||
|
bookType: 'ods',
|
||||||
|
contentType: 'application/vnd.oasis.opendocument.spreadsheet',
|
||||||
|
},
|
||||||
|
fods: {
|
||||||
|
bookType: 'fods',
|
||||||
|
contentType: 'application/vnd.oasis.opendocument.spreadsheet',
|
||||||
|
},
|
||||||
|
csv: {
|
||||||
|
bookType: 'csv',
|
||||||
|
contentType: 'text/csv',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXT_RE = /\.([a-z0-9]+)$/i
|
||||||
|
|
||||||
|
export function isSpreadsheetName(name) {
|
||||||
|
const m = String(name || '').match(EXT_RE)
|
||||||
|
if (!m) return false
|
||||||
|
return Object.prototype.hasOwnProperty.call(FORMATS, m[1].toLowerCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
export function spreadsheetWriteOptions(filename) {
|
||||||
|
const m = String(filename || '').match(EXT_RE)
|
||||||
|
const ext = m ? m[1].toLowerCase() : 'xlsx'
|
||||||
|
return FORMATS[ext] || FORMATS.xlsx
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user