@pallasoft/ps-mobile-components 1.5.0 → 1.7.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/dist/components/ps-infinite-list/index.d.ts +2 -0
- package/dist/components/ps-infinite-list/index.js +2 -0
- package/dist/components/ps-infinite-list/ps-infinite-list-footer.d.ts +2 -0
- package/dist/components/ps-infinite-list/ps-infinite-list-footer.js +5 -0
- package/dist/components/ps-infinite-list/ps-infinite-list-item.d.ts +2 -0
- package/dist/components/ps-infinite-list/ps-infinite-list-item.js +8 -0
- package/dist/components/ps-infinite-list/ps-infinite-list-props.d.ts +34 -0
- package/dist/components/ps-infinite-list/ps-infinite-list-props.js +1 -0
- package/dist/components/ps-infinite-list/ps-infinite-list.d.ts +2 -0
- package/dist/components/ps-infinite-list/ps-infinite-list.js +39 -0
- package/dist/components/ps-list/ps-list-select-row.js +8 -4
- package/dist/components/ps-menu-popup/ps-menu-popup-divider.js +8 -2
- package/dist/components/ps-menu-popup/ps-menu-popup-row.js +21 -9
- package/dist/components/ps-menu-popup/ps-menu-popup.js +28 -17
- package/dist/constants/ps-infinite-config.d.ts +6 -0
- package/dist/constants/ps-infinite-config.js +6 -0
- package/dist/constants/ps-material-menu-config.d.ts +12 -0
- package/dist/constants/ps-material-menu-config.js +12 -0
- package/dist/constants/ps-scan-format.d.ts +4 -0
- package/dist/constants/ps-scan-format.js +5 -0
- package/dist/constants/ps-scan-output-config.d.ts +17 -0
- package/dist/constants/ps-scan-output-config.js +23 -0
- package/dist/hooks/use-ps-document-scanner.js +3 -2
- package/dist/hooks/use-ps-infinite-data.d.ts +3 -0
- package/dist/hooks/use-ps-infinite-data.js +192 -0
- package/dist/hooks/use-ps-menu-popup-reveal.d.ts +3 -0
- package/dist/hooks/use-ps-menu-popup-reveal.js +28 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/interfaces/ps-capture-options.d.ts +2 -0
- package/dist/interfaces/ps-document-scanner-options.d.ts +2 -0
- package/dist/interfaces/ps-image-manipulator-native.d.ts +19 -0
- package/dist/interfaces/ps-image-manipulator-native.js +1 -0
- package/dist/interfaces/ps-infinite-data-options.d.ts +10 -0
- package/dist/interfaces/ps-infinite-data-options.js +1 -0
- package/dist/interfaces/ps-infinite-data-result.d.ts +12 -0
- package/dist/interfaces/ps-infinite-data-result.js +1 -0
- package/dist/interfaces/ps-infinite-data-state.d.ts +8 -0
- package/dist/interfaces/ps-infinite-data-state.js +1 -0
- package/dist/interfaces/ps-infinite-data-update.d.ts +1 -0
- package/dist/interfaces/ps-infinite-data-update.js +1 -0
- package/dist/interfaces/ps-infinite-data-write.d.ts +6 -0
- package/dist/interfaces/ps-infinite-data-write.js +1 -0
- package/dist/interfaces/ps-infinite-page.d.ts +4 -0
- package/dist/interfaces/ps-infinite-page.js +1 -0
- package/dist/interfaces/ps-menu-popup-container-style.d.ts +8 -0
- package/dist/interfaces/ps-menu-popup-container-style.js +1 -0
- package/dist/interfaces/ps-menu-popup-divider-style.d.ts +4 -0
- package/dist/interfaces/ps-menu-popup-divider-style.js +1 -0
- package/dist/interfaces/ps-menu-popup-layout.d.ts +1 -0
- package/dist/interfaces/ps-menu-popup-row-style.d.ts +6 -0
- package/dist/interfaces/ps-menu-popup-row-style.js +1 -0
- package/dist/interfaces/ps-menu-popup-size.d.ts +4 -0
- package/dist/interfaces/ps-menu-popup-size.js +1 -0
- package/dist/interfaces/ps-page-size.d.ts +4 -0
- package/dist/interfaces/ps-page-size.js +1 -0
- package/dist/interfaces/ps-pdf-page.d.ts +5 -0
- package/dist/interfaces/ps-pdf-page.js +1 -0
- package/dist/interfaces/ps-scan-format-labels.d.ts +7 -0
- package/dist/interfaces/ps-scan-format-labels.js +1 -0
- package/dist/services/ps-capture-picker-service.js +1 -0
- package/dist/services/ps-document-scanner-service.d.ts +2 -0
- package/dist/services/ps-document-scanner-service.js +21 -4
- package/dist/services/ps-infinite-data-service.d.ts +8 -0
- package/dist/services/ps-infinite-data-service.js +22 -0
- package/dist/services/ps-infinite-list-service.d.ts +10 -0
- package/dist/services/ps-infinite-list-service.js +36 -0
- package/dist/services/ps-jpeg-size-service.d.ts +4 -0
- package/dist/services/ps-jpeg-size-service.js +32 -0
- package/dist/services/ps-menu-popup-service.js +1 -0
- package/dist/services/ps-menu-popup-style-service.d.ts +13 -0
- package/dist/services/ps-menu-popup-style-service.js +55 -0
- package/dist/services/ps-scan-format-prompt-service.d.ts +6 -0
- package/dist/services/ps-scan-format-prompt-service.js +21 -0
- package/dist/services/ps-scan-image-converter-service.d.ts +5 -0
- package/dist/services/ps-scan-image-converter-service.js +22 -0
- package/dist/services/ps-scan-output-service.d.ts +10 -0
- package/dist/services/ps-scan-output-service.js +55 -0
- package/dist/services/ps-scan-pdf-service.d.ts +4 -0
- package/dist/services/ps-scan-pdf-service.js +6 -0
- package/jest-setup.js +1 -1
- package/package.json +6 -2
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ export * from './constants/ps-capture-source';
|
|
|
49
49
|
export * from './constants/ps-document-kind';
|
|
50
50
|
export * from './constants/ps-document-load-status';
|
|
51
51
|
export * from './constants/ps-document-scanner-config';
|
|
52
|
+
export * from './constants/ps-scan-format';
|
|
53
|
+
export * from './interfaces/ps-scan-format-labels';
|
|
52
54
|
export * from './constants/ps-chart-delta';
|
|
53
55
|
export * from './constants/ps-cartesian-series-shape';
|
|
54
56
|
export * from './constants/ps-chart-kind';
|
|
@@ -174,3 +176,9 @@ export * from './interfaces/ps-barcode-scan';
|
|
|
174
176
|
export * from './interfaces/ps-barcode-scanner-labels';
|
|
175
177
|
export * from './interfaces/use-ps-barcode-scanner-result';
|
|
176
178
|
export { PsBarcodeScannerService } from './services/ps-barcode-scanner-service';
|
|
179
|
+
export * from './components/ps-infinite-list';
|
|
180
|
+
export * from './hooks/use-ps-infinite-data';
|
|
181
|
+
export * from './interfaces/ps-infinite-data-options';
|
|
182
|
+
export * from './interfaces/ps-infinite-data-result';
|
|
183
|
+
export * from './interfaces/ps-infinite-data-update';
|
|
184
|
+
export * from './interfaces/ps-infinite-page';
|
package/dist/index.js
CHANGED
|
@@ -49,6 +49,8 @@ export * from './constants/ps-capture-source';
|
|
|
49
49
|
export * from './constants/ps-document-kind';
|
|
50
50
|
export * from './constants/ps-document-load-status';
|
|
51
51
|
export * from './constants/ps-document-scanner-config';
|
|
52
|
+
export * from './constants/ps-scan-format';
|
|
53
|
+
export * from './interfaces/ps-scan-format-labels';
|
|
52
54
|
export * from './constants/ps-chart-delta';
|
|
53
55
|
export * from './constants/ps-cartesian-series-shape';
|
|
54
56
|
export * from './constants/ps-chart-kind';
|
|
@@ -169,3 +171,9 @@ export * from './interfaces/ps-barcode-scan';
|
|
|
169
171
|
export * from './interfaces/ps-barcode-scanner-labels';
|
|
170
172
|
export * from './interfaces/use-ps-barcode-scanner-result';
|
|
171
173
|
export { PsBarcodeScannerService } from './services/ps-barcode-scanner-service';
|
|
174
|
+
export * from './components/ps-infinite-list';
|
|
175
|
+
export * from './hooks/use-ps-infinite-data';
|
|
176
|
+
export * from './interfaces/ps-infinite-data-options';
|
|
177
|
+
export * from './interfaces/ps-infinite-data-result';
|
|
178
|
+
export * from './interfaces/ps-infinite-data-update';
|
|
179
|
+
export * from './interfaces/ps-infinite-page';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface PsImageManipulatorResult {
|
|
2
|
+
uri: string;
|
|
3
|
+
}
|
|
4
|
+
export interface PsImageManipulatorContext {
|
|
5
|
+
renderAsync: () => Promise<PsImageManipulatorRendered>;
|
|
6
|
+
}
|
|
7
|
+
export interface PsImageManipulatorRendered {
|
|
8
|
+
saveAsync: (options: {
|
|
9
|
+
format: string;
|
|
10
|
+
}) => Promise<PsImageManipulatorResult>;
|
|
11
|
+
}
|
|
12
|
+
export interface PsImageManipulatorModule {
|
|
13
|
+
ImageManipulator: {
|
|
14
|
+
manipulate: (uri: string) => PsImageManipulatorContext;
|
|
15
|
+
};
|
|
16
|
+
SaveFormat: {
|
|
17
|
+
PNG: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PsInfinitePage } from './ps-infinite-page';
|
|
2
|
+
export interface PsInfiniteDataOptions<T> {
|
|
3
|
+
getKey: (item: T) => string;
|
|
4
|
+
isEnabled?: boolean;
|
|
5
|
+
key: string;
|
|
6
|
+
loadPage: (cursor: string | null) => Promise<PsInfinitePage<T>>;
|
|
7
|
+
onSettled?: () => Promise<unknown>;
|
|
8
|
+
onWriteFailed?: () => void;
|
|
9
|
+
refreshKey?: number | string;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PsInfiniteDataUpdate } from './ps-infinite-data-update';
|
|
2
|
+
export interface PsInfiniteDataResult<T> {
|
|
3
|
+
committedItems: T[];
|
|
4
|
+
error: boolean;
|
|
5
|
+
hasMore: boolean;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isLoadingMore: boolean;
|
|
8
|
+
items: T[];
|
|
9
|
+
loadMore: () => Promise<void>;
|
|
10
|
+
refresh: () => Promise<void>;
|
|
11
|
+
write: (update: PsInfiniteDataUpdate<T>, request: () => Promise<unknown>) => Promise<boolean>;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type PsInfiniteDataUpdate<T> = (items: T[]) => T[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -23,6 +23,7 @@ export class PsCapturePickerService {
|
|
|
23
23
|
}
|
|
24
24
|
return PsDocumentScannerService.scan({
|
|
25
25
|
fileName: options.scanFileName === undefined ? PsCaptureConfig.scanFileName : options.scanFileName,
|
|
26
|
+
formatLabels: options.scanFormatLabels,
|
|
26
27
|
jpegQuality: options.scanQuality === undefined ? PsCaptureConfig.scanQuality : options.scanQuality,
|
|
27
28
|
});
|
|
28
29
|
}
|
|
@@ -3,7 +3,9 @@ import type { PsScannedFile } from '../interfaces/ps-scanned-file';
|
|
|
3
3
|
export declare class PsDocumentScannerService {
|
|
4
4
|
static isAvailable(): boolean;
|
|
5
5
|
static scan(options: PsDocumentScannerOptions): Promise<PsScannedFile[]>;
|
|
6
|
+
static format(files: PsScannedFile[], options: PsDocumentScannerOptions): Promise<PsScannedFile[]>;
|
|
6
7
|
static toFiles(uris: string[], fileName?: string): PsScannedFile[];
|
|
8
|
+
static base(fileName?: string): string;
|
|
7
9
|
static quality(jpegQuality?: number): number;
|
|
8
10
|
static pageLimit(maxPages?: number): number | null;
|
|
9
11
|
private static load;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { PsDocumentScannerConfig } from '../constants/ps-document-scanner-config';
|
|
2
|
+
import { PsScanOutputConfig } from '../constants/ps-scan-output-config';
|
|
3
|
+
import { PsScanFormatPromptService } from './ps-scan-format-prompt-service';
|
|
4
|
+
import { PsScanOutputService } from './ps-scan-output-service';
|
|
2
5
|
export class PsDocumentScannerService {
|
|
3
6
|
static isAvailable() {
|
|
4
7
|
return PsDocumentScannerService.load() !== null;
|
|
@@ -16,18 +19,32 @@ export class PsDocumentScannerService {
|
|
|
16
19
|
return [];
|
|
17
20
|
const uris = response.scannedImages === undefined ? [] : response.scannedImages;
|
|
18
21
|
const limited = maxPages === null ? uris : uris.slice(0, maxPages);
|
|
19
|
-
|
|
22
|
+
const files = PsDocumentScannerService.toFiles(limited, options.fileName);
|
|
23
|
+
return PsDocumentScannerService.format(files, options);
|
|
24
|
+
}
|
|
25
|
+
static async format(files, options) {
|
|
26
|
+
const labels = options.formatLabels;
|
|
27
|
+
if (labels === undefined || files.length === 0 || !PsScanOutputService.isAvailable())
|
|
28
|
+
return files;
|
|
29
|
+
await new Promise((resolve) => setTimeout(resolve, PsScanOutputConfig.promptDelayMs));
|
|
30
|
+
const format = await PsScanFormatPromptService.ask(labels, PsScanFormatPromptService.preferred(files.length));
|
|
31
|
+
return format === null
|
|
32
|
+
? []
|
|
33
|
+
: PsScanOutputService.produce(files, format, PsDocumentScannerService.base(options.fileName));
|
|
20
34
|
}
|
|
21
35
|
static toFiles(uris, fileName) {
|
|
22
|
-
const base = fileName
|
|
23
|
-
? PsDocumentScannerConfig.defaultFileName
|
|
24
|
-
: fileName;
|
|
36
|
+
const base = PsDocumentScannerService.base(fileName);
|
|
25
37
|
return uris.map((uri, index) => ({
|
|
26
38
|
name: `${base}-${index + 1}.${PsDocumentScannerConfig.fileExtension}`,
|
|
27
39
|
type: PsDocumentScannerConfig.fileType,
|
|
28
40
|
uri,
|
|
29
41
|
}));
|
|
30
42
|
}
|
|
43
|
+
static base(fileName) {
|
|
44
|
+
return fileName === undefined || fileName === ''
|
|
45
|
+
? PsDocumentScannerConfig.defaultFileName
|
|
46
|
+
: fileName;
|
|
47
|
+
}
|
|
31
48
|
static quality(jpegQuality) {
|
|
32
49
|
if (jpegQuality === undefined)
|
|
33
50
|
return PsDocumentScannerConfig.defaultJpegQuality;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PsInfiniteDataState } from '../interfaces/ps-infinite-data-state';
|
|
2
|
+
import type { PsInfiniteDataUpdate } from '../interfaces/ps-infinite-data-update';
|
|
3
|
+
export declare class PsInfiniteDataService {
|
|
4
|
+
static initial<T>(): PsInfiniteDataState<T>;
|
|
5
|
+
static merge<T>(existing: T[], incoming: T[], getKey: (item: T) => string): T[];
|
|
6
|
+
static apply<T>(rows: T[], updates: PsInfiniteDataUpdate<T>[]): T[];
|
|
7
|
+
static hasMore<T>(state: PsInfiniteDataState<T>): boolean;
|
|
8
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class PsInfiniteDataService {
|
|
2
|
+
static initial() {
|
|
3
|
+
return {
|
|
4
|
+
error: false,
|
|
5
|
+
isLoading: true,
|
|
6
|
+
isLoadingMore: false,
|
|
7
|
+
items: [],
|
|
8
|
+
nextCursor: null,
|
|
9
|
+
pages: 0,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
static merge(existing, incoming, getKey) {
|
|
13
|
+
const keys = new Set(existing.map(getKey));
|
|
14
|
+
return [...existing, ...incoming.filter((item) => !keys.has(getKey(item)))];
|
|
15
|
+
}
|
|
16
|
+
static apply(rows, updates) {
|
|
17
|
+
return updates.reduce((current, update) => update(current), rows);
|
|
18
|
+
}
|
|
19
|
+
static hasMore(state) {
|
|
20
|
+
return state.nextCursor !== null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ViewStyle } from 'react-native';
|
|
2
|
+
import type { PsInfiniteSection } from '../components/ps-infinite-list/ps-infinite-list-props';
|
|
3
|
+
import type { PsMobileTheme } from '../interfaces/ps-mobile-theme';
|
|
4
|
+
export declare class PsInfiniteListService {
|
|
5
|
+
static readonly itemsKey = "ps-infinite-items";
|
|
6
|
+
static sections<T>(items: T[] | undefined, sections: PsInfiniteSection<T>[] | undefined): PsInfiniteSection<T>[];
|
|
7
|
+
static shouldLoadMore(hasMore: boolean, isLoading: boolean, isLoadingMore: boolean): boolean;
|
|
8
|
+
static itemFrame(theme: PsMobileTheme, index: number, count: number): ViewStyle;
|
|
9
|
+
static divider(theme: PsMobileTheme): ViewStyle;
|
|
10
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PsListConfig } from '../constants/ps-list-config';
|
|
2
|
+
import { PsMobileControlConfig } from '../constants/ps-mobile-control-config';
|
|
3
|
+
export class PsInfiniteListService {
|
|
4
|
+
static sections(items, sections) {
|
|
5
|
+
if (sections !== undefined)
|
|
6
|
+
return sections.filter((section) => section.data.length > 0);
|
|
7
|
+
return items === undefined || items.length === 0
|
|
8
|
+
? []
|
|
9
|
+
: [{ data: items, key: PsInfiniteListService.itemsKey }];
|
|
10
|
+
}
|
|
11
|
+
static shouldLoadMore(hasMore, isLoading, isLoadingMore) {
|
|
12
|
+
return hasMore && !isLoading && !isLoadingMore;
|
|
13
|
+
}
|
|
14
|
+
static itemFrame(theme, index, count) {
|
|
15
|
+
const isFirst = index === 0;
|
|
16
|
+
const isLast = index === count - 1;
|
|
17
|
+
const radius = PsMobileControlConfig.cardRadius;
|
|
18
|
+
return {
|
|
19
|
+
backgroundColor: theme.surface,
|
|
20
|
+
borderBottomLeftRadius: isLast ? radius : 0,
|
|
21
|
+
borderBottomRightRadius: isLast ? radius : 0,
|
|
22
|
+
borderBottomWidth: isLast ? 1 : 0,
|
|
23
|
+
borderColor: theme.line,
|
|
24
|
+
borderLeftWidth: 1,
|
|
25
|
+
borderRightWidth: 1,
|
|
26
|
+
borderTopLeftRadius: isFirst ? radius : 0,
|
|
27
|
+
borderTopRightRadius: isFirst ? radius : 0,
|
|
28
|
+
borderTopWidth: isFirst ? 1 : 0,
|
|
29
|
+
overflow: 'hidden',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
static divider(theme) {
|
|
33
|
+
return { backgroundColor: theme.line, height: 1, marginLeft: PsListConfig.dividerInset };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
PsInfiniteListService.itemsKey = 'ps-infinite-items';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PsScanOutputConfig } from '../constants/ps-scan-output-config';
|
|
2
|
+
export class PsJpegSizeService {
|
|
3
|
+
static size(bytes) {
|
|
4
|
+
const prefix = PsScanOutputConfig.jpegMarkerPrefix;
|
|
5
|
+
if (bytes.length < 4 || bytes[0] !== prefix || bytes[1] !== PsScanOutputConfig.jpegStartMarker)
|
|
6
|
+
return null;
|
|
7
|
+
let cursor = 2;
|
|
8
|
+
while (cursor + 4 <= bytes.length) {
|
|
9
|
+
if (bytes[cursor] !== prefix) {
|
|
10
|
+
cursor += 1;
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const marker = bytes[cursor + 1];
|
|
14
|
+
if (marker === prefix) {
|
|
15
|
+
cursor += 1;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const length = (bytes[cursor + 2] << 8) | bytes[cursor + 3];
|
|
19
|
+
if (PsScanOutputConfig.jpegSizeMarkers.some((size) => size === marker)) {
|
|
20
|
+
const at = cursor + 2 + PsScanOutputConfig.jpegSizeOffset;
|
|
21
|
+
if (at + 4 > bytes.length)
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
height: (bytes[at] << 8) | bytes[at + 1],
|
|
25
|
+
width: (bytes[at + 2] << 8) | bytes[at + 3],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
cursor += PsScanOutputConfig.jpegSegmentHeaderLength + length;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -14,6 +14,7 @@ export class PsMenuPopupService {
|
|
|
14
14
|
const alignedRight = anchor.x + menuWidth > window.width - margin;
|
|
15
15
|
const left = alignedRight ? anchor.x + anchor.width - menuWidth : anchor.x;
|
|
16
16
|
return {
|
|
17
|
+
isBelow,
|
|
17
18
|
left: Math.max(margin, Math.min(left, window.width - margin - menuWidth)),
|
|
18
19
|
maxHeight: Math.max(room, 0),
|
|
19
20
|
top,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PsFontWeight } from '../interfaces/ps-font-weight';
|
|
2
|
+
import type { PsMaterialScheme } from '../interfaces/ps-material-scheme';
|
|
3
|
+
import type { PsMenuPopupContainerStyle } from '../interfaces/ps-menu-popup-container-style';
|
|
4
|
+
import type { PsMenuPopupDividerStyle } from '../interfaces/ps-menu-popup-divider-style';
|
|
5
|
+
import type { PsMenuPopupRowStyle } from '../interfaces/ps-menu-popup-row-style';
|
|
6
|
+
import type { PsMobileTheme } from '../interfaces/ps-mobile-theme';
|
|
7
|
+
export declare class PsMenuPopupStyleService {
|
|
8
|
+
static isMaterial(os?: string): boolean;
|
|
9
|
+
static container(theme: PsMobileTheme, scheme: PsMaterialScheme, isMaterial: boolean): PsMenuPopupContainerStyle;
|
|
10
|
+
static row(theme: PsMobileTheme, scheme: PsMaterialScheme, isMaterial: boolean, isChecked: boolean, isPressed: boolean): PsMenuPopupRowStyle;
|
|
11
|
+
static divider(theme: PsMobileTheme, scheme: PsMaterialScheme, isMaterial: boolean): PsMenuPopupDividerStyle;
|
|
12
|
+
static labelWeight(isMaterial: boolean, isChecked: boolean): PsFontWeight;
|
|
13
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { PsMaterialMenuConfig } from '../constants/ps-material-menu-config';
|
|
3
|
+
import { PsMenuPopupConfig } from '../constants/ps-menu-popup-config';
|
|
4
|
+
export class PsMenuPopupStyleService {
|
|
5
|
+
static isMaterial(os = Platform.OS) {
|
|
6
|
+
return os === 'android';
|
|
7
|
+
}
|
|
8
|
+
static container(theme, scheme, isMaterial) {
|
|
9
|
+
if (isMaterial)
|
|
10
|
+
return {
|
|
11
|
+
backgroundColor: scheme.surfaceContainer,
|
|
12
|
+
borderColor: scheme.surfaceContainer,
|
|
13
|
+
borderRadius: PsMaterialMenuConfig.radius,
|
|
14
|
+
borderWidth: 0,
|
|
15
|
+
elevation: PsMaterialMenuConfig.elevation,
|
|
16
|
+
paddingVertical: PsMaterialMenuConfig.paddingVertical,
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
backgroundColor: theme.surface,
|
|
20
|
+
borderColor: theme.line,
|
|
21
|
+
borderRadius: PsMenuPopupConfig.radius,
|
|
22
|
+
borderWidth: 1,
|
|
23
|
+
elevation: 0,
|
|
24
|
+
paddingVertical: PsMenuPopupConfig.paddingVertical,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
static row(theme, scheme, isMaterial, isChecked, isPressed) {
|
|
28
|
+
if (isMaterial)
|
|
29
|
+
return {
|
|
30
|
+
backgroundColor: isChecked ? scheme.secondaryContainer : 'transparent',
|
|
31
|
+
borderRadius: PsMaterialMenuConfig.itemRadius,
|
|
32
|
+
marginHorizontal: PsMaterialMenuConfig.itemInset,
|
|
33
|
+
paddingHorizontal: PsMaterialMenuConfig.rowPaddingHorizontal,
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
backgroundColor: isPressed ? theme.primarySoft : 'transparent',
|
|
37
|
+
borderRadius: 0,
|
|
38
|
+
marginHorizontal: 0,
|
|
39
|
+
paddingHorizontal: PsMenuPopupConfig.rowPaddingHorizontal,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
static divider(theme, scheme, isMaterial) {
|
|
43
|
+
if (isMaterial)
|
|
44
|
+
return {
|
|
45
|
+
backgroundColor: scheme.outlineVariant,
|
|
46
|
+
marginVertical: PsMaterialMenuConfig.dividerInset,
|
|
47
|
+
};
|
|
48
|
+
return { backgroundColor: theme.line, marginVertical: 0 };
|
|
49
|
+
}
|
|
50
|
+
static labelWeight(isMaterial, isChecked) {
|
|
51
|
+
if (isMaterial)
|
|
52
|
+
return 'medium';
|
|
53
|
+
return isChecked ? 'semibold' : 'regular';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PsScanFormat } from '../constants/ps-scan-format';
|
|
2
|
+
import type { PsScanFormatLabels } from '../interfaces/ps-scan-format-labels';
|
|
3
|
+
export declare class PsScanFormatPromptService {
|
|
4
|
+
static ask(labels: PsScanFormatLabels, preferred: PsScanFormat): Promise<PsScanFormat | null>;
|
|
5
|
+
static preferred(pageCount: number): PsScanFormat;
|
|
6
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Alert } from 'react-native';
|
|
2
|
+
import { PsScanFormat } from '../constants/ps-scan-format';
|
|
3
|
+
export class PsScanFormatPromptService {
|
|
4
|
+
static ask(labels, preferred) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const choice = (format, text) => ({
|
|
7
|
+
isPreferred: format === preferred,
|
|
8
|
+
onPress: () => resolve(format),
|
|
9
|
+
text,
|
|
10
|
+
});
|
|
11
|
+
Alert.alert(labels.title, labels.message, [
|
|
12
|
+
choice(PsScanFormat.Pdf, labels.pdf),
|
|
13
|
+
choice(PsScanFormat.Png, labels.images),
|
|
14
|
+
{ onPress: () => resolve(null), style: 'cancel', text: labels.cancel },
|
|
15
|
+
], { cancelable: true, onDismiss: () => resolve(null) });
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
static preferred(pageCount) {
|
|
19
|
+
return pageCount > 1 ? PsScanFormat.Pdf : PsScanFormat.Png;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class PsScanImageConverterService {
|
|
2
|
+
static isAvailable() {
|
|
3
|
+
return PsScanImageConverterService.load() !== null;
|
|
4
|
+
}
|
|
5
|
+
static async toPng(uri) {
|
|
6
|
+
const loaded = PsScanImageConverterService.load();
|
|
7
|
+
if (loaded === null)
|
|
8
|
+
return uri;
|
|
9
|
+
const rendered = await loaded.ImageManipulator.manipulate(uri).renderAsync();
|
|
10
|
+
const saved = await rendered.saveAsync({ format: loaded.SaveFormat.PNG });
|
|
11
|
+
return saved.uri;
|
|
12
|
+
}
|
|
13
|
+
static load() {
|
|
14
|
+
try {
|
|
15
|
+
const loaded = require('expo-image-manipulator');
|
|
16
|
+
return typeof loaded.ImageManipulator.manipulate === 'function' ? loaded : null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PsScanFormat } from '../constants/ps-scan-format';
|
|
2
|
+
import type { PsScannedFile } from '../interfaces/ps-scanned-file';
|
|
3
|
+
export declare class PsScanOutputService {
|
|
4
|
+
static isAvailable(): boolean;
|
|
5
|
+
static produce(pages: PsScannedFile[], format: PsScanFormat, fileName: string): Promise<PsScannedFile[]>;
|
|
6
|
+
static pageName(base: string, index: number): string;
|
|
7
|
+
private static toPdf;
|
|
8
|
+
private static toPng;
|
|
9
|
+
private static loadFileSystem;
|
|
10
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { PsScanFormat } from '../constants/ps-scan-format';
|
|
2
|
+
import { PsScanOutputConfig } from '../constants/ps-scan-output-config';
|
|
3
|
+
import { PsJpegSizeService } from './ps-jpeg-size-service';
|
|
4
|
+
import { PsScanImageConverterService } from './ps-scan-image-converter-service';
|
|
5
|
+
import { PsScanPdfService } from './ps-scan-pdf-service';
|
|
6
|
+
export class PsScanOutputService {
|
|
7
|
+
static isAvailable() {
|
|
8
|
+
return (PsScanOutputService.loadFileSystem() !== null && PsScanImageConverterService.isAvailable());
|
|
9
|
+
}
|
|
10
|
+
static async produce(pages, format, fileName) {
|
|
11
|
+
return format === PsScanFormat.Pdf
|
|
12
|
+
? [await PsScanOutputService.toPdf(pages, fileName)]
|
|
13
|
+
: PsScanOutputService.toPng(pages, fileName);
|
|
14
|
+
}
|
|
15
|
+
static pageName(base, index) {
|
|
16
|
+
return `${base}-${index + 1}.${PsScanOutputConfig.extensions[PsScanFormat.Png]}`;
|
|
17
|
+
}
|
|
18
|
+
static async toPdf(pages, fileName) {
|
|
19
|
+
const system = PsScanOutputService.loadFileSystem();
|
|
20
|
+
if (system === null)
|
|
21
|
+
throw new Error('expo-file-system is not available');
|
|
22
|
+
const loaded = [];
|
|
23
|
+
for (const page of pages) {
|
|
24
|
+
const bytes = new Uint8Array(await new system.File(page.uri).arrayBuffer());
|
|
25
|
+
const size = PsJpegSizeService.size(bytes);
|
|
26
|
+
if (size === null)
|
|
27
|
+
throw new Error('unreadable scan page');
|
|
28
|
+
loaded.push({ bytes, height: size.height, width: size.width });
|
|
29
|
+
}
|
|
30
|
+
const name = `${fileName}.${PsScanOutputConfig.extensions[PsScanFormat.Pdf]}`;
|
|
31
|
+
const file = new system.File(system.Paths.cache, `${Date.now()}-${name}`);
|
|
32
|
+
file.create({ overwrite: true });
|
|
33
|
+
await file.write(PsScanPdfService.fromJpegs(loaded));
|
|
34
|
+
return { name, type: PsScanOutputConfig.pdfType, uri: file.uri };
|
|
35
|
+
}
|
|
36
|
+
static async toPng(pages, fileName) {
|
|
37
|
+
const files = [];
|
|
38
|
+
for (const [index, page] of pages.entries())
|
|
39
|
+
files.push({
|
|
40
|
+
name: PsScanOutputService.pageName(fileName, index),
|
|
41
|
+
type: PsScanOutputConfig.pngType,
|
|
42
|
+
uri: await PsScanImageConverterService.toPng(page.uri),
|
|
43
|
+
});
|
|
44
|
+
return files;
|
|
45
|
+
}
|
|
46
|
+
static loadFileSystem() {
|
|
47
|
+
try {
|
|
48
|
+
const loaded = require('expo-file-system');
|
|
49
|
+
return typeof loaded.File === 'function' ? loaded : null;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|