@ankhorage/zora 1.0.10 → 1.2.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 +16 -0
- package/README.md +164 -5
- package/dist/components/app-bar/AppBar.d.ts +4 -0
- package/dist/components/app-bar/AppBar.d.ts.map +1 -0
- package/dist/components/app-bar/AppBar.js +63 -0
- package/dist/components/app-bar/AppBar.js.map +1 -0
- package/dist/components/app-bar/index.d.ts +3 -0
- package/dist/components/app-bar/index.d.ts.map +1 -0
- package/dist/components/app-bar/index.js +3 -0
- package/dist/components/app-bar/index.js.map +1 -0
- package/dist/components/app-bar/types.d.ts +31 -0
- package/dist/components/app-bar/types.d.ts.map +1 -0
- package/dist/components/app-bar/types.js +2 -0
- package/dist/components/app-bar/types.js.map +1 -0
- package/dist/components/image/Image.d.ts +4 -0
- package/dist/components/image/Image.d.ts.map +1 -0
- package/dist/components/image/Image.js +8 -0
- package/dist/components/image/Image.js.map +1 -0
- package/dist/components/image/index.d.ts +3 -0
- package/dist/components/image/index.d.ts.map +1 -0
- package/dist/components/image/index.js +2 -0
- package/dist/components/image/index.js.map +1 -0
- package/dist/components/image/types.d.ts +6 -0
- package/dist/components/image/types.d.ts.map +1 -0
- package/dist/components/image/types.js +2 -0
- package/dist/components/image/types.js.map +1 -0
- package/dist/components/input/types.d.ts +1 -1
- package/dist/components/input/types.d.ts.map +1 -1
- package/dist/components/input/types.js.map +1 -1
- package/dist/components/toolbar/types.d.ts +1 -1
- package/dist/components/toolbar/types.d.ts.map +1 -1
- package/dist/components/toolbar/types.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/resolveZoraNavigationItems.d.ts +4 -3
- package/dist/internal/resolveZoraNavigationItems.d.ts.map +1 -1
- package/dist/internal/resolveZoraNavigationItems.js.map +1 -1
- package/dist/patterns/image-preview/ImagePreview.d.ts +4 -0
- package/dist/patterns/image-preview/ImagePreview.d.ts.map +1 -0
- package/dist/patterns/image-preview/ImagePreview.js +41 -0
- package/dist/patterns/image-preview/ImagePreview.js.map +1 -0
- package/dist/patterns/image-preview/index.d.ts +3 -0
- package/dist/patterns/image-preview/index.d.ts.map +1 -0
- package/dist/patterns/image-preview/index.js +2 -0
- package/dist/patterns/image-preview/index.js.map +1 -0
- package/dist/patterns/image-preview/types.d.ts +36 -0
- package/dist/patterns/image-preview/types.d.ts.map +1 -0
- package/dist/patterns/image-preview/types.js +2 -0
- package/dist/patterns/image-preview/types.js.map +1 -0
- package/dist/patterns/image-upload-field/ImageUploadField.d.ts +4 -0
- package/dist/patterns/image-upload-field/ImageUploadField.d.ts.map +1 -0
- package/dist/patterns/image-upload-field/ImageUploadField.js +211 -0
- package/dist/patterns/image-upload-field/ImageUploadField.js.map +1 -0
- package/dist/patterns/image-upload-field/index.d.ts +3 -0
- package/dist/patterns/image-upload-field/index.d.ts.map +1 -0
- package/dist/patterns/image-upload-field/index.js +2 -0
- package/dist/patterns/image-upload-field/index.js.map +1 -0
- package/dist/patterns/image-upload-field/types.d.ts +35 -0
- package/dist/patterns/image-upload-field/types.d.ts.map +1 -0
- package/dist/patterns/image-upload-field/types.js +2 -0
- package/dist/patterns/image-upload-field/types.js.map +1 -0
- package/dist/patterns/image-upload-field/uploadFlow.d.ts +18 -0
- package/dist/patterns/image-upload-field/uploadFlow.d.ts.map +1 -0
- package/dist/patterns/image-upload-field/uploadFlow.js +106 -0
- package/dist/patterns/image-upload-field/uploadFlow.js.map +1 -0
- package/dist/patterns/list/types.d.ts +2 -2
- package/dist/patterns/list/types.d.ts.map +1 -1
- package/dist/patterns/list/types.js.map +1 -1
- package/dist/patterns/responsive-panel/types.d.ts +1 -1
- package/dist/patterns/responsive-panel/types.d.ts.map +1 -1
- package/dist/patterns/responsive-panel/types.js.map +1 -1
- package/package.json +9 -10
- package/src/components/app-bar/AppBar.tsx +133 -0
- package/src/components/app-bar/index.ts +2 -0
- package/src/components/app-bar/types.ts +36 -0
- package/src/components/image/Image.tsx +11 -0
- package/src/components/image/index.ts +2 -0
- package/src/components/image/types.ts +7 -0
- package/src/components/input/types.ts +1 -1
- package/src/components/toolbar/types.ts +2 -2
- package/src/index.ts +24 -3
- package/src/internal/resolveZoraNavigationItems.ts +3 -3
- package/src/patterns/image-preview/ImagePreview.tsx +76 -0
- package/src/patterns/image-preview/index.ts +2 -0
- package/src/patterns/image-preview/types.ts +41 -0
- package/src/patterns/image-upload-field/ImageUploadField.tsx +293 -0
- package/src/patterns/image-upload-field/index.ts +2 -0
- package/src/patterns/image-upload-field/types.ts +41 -0
- package/src/patterns/image-upload-field/uploadFlow.test.ts +117 -0
- package/src/patterns/image-upload-field/uploadFlow.ts +145 -0
- package/src/patterns/list/types.ts +2 -2
- package/src/patterns/responsive-panel/types.ts +2 -2
- package/src/showcaseCoverage.test.ts +4 -0
- package/src/theme/themeScopeStructure.test.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadFlow.d.ts","sourceRoot":"","sources":["../../../src/patterns/image-upload-field/uploadFlow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAuC/C,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,WAAW,EACX,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,GAAG,OAAO,CAkBV;AAmBD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAI1E;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAIjF;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,eAAe,GAAG,cAAc,CAYvF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAIhF;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,MAAM,EACN,YAAY,EACZ,cAAc,GACf,EAAE;IACD,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,eAAe,KAAK,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;CAC/E,GAAG,MAAM,GAAG,SAAS,CAiBrB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
function parseAccept(accept) {
|
|
2
|
+
if (!accept)
|
|
3
|
+
return [];
|
|
4
|
+
return accept
|
|
5
|
+
.split(',')
|
|
6
|
+
.map((entry) => entry.trim())
|
|
7
|
+
.filter((entry) => entry.length > 0);
|
|
8
|
+
}
|
|
9
|
+
function matchesAcceptToken({ token, contentType, fileName, }) {
|
|
10
|
+
const normalizedToken = token.toLowerCase();
|
|
11
|
+
const normalizedContentType = contentType?.toLowerCase();
|
|
12
|
+
const normalizedFileName = fileName?.toLowerCase();
|
|
13
|
+
if (normalizedToken.startsWith('.')) {
|
|
14
|
+
if (!normalizedFileName)
|
|
15
|
+
return null;
|
|
16
|
+
return normalizedFileName.endsWith(normalizedToken);
|
|
17
|
+
}
|
|
18
|
+
if (normalizedToken.endsWith('/*')) {
|
|
19
|
+
if (!normalizedContentType)
|
|
20
|
+
return null;
|
|
21
|
+
const prefix = normalizedToken.slice(0, Math.max(0, normalizedToken.length - 1));
|
|
22
|
+
return normalizedContentType.startsWith(prefix);
|
|
23
|
+
}
|
|
24
|
+
if (!normalizedContentType)
|
|
25
|
+
return null;
|
|
26
|
+
return normalizedContentType === normalizedToken;
|
|
27
|
+
}
|
|
28
|
+
export function isAccepted({ accept, contentType, fileName, }) {
|
|
29
|
+
const tokens = parseAccept(accept);
|
|
30
|
+
if (tokens.length === 0)
|
|
31
|
+
return true;
|
|
32
|
+
let hadSignal = false;
|
|
33
|
+
for (const token of tokens) {
|
|
34
|
+
const matches = matchesAcceptToken({ token, contentType, fileName });
|
|
35
|
+
if (matches === null) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
hadSignal = true;
|
|
39
|
+
if (matches)
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
// If we cannot validate due to missing metadata, do not block.
|
|
43
|
+
return !hadSignal;
|
|
44
|
+
}
|
|
45
|
+
function formatBytes(value) {
|
|
46
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
47
|
+
return '0 B';
|
|
48
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
49
|
+
let size = value;
|
|
50
|
+
let unitIndex = 0;
|
|
51
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
52
|
+
size /= 1024;
|
|
53
|
+
unitIndex += 1;
|
|
54
|
+
}
|
|
55
|
+
const unit = units[unitIndex] ?? 'B';
|
|
56
|
+
const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;
|
|
57
|
+
return `${rounded} ${unit}`;
|
|
58
|
+
}
|
|
59
|
+
export function formatAcceptHint(accept) {
|
|
60
|
+
const tokens = parseAccept(accept);
|
|
61
|
+
if (tokens.length === 0)
|
|
62
|
+
return null;
|
|
63
|
+
return `Accepted: ${tokens.join(', ')}`;
|
|
64
|
+
}
|
|
65
|
+
export function formatMaxSizeHint(maxSizeBytes) {
|
|
66
|
+
if (maxSizeBytes === undefined)
|
|
67
|
+
return null;
|
|
68
|
+
if (!Number.isFinite(maxSizeBytes) || maxSizeBytes <= 0)
|
|
69
|
+
return null;
|
|
70
|
+
return `Max size: ${formatBytes(maxSizeBytes)}`;
|
|
71
|
+
}
|
|
72
|
+
export function createOptimisticAssetFromPicked(picked) {
|
|
73
|
+
return {
|
|
74
|
+
kind: 'url',
|
|
75
|
+
url: picked.uri,
|
|
76
|
+
width: picked.width,
|
|
77
|
+
height: picked.height,
|
|
78
|
+
contentType: picked.contentType,
|
|
79
|
+
metadata: {
|
|
80
|
+
fileName: picked.fileName,
|
|
81
|
+
sizeBytes: picked.sizeBytes,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function resolveRenderableUrl(asset) {
|
|
86
|
+
if (!asset)
|
|
87
|
+
return null;
|
|
88
|
+
if (asset.kind === 'url')
|
|
89
|
+
return asset.url;
|
|
90
|
+
return asset.publicUrl ?? null;
|
|
91
|
+
}
|
|
92
|
+
export function validatePickedImage({ picked, accept, maxSizeBytes, validatePicked, }) {
|
|
93
|
+
if (!isAccepted({ accept, contentType: picked.contentType, fileName: picked.fileName })) {
|
|
94
|
+
return accept ? `File type not accepted. Expected ${accept}.` : 'File type not accepted.';
|
|
95
|
+
}
|
|
96
|
+
if (maxSizeBytes !== undefined &&
|
|
97
|
+
Number.isFinite(maxSizeBytes) &&
|
|
98
|
+
maxSizeBytes > 0 &&
|
|
99
|
+
picked.sizeBytes !== undefined &&
|
|
100
|
+
Number.isFinite(picked.sizeBytes) &&
|
|
101
|
+
picked.sizeBytes > maxSizeBytes) {
|
|
102
|
+
return `File is too large (${formatBytes(picked.sizeBytes)}). Max ${formatBytes(maxSizeBytes)}.`;
|
|
103
|
+
}
|
|
104
|
+
return validatePicked?.(picked);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=uploadFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadFlow.js","sourceRoot":"","sources":["../../../src/patterns/image-upload-field/uploadFlow.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ZoraImageAsset } from '../image-preview';\nimport type { ZoraPickedImage } from './types';\n\nfunction parseAccept(accept: string | undefined): readonly string[] {\n if (!accept) return [];\n\n return accept\n .split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n}\n\nfunction matchesAcceptToken({\n token,\n contentType,\n fileName,\n}: {\n token: string;\n contentType: string | undefined;\n fileName: string | undefined;\n}): boolean | null {\n const normalizedToken = token.toLowerCase();\n const normalizedContentType = contentType?.toLowerCase();\n const normalizedFileName = fileName?.toLowerCase();\n\n if (normalizedToken.startsWith('.')) {\n if (!normalizedFileName) return null;\n return normalizedFileName.endsWith(normalizedToken);\n }\n\n if (normalizedToken.endsWith('/*')) {\n if (!normalizedContentType) return null;\n const prefix = normalizedToken.slice(0, Math.max(0, normalizedToken.length - 1));\n return normalizedContentType.startsWith(prefix);\n }\n\n if (!normalizedContentType) return null;\n return normalizedContentType === normalizedToken;\n}\n\nexport function isAccepted({\n accept,\n contentType,\n fileName,\n}: {\n accept: string | undefined;\n contentType: string | undefined;\n fileName: string | undefined;\n}): boolean {\n const tokens = parseAccept(accept);\n if (tokens.length === 0) return true;\n\n let hadSignal = false;\n\n for (const token of tokens) {\n const matches = matchesAcceptToken({ token, contentType, fileName });\n if (matches === null) {\n continue;\n }\n\n hadSignal = true;\n if (matches) return true;\n }\n\n // If we cannot validate due to missing metadata, do not block.\n return !hadSignal;\n}\n\nfunction formatBytes(value: number): string {\n if (!Number.isFinite(value) || value <= 0) return '0 B';\n\n const units = ['B', 'KB', 'MB', 'GB'] as const;\n let size = value;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex += 1;\n }\n\n const unit = units[unitIndex] ?? 'B';\n const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;\n return `${rounded} ${unit}`;\n}\n\nexport function formatAcceptHint(accept: string | undefined): string | null {\n const tokens = parseAccept(accept);\n if (tokens.length === 0) return null;\n return `Accepted: ${tokens.join(', ')}`;\n}\n\nexport function formatMaxSizeHint(maxSizeBytes: number | undefined): string | null {\n if (maxSizeBytes === undefined) return null;\n if (!Number.isFinite(maxSizeBytes) || maxSizeBytes <= 0) return null;\n return `Max size: ${formatBytes(maxSizeBytes)}`;\n}\n\nexport function createOptimisticAssetFromPicked(picked: ZoraPickedImage): ZoraImageAsset {\n return {\n kind: 'url',\n url: picked.uri,\n width: picked.width,\n height: picked.height,\n contentType: picked.contentType,\n metadata: {\n fileName: picked.fileName,\n sizeBytes: picked.sizeBytes,\n },\n };\n}\n\nexport function resolveRenderableUrl(asset: ZoraImageAsset | null): string | null {\n if (!asset) return null;\n if (asset.kind === 'url') return asset.url;\n return asset.publicUrl ?? null;\n}\n\nexport function validatePickedImage({\n picked,\n accept,\n maxSizeBytes,\n validatePicked,\n}: {\n picked: ZoraPickedImage;\n accept: string | undefined;\n maxSizeBytes: number | undefined;\n validatePicked: ((picked: ZoraPickedImage) => string | undefined) | undefined;\n}): string | undefined {\n if (!isAccepted({ accept, contentType: picked.contentType, fileName: picked.fileName })) {\n return accept ? `File type not accepted. Expected ${accept}.` : 'File type not accepted.';\n }\n\n if (\n maxSizeBytes !== undefined &&\n Number.isFinite(maxSizeBytes) &&\n maxSizeBytes > 0 &&\n picked.sizeBytes !== undefined &&\n Number.isFinite(picked.sizeBytes) &&\n picked.sizeBytes > maxSizeBytes\n ) {\n return `File is too large (${formatBytes(picked.sizeBytes)}). Max ${formatBytes(maxSizeBytes)}.`;\n }\n\n return validatePicked?.(picked);\n}\n"]}
|
|
@@ -34,7 +34,7 @@ export interface ListChildrenProps extends ZoraBaseProps {
|
|
|
34
34
|
children: React.ReactNode;
|
|
35
35
|
}
|
|
36
36
|
export type ListProps = ListItemsProps | ListChildrenProps;
|
|
37
|
-
|
|
37
|
+
interface ListSectionItemsProps extends ZoraBaseProps {
|
|
38
38
|
title?: React.ReactNode;
|
|
39
39
|
description?: React.ReactNode;
|
|
40
40
|
eyebrow?: React.ReactNode;
|
|
@@ -43,7 +43,7 @@ export interface ListSectionItemsProps extends ZoraBaseProps {
|
|
|
43
43
|
rowVariant?: ListRowVariant;
|
|
44
44
|
compact?: boolean;
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
interface ListSectionChildrenProps extends ZoraBaseProps {
|
|
47
47
|
title?: React.ReactNode;
|
|
48
48
|
description?: React.ReactNode;
|
|
49
49
|
eyebrow?: React.ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/patterns/list/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,CAAC;AAEhD,UAAU,gBAAiB,SAAQ,aAAa;IAC9C,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB;AAED,UAAU,kBAAkB;IAC1B,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,gBAAgB,GACzC,CAAC,qBAAqB,GAAG,kBAAkB,GAAG,kBAAkB,CAAC,CAAC;AAEpE,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAE3D,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/patterns/list/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,CAAC;AAEhD,UAAU,gBAAiB,SAAQ,aAAa;IAC9C,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB;AAED,UAAU,kBAAkB;IAC1B,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,gBAAgB,GACzC,CAAC,qBAAqB,GAAG,kBAAkB,GAAG,kBAAkB,CAAC,CAAC;AAEpE,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAE3D,UAAU,qBAAsB,SAAQ,aAAa;IACnD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,wBAAyB,SAAQ,aAAa;IACtD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,MAAM,MAAM,gBAAgB,GAAG,qBAAqB,GAAG,wBAAwB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/patterns/list/types.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport type ListRowVariant = 'divider' | 'card';\n\ninterface ListRowBaseProps extends ZoraBaseProps {\n title: React.ReactNode;\n description?: React.ReactNode;\n meta?: React.ReactNode;\n leading?: React.ReactNode;\n trailing?: React.ReactNode;\n selected?: boolean;\n disabled?: boolean;\n compact?: boolean;\n variant?: ListRowVariant;\n}\n\ninterface ListRowPressableProps {\n onPress: () => void;\n action?: never;\n}\n\ninterface ListRowActionProps {\n action: React.ReactNode;\n onPress?: never;\n}\n\ninterface ListRowStaticProps {\n action?: never;\n onPress?: never;\n}\n\nexport type ListRowProps = ListRowBaseProps &\n (ListRowPressableProps | ListRowActionProps | ListRowStaticProps);\n\nexport interface ListItemsProps extends ZoraBaseProps {\n items: readonly ListRowProps[];\n rowVariant?: ListRowVariant;\n compact?: boolean;\n}\n\nexport interface ListChildrenProps extends ZoraBaseProps {\n children: React.ReactNode;\n}\n\nexport type ListProps = ListItemsProps | ListChildrenProps;\n\
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/patterns/list/types.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport type ListRowVariant = 'divider' | 'card';\n\ninterface ListRowBaseProps extends ZoraBaseProps {\n title: React.ReactNode;\n description?: React.ReactNode;\n meta?: React.ReactNode;\n leading?: React.ReactNode;\n trailing?: React.ReactNode;\n selected?: boolean;\n disabled?: boolean;\n compact?: boolean;\n variant?: ListRowVariant;\n}\n\ninterface ListRowPressableProps {\n onPress: () => void;\n action?: never;\n}\n\ninterface ListRowActionProps {\n action: React.ReactNode;\n onPress?: never;\n}\n\ninterface ListRowStaticProps {\n action?: never;\n onPress?: never;\n}\n\nexport type ListRowProps = ListRowBaseProps &\n (ListRowPressableProps | ListRowActionProps | ListRowStaticProps);\n\nexport interface ListItemsProps extends ZoraBaseProps {\n items: readonly ListRowProps[];\n rowVariant?: ListRowVariant;\n compact?: boolean;\n}\n\nexport interface ListChildrenProps extends ZoraBaseProps {\n children: React.ReactNode;\n}\n\nexport type ListProps = ListItemsProps | ListChildrenProps;\n\ninterface ListSectionItemsProps extends ZoraBaseProps {\n title?: React.ReactNode;\n description?: React.ReactNode;\n eyebrow?: React.ReactNode;\n actions?: React.ReactNode;\n items: readonly ListRowProps[];\n rowVariant?: ListRowVariant;\n compact?: boolean;\n}\n\ninterface ListSectionChildrenProps extends ZoraBaseProps {\n title?: React.ReactNode;\n description?: React.ReactNode;\n eyebrow?: React.ReactNode;\n actions?: React.ReactNode;\n children: React.ReactNode;\n}\n\nexport type ListSectionProps = ListSectionItemsProps | ListSectionChildrenProps;\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
2
3
|
export type ResponsivePanelSide = 'left' | 'right';
|
|
3
4
|
export type ResponsivePanelDesktopMode = 'inline' | 'floating';
|
|
4
5
|
export type ResponsivePanelMobileMode = 'drawer' | 'modal';
|
|
5
|
-
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
6
6
|
export interface ResponsivePanelProps extends ZoraBaseProps {
|
|
7
7
|
title?: React.ReactNode;
|
|
8
8
|
description?: React.ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/patterns/responsive-panel/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,OAAO,CAAC;AACnD,MAAM,MAAM,0BAA0B,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC/D,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3D,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/patterns/responsive-panel/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,OAAO,CAAC;AACnD,MAAM,MAAM,0BAA0B,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC/D,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,WAAW,CAAC,EAAE,0BAA0B,CAAC;IACzC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/patterns/responsive-panel/types.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nexport type ResponsivePanelSide = 'left' | 'right';\nexport type ResponsivePanelDesktopMode = 'inline' | 'floating';\nexport type ResponsivePanelMobileMode = 'drawer' | 'modal';\n\
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/patterns/responsive-panel/types.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport type ResponsivePanelSide = 'left' | 'right';\nexport type ResponsivePanelDesktopMode = 'inline' | 'floating';\nexport type ResponsivePanelMobileMode = 'drawer' | 'modal';\n\nexport interface ResponsivePanelProps extends ZoraBaseProps {\n title?: React.ReactNode;\n description?: React.ReactNode;\n actions?: React.ReactNode;\n footer?: React.ReactNode;\n children?: React.ReactNode;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n side?: ResponsivePanelSide;\n desktopMode?: ResponsivePanelDesktopMode;\n mobileMode?: ResponsivePanelMobileMode;\n compact?: boolean;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/zora",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/zora#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@ankhorage/color-theory": "^0.0.4",
|
|
47
|
-
"@ankhorage/contracts": "^1.
|
|
48
|
-
"@ankhorage/surface": "^1.
|
|
47
|
+
"@ankhorage/contracts": "^1.2.0",
|
|
48
|
+
"@ankhorage/surface": "^1.3.0"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"dist",
|
|
@@ -72,25 +72,24 @@
|
|
|
72
72
|
"build": "rm -rf dist tsconfig.tsbuildinfo && bun x tsc -p tsconfig.json",
|
|
73
73
|
"changeset": "changeset",
|
|
74
74
|
"changeset:status": "changeset status --since=origin/main",
|
|
75
|
-
"knip": "knip",
|
|
76
|
-
"lint": "eslint . --max-warnings=0",
|
|
77
|
-
"lint:fix": "eslint . --fix --max-warnings=0",
|
|
78
|
-
"format": "prettier --write .",
|
|
79
|
-
"format:check": "prettier --check .",
|
|
75
|
+
"knip": "ankhorage-knip",
|
|
76
|
+
"lint": "ankhorage-eslint . --max-warnings=0",
|
|
77
|
+
"lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
|
|
78
|
+
"format": "ankhorage-prettier --write .",
|
|
79
|
+
"format:check": "ankhorage-prettier --check .",
|
|
80
80
|
"prepack": "bun run build",
|
|
81
81
|
"test": "bun test src",
|
|
82
82
|
"typecheck": "bun x tsc --noEmit -p tsconfig.json",
|
|
83
83
|
"version-packages": "changeset version"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@ankhorage/devtools": "^1.0.
|
|
86
|
+
"@ankhorage/devtools": "^1.0.5",
|
|
87
87
|
"@changesets/cli": "^2.31.0",
|
|
88
88
|
"@expo/vector-icons": "^15.1.1",
|
|
89
89
|
"@react-native-picker/picker": "^2.11.4",
|
|
90
90
|
"@types/bun": "^1.3.13",
|
|
91
91
|
"@types/node": "^25.6.0",
|
|
92
92
|
"@types/react": "^19.2.14",
|
|
93
|
-
"knip": "^5.88.1",
|
|
94
93
|
"react": "19.1.0",
|
|
95
94
|
"react-native": "0.81.5",
|
|
96
95
|
"typescript": "^5.9.3"
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { AppBar as SurfaceAppBar } from '@ankhorage/surface';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { Box, Inline, Stack } from '../../foundation';
|
|
5
|
+
import { useZoraTheme } from '../../theme/useZoraTheme';
|
|
6
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
7
|
+
import { Heading } from '../heading';
|
|
8
|
+
import { IconButton } from '../icon-button';
|
|
9
|
+
import { Text } from '../text';
|
|
10
|
+
import type { AppBarMode, AppBarOverflowAction, AppBarProps } from './types';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_CANCEL_ICON = { name: 'close-outline' };
|
|
13
|
+
const DEFAULT_OVERFLOW_ICON = { name: 'ellipsis-vertical' };
|
|
14
|
+
|
|
15
|
+
function resolveMode(mode: AppBarMode | undefined): AppBarMode {
|
|
16
|
+
return mode ?? { type: 'default' };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveSelectionLabel({ count, label }: { count?: number; label: string }): string {
|
|
20
|
+
if (count === undefined) {
|
|
21
|
+
return label;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return `${label} (${count})`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveOverflowLabel(overflow: AppBarOverflowAction): string {
|
|
28
|
+
return overflow.label ?? 'More options';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveCancelLabel(mode: Extract<AppBarMode, { type: 'selection' }>): string {
|
|
32
|
+
return mode.cancelLabel ?? 'Cancel selection';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function AppBarInner({
|
|
36
|
+
themeId: _themeId,
|
|
37
|
+
mode: _mode,
|
|
38
|
+
title,
|
|
39
|
+
subtitle,
|
|
40
|
+
leading,
|
|
41
|
+
actions,
|
|
42
|
+
overflow,
|
|
43
|
+
appMode,
|
|
44
|
+
children,
|
|
45
|
+
safeAreaTop = true,
|
|
46
|
+
divider = true,
|
|
47
|
+
testID,
|
|
48
|
+
}: AppBarProps) {
|
|
49
|
+
const { theme } = useZoraTheme();
|
|
50
|
+
const resolvedMode = resolveMode(appMode);
|
|
51
|
+
const isSelectionMode = resolvedMode.type === 'selection';
|
|
52
|
+
|
|
53
|
+
const resolvedLeading =
|
|
54
|
+
leading ??
|
|
55
|
+
(isSelectionMode ? (
|
|
56
|
+
<IconButton
|
|
57
|
+
icon={resolvedMode.cancelIcon ?? DEFAULT_CANCEL_ICON}
|
|
58
|
+
label={resolveCancelLabel(resolvedMode)}
|
|
59
|
+
emphasis="ghost"
|
|
60
|
+
size="m"
|
|
61
|
+
tone="neutral"
|
|
62
|
+
onPress={resolvedMode.onCancel}
|
|
63
|
+
/>
|
|
64
|
+
) : undefined);
|
|
65
|
+
|
|
66
|
+
const overflowButton = overflow?.onPress ? (
|
|
67
|
+
<IconButton
|
|
68
|
+
disabled={overflow.disabled}
|
|
69
|
+
icon={overflow.icon ?? DEFAULT_OVERFLOW_ICON}
|
|
70
|
+
label={resolveOverflowLabel(overflow)}
|
|
71
|
+
emphasis="ghost"
|
|
72
|
+
size="m"
|
|
73
|
+
tone="neutral"
|
|
74
|
+
onPress={overflow.onPress}
|
|
75
|
+
/>
|
|
76
|
+
) : null;
|
|
77
|
+
|
|
78
|
+
const resolvedTrailing =
|
|
79
|
+
actions || overflowButton ? (
|
|
80
|
+
<Inline align="center" gap="s" wrap="nowrap">
|
|
81
|
+
{actions}
|
|
82
|
+
{overflowButton}
|
|
83
|
+
</Inline>
|
|
84
|
+
) : undefined;
|
|
85
|
+
|
|
86
|
+
const resolvedCenter = (() => {
|
|
87
|
+
if (children !== undefined) {
|
|
88
|
+
return children;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (isSelectionMode) {
|
|
92
|
+
return (
|
|
93
|
+
<Text numberOfLines={1} tone="default" variant="label" weight="semiBold">
|
|
94
|
+
{resolveSelectionLabel(resolvedMode)}
|
|
95
|
+
</Text>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (title == null && subtitle == null) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Stack gap="xs">
|
|
105
|
+
{title != null ? (
|
|
106
|
+
<Heading ellipsizeMode="tail" level={3} numberOfLines={1} size="h5">
|
|
107
|
+
{title}
|
|
108
|
+
</Heading>
|
|
109
|
+
) : null}
|
|
110
|
+
{subtitle != null ? (
|
|
111
|
+
<Text ellipsizeMode="tail" numberOfLines={1} tone="muted" variant="bodySmall">
|
|
112
|
+
{subtitle}
|
|
113
|
+
</Text>
|
|
114
|
+
) : null}
|
|
115
|
+
</Stack>
|
|
116
|
+
);
|
|
117
|
+
})();
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<SurfaceAppBar
|
|
121
|
+
bg={isSelectionMode ? theme.semantics.action.primary.softBg : undefined}
|
|
122
|
+
divider={divider}
|
|
123
|
+
leading={resolvedLeading}
|
|
124
|
+
safeAreaTop={safeAreaTop}
|
|
125
|
+
testID={testID}
|
|
126
|
+
trailing={resolvedTrailing}
|
|
127
|
+
>
|
|
128
|
+
{resolvedCenter ? <Box style={{ minWidth: 0 }}>{resolvedCenter}</Box> : null}
|
|
129
|
+
</SurfaceAppBar>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const AppBar = withZoraThemeScope(AppBarInner);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ButtonIconSpec } from '@ankhorage/surface';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
5
|
+
|
|
6
|
+
export type AppBarMode =
|
|
7
|
+
| {
|
|
8
|
+
type: 'default';
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
type: 'selection';
|
|
12
|
+
label: string;
|
|
13
|
+
count?: number;
|
|
14
|
+
onCancel: () => void;
|
|
15
|
+
cancelLabel?: string;
|
|
16
|
+
cancelIcon?: ButtonIconSpec;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface AppBarOverflowAction {
|
|
20
|
+
onPress: () => void;
|
|
21
|
+
label?: string;
|
|
22
|
+
icon?: ButtonIconSpec;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AppBarProps extends ZoraBaseProps {
|
|
27
|
+
title?: React.ReactNode;
|
|
28
|
+
subtitle?: React.ReactNode;
|
|
29
|
+
leading?: React.ReactNode;
|
|
30
|
+
actions?: React.ReactNode;
|
|
31
|
+
overflow?: AppBarOverflowAction;
|
|
32
|
+
appMode?: AppBarMode;
|
|
33
|
+
children?: React.ReactNode;
|
|
34
|
+
safeAreaTop?: boolean;
|
|
35
|
+
divider?: boolean;
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Image as SurfaceImage } from '@ankhorage/surface';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
5
|
+
import type { ImageProps } from './types';
|
|
6
|
+
|
|
7
|
+
function ImageInner({ themeId: _themeId, mode: _mode, ...props }: ImageProps) {
|
|
8
|
+
return <SurfaceImage {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Image = withZoraThemeScope(ImageInner);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ImageProps as SurfaceImageProps } from '@ankhorage/surface';
|
|
2
|
+
|
|
3
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
4
|
+
|
|
5
|
+
export type { ImageFit, SurfaceImageSource } from '@ankhorage/surface';
|
|
6
|
+
|
|
7
|
+
export interface ImageProps extends ZoraBaseProps, Omit<SurfaceImageProps, 'mode' | 'themeId'> {}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ButtonIconSpec } from '@ankhorage/surface';
|
|
2
2
|
import type React from 'react';
|
|
3
3
|
|
|
4
|
-
export type ToolbarPosition = 'top' | 'bottom' | 'inline';
|
|
5
|
-
|
|
6
4
|
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
7
5
|
|
|
6
|
+
export type ToolbarPosition = 'top' | 'bottom' | 'inline';
|
|
7
|
+
|
|
8
8
|
export interface ToolbarProps extends ZoraBaseProps {
|
|
9
9
|
children?: React.ReactNode;
|
|
10
10
|
position?: ToolbarPosition;
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type { AppBarMode, AppBarOverflowAction, AppBarProps } from './components/app-bar';
|
|
2
|
+
export { AppBar } from './components/app-bar';
|
|
1
3
|
export type { AvatarProps, AvatarShape, AvatarSize } from './components/avatar';
|
|
2
4
|
export { Avatar, resolveAvatarInitials } from './components/avatar';
|
|
3
5
|
export type { AvatarGroupItem, AvatarGroupProps } from './components/avatar-group';
|
|
@@ -58,6 +60,8 @@ export type { IconProps } from './components/icon';
|
|
|
58
60
|
export { Icon } from './components/icon';
|
|
59
61
|
export type { IconButtonProps } from './components/icon-button';
|
|
60
62
|
export { IconButton } from './components/icon-button';
|
|
63
|
+
export type { ImageFit, ImageProps, SurfaceImageSource } from './components/image';
|
|
64
|
+
export { Image } from './components/image';
|
|
61
65
|
export type { InputProps, InputTrailingAction } from './components/input';
|
|
62
66
|
export { Input } from './components/input';
|
|
63
67
|
export type { MediaCardImageProps, MediaCardProps } from './components/media-card';
|
|
@@ -84,13 +88,13 @@ export type { SearchBarProps } from './components/search-bar';
|
|
|
84
88
|
export { SearchBar } from './components/search-bar';
|
|
85
89
|
export type { SelectOption, SelectProps } from './components/select';
|
|
86
90
|
export { Select } from './components/select';
|
|
87
|
-
export type { TabItem, TabsProps } from './components/tabs';
|
|
91
|
+
export type { TabItem, TabsProps, TabsVariant } from './components/tabs';
|
|
88
92
|
export { Tabs } from './components/tabs';
|
|
89
93
|
export type { TextAlign, TextProps, TextTone, TextVariant, TextWeight } from './components/text';
|
|
90
94
|
export { Text } from './components/text';
|
|
91
95
|
export type { TextareaProps } from './components/textarea';
|
|
92
96
|
export { Textarea } from './components/textarea';
|
|
93
|
-
export type { ToolbarActionProps, ToolbarProps } from './components/toolbar';
|
|
97
|
+
export type { ToolbarActionProps, ToolbarPosition, ToolbarProps } from './components/toolbar';
|
|
94
98
|
export { Toolbar, ToolbarAction } from './components/toolbar';
|
|
95
99
|
export type {
|
|
96
100
|
BoxProps,
|
|
@@ -160,6 +164,18 @@ export type { EmptyStateAction, EmptyStateProps } from './patterns/empty-state';
|
|
|
160
164
|
export { EmptyState } from './patterns/empty-state';
|
|
161
165
|
export type { FilterBarProps } from './patterns/filter-bar';
|
|
162
166
|
export { FilterBar } from './patterns/filter-bar';
|
|
167
|
+
export type {
|
|
168
|
+
ImagePreviewProps,
|
|
169
|
+
ZoraImageAsset,
|
|
170
|
+
ZoraImageMetadata,
|
|
171
|
+
} from './patterns/image-preview';
|
|
172
|
+
export { ImagePreview } from './patterns/image-preview';
|
|
173
|
+
export type {
|
|
174
|
+
ImageUploadFieldProps,
|
|
175
|
+
ImageUploadProgressContext,
|
|
176
|
+
ZoraPickedImage,
|
|
177
|
+
} from './patterns/image-upload-field';
|
|
178
|
+
export { ImageUploadField } from './patterns/image-upload-field';
|
|
163
179
|
export type { InspectorFieldProps } from './patterns/inspector-field';
|
|
164
180
|
export { InspectorField } from './patterns/inspector-field';
|
|
165
181
|
export type {
|
|
@@ -175,7 +191,12 @@ export type { NoticeProps } from './patterns/notice';
|
|
|
175
191
|
export { Notice } from './patterns/notice';
|
|
176
192
|
export type { PanelProps } from './patterns/panel';
|
|
177
193
|
export { Panel } from './patterns/panel';
|
|
178
|
-
export type {
|
|
194
|
+
export type {
|
|
195
|
+
ResponsivePanelDesktopMode,
|
|
196
|
+
ResponsivePanelMobileMode,
|
|
197
|
+
ResponsivePanelProps,
|
|
198
|
+
ResponsivePanelSide,
|
|
199
|
+
} from './patterns/responsive-panel';
|
|
179
200
|
export { ResponsivePanel } from './patterns/responsive-panel';
|
|
180
201
|
export type { SectionHeaderProps } from './patterns/section-header';
|
|
181
202
|
export { SectionHeader } from './patterns/section-header';
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
} from '../components/navigation-item';
|
|
7
7
|
import type { ZoraNavigationRouteMap } from '../components/navigation-list';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface ZoraNavigationDescriptorOptions {
|
|
10
10
|
title?: string;
|
|
11
11
|
tabBarLabel?: string | React.ReactNode;
|
|
12
12
|
drawerLabel?: string | React.ReactNode;
|
|
@@ -25,13 +25,13 @@ export interface ZoraNavigationState {
|
|
|
25
25
|
routes: readonly ZoraNavigationRouteState[];
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
interface ZoraTabPressEvent {
|
|
29
29
|
type: 'tabPress';
|
|
30
30
|
target: string;
|
|
31
31
|
canPreventDefault: true;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
interface ZoraTabPressEventResult {
|
|
35
35
|
defaultPrevented: boolean;
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Image } from '../../components/image';
|
|
4
|
+
import { Text } from '../../components/text';
|
|
5
|
+
import { Box, Stack } from '../../foundation';
|
|
6
|
+
import { useZoraTheme } from '../../theme/useZoraTheme';
|
|
7
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
8
|
+
import type { ImagePreviewProps, ZoraImageAsset } from './types';
|
|
9
|
+
|
|
10
|
+
function resolveRenderableUrl(asset: ZoraImageAsset | null | undefined): string | null {
|
|
11
|
+
if (!asset) return null;
|
|
12
|
+
|
|
13
|
+
if (asset.kind === 'url') {
|
|
14
|
+
return asset.url;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return asset.publicUrl ?? null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function resolveSafeAspectRatio(value: number | undefined): number {
|
|
21
|
+
if (!value || !Number.isFinite(value) || value <= 0) {
|
|
22
|
+
return 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function ImagePreviewInner({
|
|
29
|
+
themeId: _themeId,
|
|
30
|
+
mode: _mode,
|
|
31
|
+
testID,
|
|
32
|
+
asset,
|
|
33
|
+
aspectRatio,
|
|
34
|
+
fit = 'cover',
|
|
35
|
+
emptyTitle = 'No image',
|
|
36
|
+
emptyDescription = 'Select an image to preview it here.',
|
|
37
|
+
}: ImagePreviewProps) {
|
|
38
|
+
const { theme } = useZoraTheme();
|
|
39
|
+
const renderableUrl = resolveRenderableUrl(asset);
|
|
40
|
+
const resolvedAspectRatio = resolveSafeAspectRatio(aspectRatio);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Box
|
|
44
|
+
bg={theme.semantics.neutral.surface}
|
|
45
|
+
borderColor={theme.semantics.neutral.divider}
|
|
46
|
+
borderWidth={1}
|
|
47
|
+
radius="l"
|
|
48
|
+
testID={testID}
|
|
49
|
+
style={{ overflow: 'hidden' }}
|
|
50
|
+
>
|
|
51
|
+
{renderableUrl ? (
|
|
52
|
+
<Box style={{ aspectRatio: resolvedAspectRatio, width: '100%' }}>
|
|
53
|
+
<Image
|
|
54
|
+
alt={asset?.alt}
|
|
55
|
+
fit={fit}
|
|
56
|
+
source={renderableUrl}
|
|
57
|
+
style={{ height: '100%', width: '100%' }}
|
|
58
|
+
/>
|
|
59
|
+
</Box>
|
|
60
|
+
) : (
|
|
61
|
+
<Box p="l">
|
|
62
|
+
<Stack gap="xs">
|
|
63
|
+
<Text variant="label" weight="semiBold">
|
|
64
|
+
{emptyTitle}
|
|
65
|
+
</Text>
|
|
66
|
+
<Text tone="muted" variant="bodySmall">
|
|
67
|
+
{emptyDescription}
|
|
68
|
+
</Text>
|
|
69
|
+
</Stack>
|
|
70
|
+
</Box>
|
|
71
|
+
)}
|
|
72
|
+
</Box>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const ImagePreview = withZoraThemeScope(ImagePreviewInner);
|