@pallasoft/ps-file-components 2.4.0 → 2.4.1

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.
@@ -6,7 +6,7 @@ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
6
  const ps_icons_1 = require("@pallasoft/ps-icons");
7
7
  const ps_file_icon_button_classes_1 = require("../constants/ps-file-icon-button-classes");
8
8
  const PsFileIconButton = ({ disabled = false, download, href, iconKey, label, onClick, tone = 'neutral', }) => {
9
- const className = ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring ps-control-square ps-control-compact inline-flex shrink-0 items-center justify-center rounded-ps-control text-ink/55 transition disabled:pointer-events-none disabled:opacity-40', ps_file_icon_button_classes_1.PsFileIconButtonClasses[tone]);
9
+ const className = ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring ps-control-square ps-control-compact inline-flex shrink-0 items-center justify-center rounded-ps-control text-muted transition disabled:pointer-events-none disabled:opacity-40', ps_file_icon_button_classes_1.PsFileIconButtonClasses[tone]);
10
10
  const icon = ((0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: iconKey }));
11
11
  return href === undefined ? ((0, jsx_runtime_1.jsx)("button", { "aria-label": label, className: className, disabled: disabled, onClick: onClick, title: label, type: 'button', children: icon })) : ((0, jsx_runtime_1.jsx)("a", { "aria-label": label, className: className, download: download, href: href, title: label, children: icon }));
12
12
  };
