@pallasoft/ps-mobile-components 1.4.0 → 1.5.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.
@@ -1,2 +1,2 @@
1
1
  import type { PsListActionRowProps } from './ps-list-props';
2
- export declare const PsListActionRow: ({ children, iconKey, isDisabled, onPress, tone, }: PsListActionRowProps) => import("react").JSX.Element;
2
+ export declare const PsListActionRow: ({ children, iconKey, isDisabled, isExternal, onPress, tone, }: PsListActionRowProps) => import("react").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PsIconKey } from '@pallasoft/ps-icons';
2
3
  import { Pressable } from 'react-native';
3
4
  import { PsLayoutConfig } from '../../constants/ps-layout-config';
4
5
  import { PsMobileControlConfig } from '../../constants/ps-mobile-control-config';
@@ -8,11 +9,11 @@ import { PsRippleService } from '../../services/ps-ripple-service';
8
9
  import { PsStateService } from '../../services/ps-state-service';
9
10
  import { PsIcon } from '../ps-icon/ps-icon';
10
11
  import { PsText } from '../ps-text/ps-text';
11
- export const PsListActionRow = ({ children, iconKey, isDisabled = false, onPress, tone = 'primary', }) => {
12
+ export const PsListActionRow = ({ children, iconKey, isDisabled = false, isExternal = false, onPress, tone = 'primary', }) => {
12
13
  const theme = usePsMobileTheme();
13
14
  const isDestructive = tone === 'destructive';
14
15
  const color = isDestructive ? theme.danger : theme.ink;
15
- return (_jsxs(Pressable, { android_ripple: PsRippleService.resolve(theme, isDestructive ? PsRippleKind.Danger : PsRippleKind.Surface), accessibilityRole: 'button', accessibilityState: { disabled: isDisabled }, disabled: isDisabled, onPress: onPress, style: ({ pressed }) => ({
16
+ return (_jsxs(Pressable, { android_ripple: PsRippleService.resolve(theme, isDestructive ? PsRippleKind.Danger : PsRippleKind.Surface), accessibilityRole: isExternal ? 'link' : 'button', accessibilityState: { disabled: isDisabled }, disabled: isDisabled, onPress: onPress, style: ({ pressed }) => ({
16
17
  alignItems: 'center',
17
18
  backgroundColor: pressed ? PsStateService.pressedFill(theme, theme.surface) : theme.surface,
18
19
  flexDirection: 'row',
@@ -20,5 +21,5 @@ export const PsListActionRow = ({ children, iconKey, isDisabled = false, onPress
20
21
  minHeight: PsLayoutConfig.rowMinHeight,
21
22
  opacity: isDisabled ? PsStateService.disabledOpacity() : 1,
22
23
  paddingHorizontal: PsLayoutConfig.gutter,
23
- }), testID: 'ps-list-action-row', children: [iconKey === undefined ? null : _jsx(PsIcon, { color: color, iconKey: iconKey }), _jsx(PsText, { style: { color }, weight: 'semibold', children: children })] }));
24
+ }), testID: 'ps-list-action-row', children: [iconKey === undefined ? null : _jsx(PsIcon, { color: color, iconKey: iconKey }), _jsx(PsText, { style: { color, flex: 1 }, weight: 'semibold', children: children }), isExternal ? (_jsx(PsIcon, { color: theme.muted, iconKey: PsIconKey.ExternalLink, size: PsMobileControlConfig.iconSizeInline })) : null] }));
24
25
  };
@@ -4,6 +4,7 @@ export interface PsListLinkRowProps {
4
4
  countLabel?: string;
5
5
  iconKey: PsMobileIconKey;
6
6
  isAlert?: boolean;
7
+ isExternal?: boolean;
7
8
  isLoading?: boolean;
8
9
  onPress: () => void;
9
10
  subtitle?: string;
@@ -1,2 +1,2 @@
1
1
  import type { PsListLinkRowProps } from './ps-list-link-row-props';
2
- export declare const PsListLinkRow: ({ count, countLabel, iconKey, isAlert, isLoading, onPress, subtitle, title, }: PsListLinkRowProps) => import("react").JSX.Element;
2
+ export declare const PsListLinkRow: ({ count, countLabel, iconKey, isAlert, isExternal, isLoading, onPress, subtitle, title, }: PsListLinkRowProps) => import("react").JSX.Element;
@@ -12,13 +12,13 @@ import { PsCountBadge } from '../ps-count-badge/ps-count-badge';
12
12
  import { PsIcon } from '../ps-icon/ps-icon';
13
13
  import { PsText } from '../ps-text/ps-text';
14
14
  import { PsListLinkRowSkeleton } from './ps-list-link-row-skeleton';
15
- export const PsListLinkRow = ({ count, countLabel, iconKey, isAlert = false, isLoading = false, onPress, subtitle, title, }) => {
15
+ export const PsListLinkRow = ({ count, countLabel, iconKey, isAlert = false, isExternal = false, isLoading = false, onPress, subtitle, title, }) => {
16
16
  const theme = usePsMobileTheme();
17
17
  const haptic = usePsHaptics();
18
18
  if (isLoading)
19
19
  return _jsx(PsListLinkRowSkeleton, {});
20
20
  const hasCount = count !== undefined && count > 0;
21
- return (_jsxs(Pressable, { android_ripple: PsRippleService.resolve(theme, PsRippleKind.Surface), accessibilityHint: countLabel, accessibilityRole: 'button', onPress: () => {
21
+ return (_jsxs(Pressable, { android_ripple: PsRippleService.resolve(theme, PsRippleKind.Surface), accessibilityHint: countLabel, accessibilityRole: isExternal ? 'link' : 'button', onPress: () => {
22
22
  haptic('light');
23
23
  onPress();
24
24
  }, style: ({ pressed }) => ({
@@ -36,5 +36,5 @@ export const PsListLinkRow = ({ count, countLabel, iconKey, isAlert = false, isL
36
36
  height: PsLayoutConfig.linkRowIconBox,
37
37
  justifyContent: 'center',
38
38
  width: PsLayoutConfig.linkRowIconBox,
39
- }, children: _jsx(PsIcon, { color: theme.primaryText, iconKey: iconKey, size: PsLayoutConfig.linkRowIconSize }) }), _jsxs(View, { style: { flex: 1, gap: PsLayoutConfig.rowLineGap }, children: [_jsx(PsText, { numberOfLines: 1, weight: 'semibold', children: title }), subtitle === undefined ? null : (_jsx(PsText, { numberOfLines: 1, tone: 'muted', variant: 'small', children: subtitle }))] }), hasCount ? _jsx(PsCountBadge, { count: count, isAlert: isAlert, label: countLabel }) : null, _jsx(PsIcon, { color: theme.muted, iconKey: PsIconKey.ChevronRight, size: PsMobileControlConfig.iconSizeInline })] }));
39
+ }, children: _jsx(PsIcon, { color: theme.primaryText, iconKey: iconKey, size: PsLayoutConfig.linkRowIconSize }) }), _jsxs(View, { style: { flex: 1, gap: PsLayoutConfig.rowLineGap }, children: [_jsx(PsText, { numberOfLines: 1, weight: 'semibold', children: title }), subtitle === undefined ? null : (_jsx(PsText, { numberOfLines: 1, tone: 'muted', variant: 'small', children: subtitle }))] }), hasCount ? _jsx(PsCountBadge, { count: count, isAlert: isAlert, label: countLabel }) : null, _jsx(PsIcon, { color: theme.muted, iconKey: isExternal ? PsIconKey.ExternalLink : PsIconKey.ChevronRight, size: PsMobileControlConfig.iconSizeInline })] }));
40
40
  };
@@ -66,6 +66,7 @@ export interface PsListActionRowProps {
66
66
  children: string;
67
67
  iconKey?: PsIconKey;
68
68
  isDisabled?: boolean;
69
+ isExternal?: boolean;
69
70
  onPress?: () => void;
70
71
  tone?: 'primary' | 'destructive';
71
72
  }
@@ -22,6 +22,7 @@ export declare const PsMobileIconKey: {
22
22
  readonly Doc: "doc";
23
23
  readonly Download: "download";
24
24
  readonly Expand: "expand";
25
+ readonly ExternalLink: "external-link";
25
26
  readonly Eye: "eye";
26
27
  readonly EyeOff: "eye-off";
27
28
  readonly File: "file";
@@ -22,6 +22,7 @@ export declare const PsSfSymbolConfig: {
22
22
  doc: "doc.text";
23
23
  download: "arrow.down.circle";
24
24
  expand: "arrow.up.left.and.arrow.down.right";
25
+ 'external-link': "arrow.up.right.square";
25
26
  eye: "eye";
26
27
  'eye-off': "eye.slash";
27
28
  file: "doc";
@@ -22,6 +22,7 @@ export const PsSfSymbolConfig = {
22
22
  'doc': 'doc.text',
23
23
  'download': 'arrow.down.circle',
24
24
  'expand': 'arrow.up.left.and.arrow.down.right',
25
+ 'external-link': 'arrow.up.right.square',
25
26
  'eye': 'eye',
26
27
  'eye-off': 'eye.slash',
27
28
  'file': 'doc',
@@ -1,4 +1,5 @@
1
1
  export interface PsDeviceFile {
2
+ bytes?: () => Promise<Uint8Array>;
2
3
  name: string;
3
4
  type: string;
4
5
  uri: string;
@@ -0,0 +1,4 @@
1
+ export interface PsOpenedFile {
2
+ arrayBuffer: () => Promise<ArrayBuffer>;
3
+ }
4
+ export type PsFileOpener = (uri: string) => PsOpenedFile;
@@ -0,0 +1 @@
1
+ export {};
@@ -3,6 +3,7 @@ import type { PsCaptureOptions } from '../interfaces/ps-capture-options';
3
3
  import type { PsDeviceFile } from '../interfaces/ps-device-file';
4
4
  export declare class PsCapturePickerService {
5
5
  static pick(source: PsCaptureSource, options?: PsCaptureOptions): Promise<PsDeviceFile[]>;
6
+ private static pickRaw;
6
7
  private static scan;
7
8
  private static pickImage;
8
9
  private static pickDocument;
@@ -1,9 +1,14 @@
1
1
  import { PsCaptureConfig } from '../constants/ps-capture-config';
2
2
  import { PsCaptureSource } from '../constants/ps-capture-source';
3
+ import { PsDeviceFileBytesService } from './ps-device-file-bytes-service';
3
4
  import { PsDeviceFileService } from './ps-device-file-service';
4
5
  import { PsDocumentScannerService } from './ps-document-scanner-service';
5
6
  export class PsCapturePickerService {
6
7
  static async pick(source, options = {}) {
8
+ const files = await PsCapturePickerService.pickRaw(source, options);
9
+ return files.map((file) => PsDeviceFileBytesService.withBytes(file));
10
+ }
11
+ static async pickRaw(source, options) {
7
12
  if (source === PsCaptureSource.Scan)
8
13
  return PsCapturePickerService.scan(options);
9
14
  const file = source === PsCaptureSource.Files
@@ -0,0 +1,6 @@
1
+ import type { PsDeviceFile } from '../interfaces/ps-device-file';
2
+ import type { PsFileOpener } from '../interfaces/ps-file-opener';
3
+ export declare class PsDeviceFileBytesService {
4
+ static withBytes(file: PsDeviceFile, open?: PsFileOpener | null): PsDeviceFile;
5
+ private static opener;
6
+ }
@@ -0,0 +1,19 @@
1
+ export class PsDeviceFileBytesService {
2
+ static withBytes(file, open = PsDeviceFileBytesService.opener()) {
3
+ if (open === null)
4
+ return file;
5
+ return {
6
+ ...file,
7
+ bytes: async () => new Uint8Array(await open(file.uri).arrayBuffer()),
8
+ };
9
+ }
10
+ static opener() {
11
+ try {
12
+ const loaded = require('expo-file-system');
13
+ return typeof loaded.File === 'function' ? (uri) => new loaded.File(uri) : null;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-mobile-components",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "private": false,
5
5
  "description": "Native iOS (SwiftUI) and Android (Jetpack Compose) Ps components for Pallasoft mobile apps",
6
6
  "main": "./dist/index.js",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@pallasoft/ps-design-system": "^1.19.0",
38
- "@pallasoft/ps-icons": "^1.11.0",
38
+ "@pallasoft/ps-icons": "^1.12.0",
39
39
  "@pallasoft/ps-utilities": "^2.27.0"
40
40
  },
41
41
  "peerDependencies": {