@asteby/metacore-runtime-react 34.1.0 → 35.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -90
- package/dist/action-modal-dispatcher.d.ts +4 -0
- package/dist/action-modal-dispatcher.d.ts.map +1 -1
- package/dist/action-modal-dispatcher.js +102 -23
- package/dist/addon-loader.d.ts +1 -1
- package/dist/addon-loader.d.ts.map +1 -1
- package/dist/addon-loader.js +8 -1
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +99 -61
- package/dist/dynamic-columns.d.ts.map +1 -1
- package/dist/dynamic-columns.js +23 -8
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +6 -3
- package/dist/use-print-document.d.ts +10 -1
- package/dist/use-print-document.d.ts.map +1 -1
- package/dist/use-print-document.js +28 -1
- package/package.json +1 -1
- package/src/__tests__/filename-from-disposition.test.ts +30 -0
- package/src/__tests__/prefill-scalar-from-record.test.ts +37 -0
- package/src/__tests__/record-detail-display.test.tsx +30 -0
- package/src/action-modal-dispatcher.tsx +110 -22
- package/src/addon-loader.tsx +7 -1
- package/src/dialogs/dynamic-record.tsx +139 -65
- package/src/dynamic-columns.tsx +28 -12
- package/src/dynamic-select-field.tsx +7 -3
- package/src/use-print-document.ts +38 -2
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
// value). A dedicated `?ids=` lookup is a follow-up; create flows — the common
|
|
23
23
|
// case — start empty and never hit this.
|
|
24
24
|
import { useEffect, useRef, useState } from 'react'
|
|
25
|
+
import { useTranslation } from 'react-i18next'
|
|
25
26
|
import {
|
|
26
27
|
Badge,
|
|
27
28
|
Button,
|
|
@@ -205,6 +206,9 @@ export function DynamicSelectField({
|
|
|
205
206
|
descriptionAsBadge = false,
|
|
206
207
|
hideCreate = false,
|
|
207
208
|
}: DynamicSelectFieldProps) {
|
|
209
|
+
const { t } = useTranslation()
|
|
210
|
+
const ph = (fallback: string) =>
|
|
211
|
+
field.placeholder ? t(field.placeholder, { defaultValue: field.placeholder }) : fallback
|
|
208
212
|
const [open, setOpen] = useState(false)
|
|
209
213
|
const [search, setSearch] = useState('')
|
|
210
214
|
const [scanOpen, setScanOpen] = useState(false)
|
|
@@ -353,7 +357,7 @@ export function DynamicSelectField({
|
|
|
353
357
|
<span className={'min-w-0 flex-1 truncate ' + (selectedOption ? '' : 'text-muted-foreground')}>
|
|
354
358
|
{/* Never flash the raw id: until the eager fetch resolves the
|
|
355
359
|
option, show a loading hint instead of String(value). */}
|
|
356
|
-
{selectedOption?.label ?? (loading ? 'Cargando…' :
|
|
360
|
+
{selectedOption?.label ?? (loading ? 'Cargando…' : ph('—'))}
|
|
357
361
|
</span>
|
|
358
362
|
</span>
|
|
359
363
|
</Button>
|
|
@@ -386,7 +390,7 @@ export function DynamicSelectField({
|
|
|
386
390
|
<span className={'min-w-0 flex-1 truncate ' + (selectedLabel ? '' : 'text-muted-foreground')}>
|
|
387
391
|
{blockedByDependency
|
|
388
392
|
? (dependsHint || DEFAULT_DEPENDS_HINT)
|
|
389
|
-
: selectedLabel ||
|
|
393
|
+
: selectedLabel || ph('Buscar…')}
|
|
390
394
|
</span>
|
|
391
395
|
{descriptionAsBadge && selectedOption?.description ? (
|
|
392
396
|
<Badge variant="secondary" className="shrink-0 font-normal tabular-nums">
|
|
@@ -406,7 +410,7 @@ export function DynamicSelectField({
|
|
|
406
410
|
>
|
|
407
411
|
<Command shouldFilter={false}>
|
|
408
412
|
<CommandInput
|
|
409
|
-
placeholder={
|
|
413
|
+
placeholder={ph('Buscar…')}
|
|
410
414
|
value={search}
|
|
411
415
|
onValueChange={setSearch}
|
|
412
416
|
/>
|
|
@@ -33,10 +33,36 @@ export interface PrintDocumentArgs {
|
|
|
33
33
|
* open → open the PDF in a new tab (user prints from the viewer).
|
|
34
34
|
*/
|
|
35
35
|
mode?: 'print' | 'download' | 'open'
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Hint filename for download mode. Prefer leaving this unset: the server
|
|
38
|
+
* expands `{{record.*}}` into Content-Disposition. A raw template string
|
|
39
|
+
* (e.g. "cfdi-{{record.number}}.pdf") must NOT be used as a.download —
|
|
40
|
+
* that is how downloads end up literally named with mustache braces.
|
|
41
|
+
*/
|
|
37
42
|
filename?: string
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
/** Parse filename from Content-Disposition (RFC 5987 / quoted). */
|
|
46
|
+
export function filenameFromContentDisposition(header: string | undefined | null): string | undefined {
|
|
47
|
+
if (!header) return undefined
|
|
48
|
+
const star = /filename\*\s*=\s*UTF-8''([^;]+)/i.exec(header)
|
|
49
|
+
if (star?.[1]) {
|
|
50
|
+
try {
|
|
51
|
+
return decodeURIComponent(star[1].trim().replace(/^"|"$/g, ''))
|
|
52
|
+
} catch {
|
|
53
|
+
return star[1].trim().replace(/^"|"$/g, '')
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const plain = /filename\s*=\s*"([^"]+)"|filename\s*=\s*([^;]+)/i.exec(header)
|
|
57
|
+
const raw = (plain?.[1] ?? plain?.[2] ?? '').trim()
|
|
58
|
+
return raw || undefined
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** True when a caller passed an unexpanded mustache template as filename. */
|
|
62
|
+
export function looksLikeFilenameTemplate(name: string | undefined): boolean {
|
|
63
|
+
return !!name && /\{\{/.test(name)
|
|
64
|
+
}
|
|
65
|
+
|
|
40
66
|
/**
|
|
41
67
|
* Returns a `printDocument(args)` callback. Resolves once the PDF has been
|
|
42
68
|
* fetched and the browser action (print/download/open) has been kicked off;
|
|
@@ -65,9 +91,19 @@ export function usePrintDocument() {
|
|
|
65
91
|
const cleanup = () => setTimeout(() => URL.revokeObjectURL(blobUrl), 60_000)
|
|
66
92
|
|
|
67
93
|
if (mode === 'download') {
|
|
94
|
+
const headers = (res as { headers?: Record<string, string> }).headers || {}
|
|
95
|
+
const fromHeader =
|
|
96
|
+
filenameFromContentDisposition(
|
|
97
|
+
headers['content-disposition'] || headers['Content-Disposition'],
|
|
98
|
+
) || undefined
|
|
99
|
+
// Prefer server-expanded name; never use a raw {{record.*}} template.
|
|
100
|
+
const downloadName =
|
|
101
|
+
fromHeader ||
|
|
102
|
+
(!looksLikeFilenameTemplate(filename) ? filename : undefined) ||
|
|
103
|
+
`${key}.pdf`
|
|
68
104
|
const a = document.createElement('a')
|
|
69
105
|
a.href = blobUrl
|
|
70
|
-
a.download =
|
|
106
|
+
a.download = downloadName
|
|
71
107
|
document.body.appendChild(a)
|
|
72
108
|
a.click()
|
|
73
109
|
a.remove()
|