@@ -21,7 +21,7 @@ const PsFileList = ({ busyIds = [], files, formatDate, labels, onDelete, onOpen,
21
21
  ? null
22
22
  : formatDate(file.createdAt),
23
23
  ].filter((value) => value !== null);
24
- return ((0, jsx_runtime_1.jsxs)("li", { className: 'group flex min-w-0 items-center gap-3 rounded-ps-control border border-line bg-surface p-2 transition hover:border-primary/40', children: [(0, jsx_runtime_1.jsxs)("button", { "aria-label": `${text.open} ${file.name}`, className: 'ps-focus-ring flex min-w-0 flex-1 items-center gap-3 rounded-ps-control text-left', onClick: () => onOpen(index), type: 'button', children: [isImage ? ((0, jsx_runtime_1.jsx)("img", { alt: '', className: 'h-12 w-12 shrink-0 rounded-ps-control border border-line object-cover', loading: 'lazy', src: file.url })) : ((0, jsx_runtime_1.jsx)(ps_file_kind_icon_1.PsFileKindIcon, { className: 'h-12 w-12', contentType: file.contentType, name: file.name })), (0, jsx_runtime_1.jsxs)("span", { className: 'flex min-w-0 flex-col', children: [(0, jsx_runtime_1.jsx)("span", { className: 'truncate text-sm font-medium text-ink group-hover:text-primary', children: file.name }), meta.length > 0 ? ((0, jsx_runtime_1.jsx)("span", { className: 'truncate text-xs text-ink/55', children: meta.join(' · ') })) : null] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 items-center gap-0.5', children: [(0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, iconKey: ps_icons_1.PsIconKey.Download, label: `${text.download} ${file.name}` }), onRename ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: ps_icons_1.PsIconKey.Pencil, label: `${text.rename} ${file.name}`, onClick: () => onRename(file) })) : null, onDelete ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: ps_icons_1.PsIconKey.Trash, label: `${text.delete} ${file.name}`, onClick: () => onDelete(file), tone: 'danger' })) : null] })] }, file.id));
24
+ return ((0, jsx_runtime_1.jsxs)("li", { className: 'group flex min-w-0 items-center gap-3 rounded-ps-control border border-line bg-surface p-2 transition hover:border-primary/40', children: [(0, jsx_runtime_1.jsxs)("button", { "aria-label": `${text.open} ${file.name}`, className: 'ps-focus-ring flex min-w-0 flex-1 items-center gap-3 rounded-ps-control text-left', onClick: () => onOpen(index), type: 'button', children: [isImage ? ((0, jsx_runtime_1.jsx)("img", { alt: '', className: 'h-12 w-12 shrink-0 rounded-ps-control border border-line object-cover', loading: 'lazy', src: file.url })) : ((0, jsx_runtime_1.jsx)(ps_file_kind_icon_1.PsFileKindIcon, { className: 'h-12 w-12', contentType: file.contentType, name: file.name })), (0, jsx_runtime_1.jsxs)("span", { className: 'flex min-w-0 flex-col', children: [(0, jsx_runtime_1.jsx)("span", { className: 'truncate text-sm font-medium text-ink group-hover:text-primary', children: file.name }), meta.length > 0 ? ((0, jsx_runtime_1.jsx)("span", { className: 'truncate text-xs text-muted', children: meta.join(' · ') })) : null] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 items-center gap-0.5', children: [(0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, iconKey: ps_icons_1.PsIconKey.Download, label: `${text.download} ${file.name}` }), onRename ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: ps_icons_1.PsIconKey.Pencil, label: `${text.rename} ${file.name}`, onClick: () => onRename(file) })) : null, onDelete ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: ps_icons_1.PsIconKey.Trash, label: `${text.delete} ${file.name}`, onClick: () => onDelete(file), tone: 'danger' })) : null] })] }, file.id));
25
25
  }) }));
26
26
  };
27
27
  exports.PsFileList = PsFileList;
@@ -67,6 +67,6 @@ const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLa
67
67
  select(event.dataTransfer.files);
68
68
  }, children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('flex size-12 items-center justify-center rounded-full transition-colors duration-200', isDragging
69
69
  ? 'bg-primary text-[color:var(--ps-color-primary-contrast)]'
70
- : 'bg-primary-soft text-primary'), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { className: 'size-6', iconKey: ps_icons_1.PsIconKey.Upload }) }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-1', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-base font-semibold text-ink', children: isDragging ? (text.dropActive) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : ((0, jsx_runtime_1.jsx)("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), (0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/60 [@media(pointer:coarse)]:hidden', children: multiple ? text.dragHint : text.dragHintOne })] }), (0, jsx_runtime_1.jsx)(ps_file_picker_actions_1.PsFilePickerActions, { chooseLabel: chooseLabel, className: 'flex [@media(pointer:coarse)]:hidden', disabled: disabled, isTouch: false, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), (0, jsx_runtime_1.jsx)(ps_file_picker_actions_1.PsFilePickerActions, { chooseLabel: chooseLabel, className: 'hidden [@media(pointer:coarse)]:flex', disabled: disabled, isTouch: true, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), details.length > 0 ? (0, jsx_runtime_1.jsx)("p", { className: 'text-xs text-ink/50', children: details.join(' · ') }) : null, (0, jsx_runtime_1.jsx)("input", { accept: acceptAttr, "aria-hidden": 'true', className: 'sr-only', disabled: disabled, multiple: multiple, onChange: onChange, onClick: (event) => event.stopPropagation(), ref: input, tabIndex: -1, type: 'file' })] }), queue === undefined || queue.items.length === 0 ? null : ((0, jsx_runtime_1.jsx)("ul", { "aria-label": rowLabels.title, className: 'flex flex-col gap-2', children: queue.items.map((item) => ((0, jsx_runtime_1.jsx)(ps_upload_row_1.PsUploadRow, { item: item, labels: rowLabels, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) })), scan === undefined ? null : ((0, jsx_runtime_1.jsx)(ps_document_scanner_1.PsDocumentScanner, { ...scan, fileName: scanName === null ? ps_scan_config_1.PsScanConfig.defaultFileName : scanName, isOpen: scanName !== null, onClose: () => setScanName(null), onComplete: select }))] }));
70
+ : 'bg-primary-soft text-primary'), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { className: 'size-6', iconKey: ps_icons_1.PsIconKey.Upload }) }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-1', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-base font-semibold text-ink', children: isDragging ? (text.dropActive) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : ((0, jsx_runtime_1.jsx)("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), (0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/60 [@media(pointer:coarse)]:hidden', children: multiple ? text.dragHint : text.dragHintOne })] }), (0, jsx_runtime_1.jsx)(ps_file_picker_actions_1.PsFilePickerActions, { chooseLabel: chooseLabel, className: 'flex [@media(pointer:coarse)]:hidden', disabled: disabled, isTouch: false, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), (0, jsx_runtime_1.jsx)(ps_file_picker_actions_1.PsFilePickerActions, { chooseLabel: chooseLabel, className: 'hidden [@media(pointer:coarse)]:flex', disabled: disabled, isTouch: true, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), details.length > 0 ? (0, jsx_runtime_1.jsx)("p", { className: 'text-xs text-muted', children: details.join(' · ') }) : null, (0, jsx_runtime_1.jsx)("input", { accept: acceptAttr, "aria-hidden": 'true', className: 'sr-only', disabled: disabled, multiple: multiple, onChange: onChange, onClick: (event) => event.stopPropagation(), ref: input, tabIndex: -1, type: 'file' })] }), queue === undefined || queue.items.length === 0 ? null : ((0, jsx_runtime_1.jsx)("ul", { "aria-label": rowLabels.title, className: 'flex flex-col gap-2', children: queue.items.map((item) => ((0, jsx_runtime_1.jsx)(ps_upload_row_1.PsUploadRow, { item: item, labels: rowLabels, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) })), scan === undefined ? null : ((0, jsx_runtime_1.jsx)(ps_document_scanner_1.PsDocumentScanner, { ...scan, fileName: scanName === null ? ps_scan_config_1.PsScanConfig.defaultFileName : scanName, isOpen: scanName !== null, onClose: () => setScanName(null), onComplete: select }))] }));
71
71
  };
72
72
  exports.PsFilePicker = PsFilePicker;
@@ -19,6 +19,6 @@ const PsUploadRow = ({ item, labels, onCancel, onRemove, onRetry }) => {
19
19
  : item.status === ps_upload_status_1.PsUploadStatus.failed && item.error
20
20
  ? item.error
21
21
  : labels[item.status];
22
- return ((0, jsx_runtime_1.jsxs)("li", { className: 'flex items-center gap-3 rounded-ps-control border border-line px-3 py-2.5', children: [(0, jsx_runtime_1.jsx)(ps_file_kind_icon_1.PsFileKindIcon, { contentType: item.file.type, name: item.file.name }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-1 flex-col gap-1.5', children: [(0, jsx_runtime_1.jsx)("p", { className: 'truncate text-sm font-medium text-ink', children: item.file.name }), (0, jsx_runtime_1.jsx)(ps_progress_bar_1.PsProgressBar, { label: item.file.name, tone: ps_upload_status_tones_1.PsUploadStatusTones[item.status], value: isProblem ? 1 : item.progress }), (0, jsx_runtime_1.jsxs)("p", { className: `truncate text-xs ${isProblem ? 'text-danger' : item.status === ps_upload_status_1.PsUploadStatus.done ? 'text-success' : 'text-ink/55'}`, role: item.status === ps_upload_status_1.PsUploadStatus.failed ? 'alert' : undefined, children: [size, " \u00B7 ", status] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex w-20 shrink-0 items-center justify-end gap-1', children: [isProblem ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { iconKey: ps_icons_1.PsIconKey.Refresh, label: `${labels.retry} ${item.file.name}`, onClick: onRetry })) : null, isActive ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: `${labels.cancel} ${item.file.name}`, onClick: onCancel, tone: 'danger' })) : item.status === ps_upload_status_1.PsUploadStatus.done ? ((0, jsx_runtime_1.jsx)("span", { className: 'ps-control-square ps-control-compact inline-flex items-center justify-center text-success', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-label": labels.done, className: 'h-4 w-4', iconKey: ps_icons_1.PsIconKey.Check }) })) : ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: `${labels.remove} ${item.file.name}`, onClick: onRemove }))] })] }));
22
+ return ((0, jsx_runtime_1.jsxs)("li", { className: 'flex items-center gap-3 rounded-ps-control border border-line px-3 py-2.5', children: [(0, jsx_runtime_1.jsx)(ps_file_kind_icon_1.PsFileKindIcon, { contentType: item.file.type, name: item.file.name }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-1 flex-col gap-1.5', children: [(0, jsx_runtime_1.jsx)("p", { className: 'truncate text-sm font-medium text-ink', children: item.file.name }), (0, jsx_runtime_1.jsx)(ps_progress_bar_1.PsProgressBar, { label: item.file.name, tone: ps_upload_status_tones_1.PsUploadStatusTones[item.status], value: isProblem ? 1 : item.progress }), (0, jsx_runtime_1.jsxs)("p", { className: `truncate text-xs ${isProblem ? 'text-danger' : item.status === ps_upload_status_1.PsUploadStatus.done ? 'text-success' : 'text-muted'}`, role: item.status === ps_upload_status_1.PsUploadStatus.failed ? 'alert' : undefined, children: [size, " \u00B7 ", status] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex w-20 shrink-0 items-center justify-end gap-1', children: [isProblem ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { iconKey: ps_icons_1.PsIconKey.Refresh, label: `${labels.retry} ${item.file.name}`, onClick: onRetry })) : null, isActive ? ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: `${labels.cancel} ${item.file.name}`, onClick: onCancel, tone: 'danger' })) : item.status === ps_upload_status_1.PsUploadStatus.done ? ((0, jsx_runtime_1.jsx)("span", { className: 'ps-control-square ps-control-compact inline-flex items-center justify-center text-success', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-label": labels.done, className: 'h-4 w-4', iconKey: ps_icons_1.PsIconKey.Check }) })) : ((0, jsx_runtime_1.jsx)(ps_file_icon_button_1.PsFileIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: `${labels.remove} ${item.file.name}`, onClick: onRemove }))] })] }));
23
23
  };
24
24
  exports.PsUploadRow = PsUploadRow;
@@ -3,7 +3,7 @@ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
3
3
  import { PsIcon } from '@pallasoft/ps-icons';
4
4
  import { PsFileIconButtonClasses } from '../constants/ps-file-icon-button-classes';
5
5
  export const PsFileIconButton = ({ disabled = false, download, href, iconKey, label, onClick, tone = 'neutral', }) => {
6
- const className = PallasoftClassNameService.merge('ps-focus-ring ps-control-square ps-control-compact inline-flex shrink-0 items-center justify-center rounded-ps-control text-ink/55 transition disabled:pointer-events-none disabled:opacity-40', PsFileIconButtonClasses[tone]);
6
+ const className = PallasoftClassNameService.merge('ps-focus-ring ps-control-square ps-control-compact inline-flex shrink-0 items-center justify-center rounded-ps-control text-muted transition disabled:pointer-events-none disabled:opacity-40', PsFileIconButtonClasses[tone]);
7
7
  const icon = (_jsx(PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: iconKey }));
8
8
  return href === undefined ? (_jsx("button", { "aria-label": label, className: className, disabled: disabled, onClick: onClick, title: label, type: 'button', children: icon })) : (_jsx("a", { "aria-label": label, className: className, download: download, href: href, title: label, children: icon }));
9
9
  };
@@ -18,6 +18,6 @@ export const PsFileList = ({ busyIds = [], files, formatDate, labels, onDelete,
18
18
  ? null
19
19
  : formatDate(file.createdAt),
20
20
  ].filter((value) => value !== null);
21
- return (_jsxs("li", { className: 'group flex min-w-0 items-center gap-3 rounded-ps-control border border-line bg-surface p-2 transition hover:border-primary/40', children: [_jsxs("button", { "aria-label": `${text.open} ${file.name}`, className: 'ps-focus-ring flex min-w-0 flex-1 items-center gap-3 rounded-ps-control text-left', onClick: () => onOpen(index), type: 'button', children: [isImage ? (_jsx("img", { alt: '', className: 'h-12 w-12 shrink-0 rounded-ps-control border border-line object-cover', loading: 'lazy', src: file.url })) : (_jsx(PsFileKindIcon, { className: 'h-12 w-12', contentType: file.contentType, name: file.name })), _jsxs("span", { className: 'flex min-w-0 flex-col', children: [_jsx("span", { className: 'truncate text-sm font-medium text-ink group-hover:text-primary', children: file.name }), meta.length > 0 ? (_jsx("span", { className: 'truncate text-xs text-ink/55', children: meta.join(' · ') })) : null] })] }), _jsxs("div", { className: 'flex shrink-0 items-center gap-0.5', children: [_jsx(PsFileIconButton, { download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, iconKey: PsIconKey.Download, label: `${text.download} ${file.name}` }), onRename ? (_jsx(PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: PsIconKey.Pencil, label: `${text.rename} ${file.name}`, onClick: () => onRename(file) })) : null, onDelete ? (_jsx(PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: PsIconKey.Trash, label: `${text.delete} ${file.name}`, onClick: () => onDelete(file), tone: 'danger' })) : null] })] }, file.id));
21
+ return (_jsxs("li", { className: 'group flex min-w-0 items-center gap-3 rounded-ps-control border border-line bg-surface p-2 transition hover:border-primary/40', children: [_jsxs("button", { "aria-label": `${text.open} ${file.name}`, className: 'ps-focus-ring flex min-w-0 flex-1 items-center gap-3 rounded-ps-control text-left', onClick: () => onOpen(index), type: 'button', children: [isImage ? (_jsx("img", { alt: '', className: 'h-12 w-12 shrink-0 rounded-ps-control border border-line object-cover', loading: 'lazy', src: file.url })) : (_jsx(PsFileKindIcon, { className: 'h-12 w-12', contentType: file.contentType, name: file.name })), _jsxs("span", { className: 'flex min-w-0 flex-col', children: [_jsx("span", { className: 'truncate text-sm font-medium text-ink group-hover:text-primary', children: file.name }), meta.length > 0 ? (_jsx("span", { className: 'truncate text-xs text-muted', children: meta.join(' · ') })) : null] })] }), _jsxs("div", { className: 'flex shrink-0 items-center gap-0.5', children: [_jsx(PsFileIconButton, { download: file.name, href: file.downloadUrl === undefined ? file.url : file.downloadUrl, iconKey: PsIconKey.Download, label: `${text.download} ${file.name}` }), onRename ? (_jsx(PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: PsIconKey.Pencil, label: `${text.rename} ${file.name}`, onClick: () => onRename(file) })) : null, onDelete ? (_jsx(PsFileIconButton, { disabled: busyIds.includes(file.id), iconKey: PsIconKey.Trash, label: `${text.delete} ${file.name}`, onClick: () => onDelete(file), tone: 'danger' })) : null] })] }, file.id));
22
22
  }) }));
23
23
  };
@@ -64,5 +64,5 @@ export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false,
64
64
  select(event.dataTransfer.files);
65
65
  }, children: [_jsx("span", { "aria-hidden": 'true', className: PallasoftClassNameService.merge('flex size-12 items-center justify-center rounded-full transition-colors duration-200', isDragging
66
66
  ? 'bg-primary text-[color:var(--ps-color-primary-contrast)]'
67
- : 'bg-primary-soft text-primary'), children: _jsx(PsIcon, { className: 'size-6', iconKey: PsIconKey.Upload }) }), _jsxs("div", { className: 'flex flex-col gap-1', children: [_jsx("p", { className: 'text-base font-semibold text-ink', children: isDragging ? (text.dropActive) : (_jsxs(_Fragment, { children: [_jsx("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : (_jsx("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), _jsx("p", { className: 'text-sm text-ink/60 [@media(pointer:coarse)]:hidden', children: multiple ? text.dragHint : text.dragHintOne })] }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'flex [@media(pointer:coarse)]:hidden', disabled: disabled, isTouch: false, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'hidden [@media(pointer:coarse)]:flex', disabled: disabled, isTouch: true, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), details.length > 0 ? _jsx("p", { className: 'text-xs text-ink/50', children: details.join(' · ') }) : null, _jsx("input", { accept: acceptAttr, "aria-hidden": 'true', className: 'sr-only', disabled: disabled, multiple: multiple, onChange: onChange, onClick: (event) => event.stopPropagation(), ref: input, tabIndex: -1, type: 'file' })] }), queue === undefined || queue.items.length === 0 ? null : (_jsx("ul", { "aria-label": rowLabels.title, className: 'flex flex-col gap-2', children: queue.items.map((item) => (_jsx(PsUploadRow, { item: item, labels: rowLabels, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) })), scan === undefined ? null : (_jsx(PsDocumentScanner, { ...scan, fileName: scanName === null ? PsScanConfig.defaultFileName : scanName, isOpen: scanName !== null, onClose: () => setScanName(null), onComplete: select }))] }));
67
+ : 'bg-primary-soft text-primary'), children: _jsx(PsIcon, { className: 'size-6', iconKey: PsIconKey.Upload }) }), _jsxs("div", { className: 'flex flex-col gap-1', children: [_jsx("p", { className: 'text-base font-semibold text-ink', children: isDragging ? (text.dropActive) : (_jsxs(_Fragment, { children: [_jsx("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : (_jsx("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), _jsx("p", { className: 'text-sm text-ink/60 [@media(pointer:coarse)]:hidden', children: multiple ? text.dragHint : text.dragHintOne })] }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'flex [@media(pointer:coarse)]:hidden', disabled: disabled, isTouch: false, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'hidden [@media(pointer:coarse)]:flex', disabled: disabled, isTouch: true, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), details.length > 0 ? _jsx("p", { className: 'text-xs text-muted', children: details.join(' · ') }) : null, _jsx("input", { accept: acceptAttr, "aria-hidden": 'true', className: 'sr-only', disabled: disabled, multiple: multiple, onChange: onChange, onClick: (event) => event.stopPropagation(), ref: input, tabIndex: -1, type: 'file' })] }), queue === undefined || queue.items.length === 0 ? null : (_jsx("ul", { "aria-label": rowLabels.title, className: 'flex flex-col gap-2', children: queue.items.map((item) => (_jsx(PsUploadRow, { item: item, labels: rowLabels, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) })), scan === undefined ? null : (_jsx(PsDocumentScanner, { ...scan, fileName: scanName === null ? PsScanConfig.defaultFileName : scanName, isOpen: scanName !== null, onClose: () => setScanName(null), onComplete: select }))] }));
68
68
  };
@@ -16,5 +16,5 @@ export const PsUploadRow = ({ item, labels, onCancel, onRemove, onRetry }) => {
16
16
  : item.status === PsUploadStatus.failed && item.error
17
17
  ? item.error
18
18
  : labels[item.status];
19
- return (_jsxs("li", { className: 'flex items-center gap-3 rounded-ps-control border border-line px-3 py-2.5', children: [_jsx(PsFileKindIcon, { contentType: item.file.type, name: item.file.name }), _jsxs("div", { className: 'flex min-w-0 flex-1 flex-col gap-1.5', children: [_jsx("p", { className: 'truncate text-sm font-medium text-ink', children: item.file.name }), _jsx(PsProgressBar, { label: item.file.name, tone: PsUploadStatusTones[item.status], value: isProblem ? 1 : item.progress }), _jsxs("p", { className: `truncate text-xs ${isProblem ? 'text-danger' : item.status === PsUploadStatus.done ? 'text-success' : 'text-ink/55'}`, role: item.status === PsUploadStatus.failed ? 'alert' : undefined, children: [size, " \u00B7 ", status] })] }), _jsxs("div", { className: 'flex w-20 shrink-0 items-center justify-end gap-1', children: [isProblem ? (_jsx(PsFileIconButton, { iconKey: PsIconKey.Refresh, label: `${labels.retry} ${item.file.name}`, onClick: onRetry })) : null, isActive ? (_jsx(PsFileIconButton, { iconKey: PsIconKey.Close, label: `${labels.cancel} ${item.file.name}`, onClick: onCancel, tone: 'danger' })) : item.status === PsUploadStatus.done ? (_jsx("span", { className: 'ps-control-square ps-control-compact inline-flex items-center justify-center text-success', children: _jsx(PsIcon, { "aria-label": labels.done, className: 'h-4 w-4', iconKey: PsIconKey.Check }) })) : (_jsx(PsFileIconButton, { iconKey: PsIconKey.Close, label: `${labels.remove} ${item.file.name}`, onClick: onRemove }))] })] }));
19
+ return (_jsxs("li", { className: 'flex items-center gap-3 rounded-ps-control border border-line px-3 py-2.5', children: [_jsx(PsFileKindIcon, { contentType: item.file.type, name: item.file.name }), _jsxs("div", { className: 'flex min-w-0 flex-1 flex-col gap-1.5', children: [_jsx("p", { className: 'truncate text-sm font-medium text-ink', children: item.file.name }), _jsx(PsProgressBar, { label: item.file.name, tone: PsUploadStatusTones[item.status], value: isProblem ? 1 : item.progress }), _jsxs("p", { className: `truncate text-xs ${isProblem ? 'text-danger' : item.status === PsUploadStatus.done ? 'text-success' : 'text-muted'}`, role: item.status === PsUploadStatus.failed ? 'alert' : undefined, children: [size, " \u00B7 ", status] })] }), _jsxs("div", { className: 'flex w-20 shrink-0 items-center justify-end gap-1', children: [isProblem ? (_jsx(PsFileIconButton, { iconKey: PsIconKey.Refresh, label: `${labels.retry} ${item.file.name}`, onClick: onRetry })) : null, isActive ? (_jsx(PsFileIconButton, { iconKey: PsIconKey.Close, label: `${labels.cancel} ${item.file.name}`, onClick: onCancel, tone: 'danger' })) : item.status === PsUploadStatus.done ? (_jsx("span", { className: 'ps-control-square ps-control-compact inline-flex items-center justify-center text-success', children: _jsx(PsIcon, { "aria-label": labels.done, className: 'h-4 w-4', iconKey: PsIconKey.Check }) })) : (_jsx(PsFileIconButton, { iconKey: PsIconKey.Close, label: `${labels.remove} ${item.file.name}`, onClick: onRemove }))] })] }));
20
20
  };
@@ -1,28 +1,6 @@
1
- import { PsTextSearchService } from './ps-text-search-service';
1
+ import { Common } from '@pallasoft/ps-utilities';
2
2
  export class PsImageTextService {
3
3
  static matches(layer, query) {
4
- if (layer.width <= 0 || layer.height <= 0)
5
- return [];
6
- const items = layer.words.map((word, index) => ({
7
- isBreakAfter: index < layer.words.length - 1,
8
- text: word.text,
9
- }));
10
- const rects = [];
11
- PsTextSearchService.findInItems(items, query).forEach((match, matchIndex) => {
12
- match.parts.forEach((part) => {
13
- const word = layer.words[part.itemIndex];
14
- const length = word.text.length;
15
- const left = word.box.left + (word.box.width * part.start) / length;
16
- const width = (word.box.width * (part.end - part.start)) / length;
17
- rects.push({
18
- height: word.box.height / layer.height,
19
- left: left / layer.width,
20
- matchIndex,
21
- top: word.box.top / layer.height,
22
- width: width / layer.width,
23
- });
24
- });
25
- });
26
- return rects;
4
+ return Common.TextLayerService.fractionRects(layer, Common.TextLayerService.phraseMatches(layer, query)).map((rect) => ({ ...rect.box, matchIndex: rect.matchIndex }));
27
5
  }
28
6
  }
@@ -1,86 +1,16 @@
1
- const whitespace = /\s/u;
2
- const marks = /\p{M}/gu;
1
+ import { Common } from '@pallasoft/ps-utilities';
3
2
  export class PsTextSearchService {
4
3
  static normalize(text) {
5
- const starts = [];
6
- const ends = [];
7
- let folded = '';
8
- let isAfterSpace = false;
9
- let index = 0;
10
- while (index < text.length) {
11
- const char = String.fromCodePoint(text.codePointAt(index));
12
- const next = index + char.length;
13
- if (whitespace.test(char)) {
14
- if (!isAfterSpace) {
15
- folded += ' ';
16
- starts.push(index);
17
- ends.push(next);
18
- }
19
- isAfterSpace = true;
20
- }
21
- else {
22
- const plain = char.toLowerCase().normalize('NFD').replace(marks, '');
23
- for (let at = 0; at < plain.length; at += 1) {
24
- folded += plain[at];
25
- starts.push(index);
26
- ends.push(next);
27
- }
28
- isAfterSpace = false;
29
- }
30
- index = next;
31
- }
32
- return { ends, starts, text: folded };
4
+ return Common.TextSearchService.normalize(text);
33
5
  }
34
6
  static find(text, query) {
35
- const needle = PsTextSearchService.normalize(query.trim()).text;
36
- if (needle.length === 0)
37
- return [];
38
- const haystack = PsTextSearchService.normalize(text);
39
- const matches = [];
40
- let from = haystack.text.indexOf(needle);
41
- while (from !== -1) {
42
- matches.push({
43
- end: haystack.ends[from + needle.length - 1],
44
- start: haystack.starts[from],
45
- });
46
- from = haystack.text.indexOf(needle, from + needle.length);
47
- }
48
- return matches;
7
+ return Common.TextSearchService.find(text, query);
49
8
  }
50
9
  static findInItems(items, query) {
51
- const offsets = [];
52
- let joined = '';
53
- items.forEach((item) => {
54
- offsets.push(joined.length);
55
- joined += item.isBreakAfter ? `${item.text} ` : item.text;
56
- });
57
- return PsTextSearchService.find(joined, query).map((match) => {
58
- const parts = [];
59
- items.forEach((item, itemIndex) => {
60
- const start = Math.max(match.start, offsets[itemIndex]) - offsets[itemIndex];
61
- const end = Math.min(match.end, offsets[itemIndex] + item.text.length) - offsets[itemIndex];
62
- if (end > start)
63
- parts.push({ end, itemIndex, start });
64
- });
65
- return { parts };
66
- });
10
+ return Common.TextSearchService.findInItems(items, query);
67
11
  }
68
12
  static segments(text, matches, firstIndex = 0) {
69
- const segments = [];
70
- let cursor = 0;
71
- matches.forEach((match, index) => {
72
- if (match.start > cursor)
73
- segments.push({ isMatch: false, matchIndex: null, text: text.slice(cursor, match.start) });
74
- segments.push({
75
- isMatch: true,
76
- matchIndex: firstIndex + index,
77
- text: text.slice(match.start, match.end),
78
- });
79
- cursor = match.end;
80
- });
81
- if (cursor < text.length)
82
- segments.push({ isMatch: false, matchIndex: null, text: text.slice(cursor) });
83
- return segments;
13
+ return Common.TextSearchService.segments(text, matches, firstIndex);
84
14
  }
85
15
  static tableSegments(rows, query) {
86
16
  let offset = 0;
@@ -1,6 +1,2 @@
1
- export interface PsImageTextBox {
2
- height: number;
3
- left: number;
4
- top: number;
5
- width: number;
6
- }
1
+ import type { TextLayerBox } from '@pallasoft/ps-utilities';
2
+ export type PsImageTextBox = TextLayerBox;
@@ -1,6 +1,2 @@
1
- import type { PsImageTextWord } from './ps-image-text-word';
2
- export interface PsImageTextLayer {
3
- height: number;
4
- width: number;
5
- words: PsImageTextWord[];
6
- }
1
+ import type { TextLayer } from '@pallasoft/ps-utilities';
2
+ export type PsImageTextLayer = TextLayer;
@@ -1,5 +1,2 @@
1
- import type { PsImageTextBox } from './ps-image-text-box';
2
- export interface PsImageTextWord {
3
- box: PsImageTextBox;
4
- text: string;
5
- }
1
+ import type { TextLayerWord } from '@pallasoft/ps-utilities';
2
+ export type PsImageTextWord = TextLayerWord;
@@ -1,5 +1,2 @@
1
- export interface PsItemMatchPart {
2
- end: number;
3
- itemIndex: number;
4
- start: number;
5
- }
1
+ import type { TextItemMatchPart } from '@pallasoft/ps-utilities';
2
+ export type PsItemMatchPart = TextItemMatchPart;
@@ -1,4 +1,2 @@
1
- import type { PsItemMatchPart } from './ps-item-match-part';
2
- export interface PsItemMatch {
3
- parts: PsItemMatchPart[];
4
- }
1
+ import type { TextItemMatch } from '@pallasoft/ps-utilities';
2
+ export type PsItemMatch = TextItemMatch;
@@ -1,5 +1,2 @@
1
- export interface PsNormalizedText {
2
- ends: number[];
3
- starts: number[];
4
- text: string;
5
- }
1
+ import type { TextNormalized } from '@pallasoft/ps-utilities';
2
+ export type PsNormalizedText = TextNormalized;
@@ -1,4 +1,2 @@
1
- export interface PsSearchItem {
2
- isBreakAfter: boolean;
3
- text: string;
4
- }
1
+ import type { TextSearchItem } from '@pallasoft/ps-utilities';
2
+ export type PsSearchItem = TextSearchItem;
@@ -1,4 +1,2 @@
1
- export interface PsTextMatch {
2
- end: number;
3
- start: number;
4
- }
1
+ import type { TextMatch } from '@pallasoft/ps-utilities';
2
+ export type PsTextMatch = TextMatch;
@@ -1,5 +1,2 @@
1
- export interface PsTextSegment {
2
- isMatch: boolean;
3
- matchIndex: number | null;
4
- text: string;
5
- }
1
+ import type { TextSegment } from '@pallasoft/ps-utilities';
2
+ export type PsTextSegment = TextSegment;
@@ -1,32 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsImageTextService = void 0;
4
- const ps_text_search_service_1 = require("./ps-text-search-service");
4
+ const ps_utilities_1 = require("@pallasoft/ps-utilities");
5
5
  class PsImageTextService {
6
6
  static matches(layer, query) {
7
- if (layer.width <= 0 || layer.height <= 0)
8
- return [];
9
- const items = layer.words.map((word, index) => ({
10
- isBreakAfter: index < layer.words.length - 1,
11
- text: word.text,
12
- }));
13
- const rects = [];
14
- ps_text_search_service_1.PsTextSearchService.findInItems(items, query).forEach((match, matchIndex) => {
15
- match.parts.forEach((part) => {
16
- const word = layer.words[part.itemIndex];
17
- const length = word.text.length;
18
- const left = word.box.left + (word.box.width * part.start) / length;
19
- const width = (word.box.width * (part.end - part.start)) / length;
20
- rects.push({
21
- height: word.box.height / layer.height,
22
- left: left / layer.width,
23
- matchIndex,
24
- top: word.box.top / layer.height,
25
- width: width / layer.width,
26
- });
27
- });
28
- });
29
- return rects;
7
+ return ps_utilities_1.Common.TextLayerService.fractionRects(layer, ps_utilities_1.Common.TextLayerService.phraseMatches(layer, query)).map((rect) => ({ ...rect.box, matchIndex: rect.matchIndex }));
30
8
  }
31
9
  }
32
10
  exports.PsImageTextService = PsImageTextService;
@@ -1,89 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsTextSearchService = void 0;
4
- const whitespace = /\s/u;
5
- const marks = /\p{M}/gu;
4
+ const ps_utilities_1 = require("@pallasoft/ps-utilities");
6
5
  class PsTextSearchService {
7
6
  static normalize(text) {
8
- const starts = [];
9
- const ends = [];
10
- let folded = '';
11
- let isAfterSpace = false;
12
- let index = 0;
13
- while (index < text.length) {
14
- const char = String.fromCodePoint(text.codePointAt(index));
15
- const next = index + char.length;
16
- if (whitespace.test(char)) {
17
- if (!isAfterSpace) {
18
- folded += ' ';
19
- starts.push(index);
20
- ends.push(next);
21
- }
22
- isAfterSpace = true;
23
- }
24
- else {
25
- const plain = char.toLowerCase().normalize('NFD').replace(marks, '');
26
- for (let at = 0; at < plain.length; at += 1) {
27
- folded += plain[at];
28
- starts.push(index);
29
- ends.push(next);
30
- }
31
- isAfterSpace = false;
32
- }
33
- index = next;
34
- }
35
- return { ends, starts, text: folded };
7
+ return ps_utilities_1.Common.TextSearchService.normalize(text);
36
8
  }
37
9
  static find(text, query) {
38
- const needle = PsTextSearchService.normalize(query.trim()).text;
39
- if (needle.length === 0)
40
- return [];
41
- const haystack = PsTextSearchService.normalize(text);
42
- const matches = [];
43
- let from = haystack.text.indexOf(needle);
44
- while (from !== -1) {
45
- matches.push({
46
- end: haystack.ends[from + needle.length - 1],
47
- start: haystack.starts[from],
48
- });
49
- from = haystack.text.indexOf(needle, from + needle.length);
50
- }
51
- return matches;
10
+ return ps_utilities_1.Common.TextSearchService.find(text, query);
52
11
  }
53
12
  static findInItems(items, query) {
54
- const offsets = [];
55
- let joined = '';
56
- items.forEach((item) => {
57
- offsets.push(joined.length);
58
- joined += item.isBreakAfter ? `${item.text} ` : item.text;
59
- });
60
- return PsTextSearchService.find(joined, query).map((match) => {
61
- const parts = [];
62
- items.forEach((item, itemIndex) => {
63
- const start = Math.max(match.start, offsets[itemIndex]) - offsets[itemIndex];
64
- const end = Math.min(match.end, offsets[itemIndex] + item.text.length) - offsets[itemIndex];
65
- if (end > start)
66
- parts.push({ end, itemIndex, start });
67
- });
68
- return { parts };
69
- });
13
+ return ps_utilities_1.Common.TextSearchService.findInItems(items, query);
70
14
  }
71
15
  static segments(text, matches, firstIndex = 0) {
72
- const segments = [];
73
- let cursor = 0;
74
- matches.forEach((match, index) => {
75
- if (match.start > cursor)
76
- segments.push({ isMatch: false, matchIndex: null, text: text.slice(cursor, match.start) });
77
- segments.push({
78
- isMatch: true,
79
- matchIndex: firstIndex + index,
80
- text: text.slice(match.start, match.end),
81
- });
82
- cursor = match.end;
83
- });
84
- if (cursor < text.length)
85
- segments.push({ isMatch: false, matchIndex: null, text: text.slice(cursor) });
86
- return segments;
16
+ return ps_utilities_1.Common.TextSearchService.segments(text, matches, firstIndex);
87
17
  }
88
18
  static tableSegments(rows, query) {
89
19
  let offset = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-file-components",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -33,7 +33,7 @@
33
33
  "@pallasoft/ps-core-components": "^2.37.0",
34
34
  "@pallasoft/ps-icons": "^1.9.0",
35
35
  "@pallasoft/ps-popovers": "^1.13.0",
36
- "@pallasoft/ps-utilities": "^2.28.0",
36
+ "@pallasoft/ps-utilities": "^2.30.0",
37
37
  "dompurify": "^3.4.17",
38
38
  "mammoth": "^1.13.0",
39
39
  "pdfjs-dist": "^6.3.289",