@a-type/ui 3.0.36 → 3.0.38
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/cjs/components/provider/Provider.d.ts +2 -1
- package/dist/cjs/components/provider/Provider.js +3 -2
- package/dist/cjs/components/provider/Provider.js.map +1 -1
- package/dist/cjs/components/pwaInstall/PwaInstall.d.ts +5 -0
- package/dist/cjs/components/pwaInstall/PwaInstall.js +79 -0
- package/dist/cjs/components/pwaInstall/PwaInstall.js.map +1 -0
- package/dist/cjs/components/pwaInstall/PwaInstallTrigger.d.ts +3 -4
- package/dist/cjs/components/pwaInstall/PwaInstallTrigger.js +5 -48
- package/dist/cjs/components/pwaInstall/PwaInstallTrigger.js.map +1 -1
- package/dist/cjs/components/pwaInstall/index.d.ts +1 -0
- package/dist/cjs/components/pwaInstall/index.js +1 -0
- package/dist/cjs/components/pwaInstall/index.js.map +1 -1
- package/dist/cjs/components/pwaInstall/state.d.ts +4 -0
- package/dist/cjs/components/pwaInstall/state.js +13 -0
- package/dist/cjs/components/pwaInstall/state.js.map +1 -0
- package/dist/cjs/platform.js +5 -4
- package/dist/cjs/platform.js.map +1 -1
- package/dist/esm/components/provider/Provider.d.ts +2 -1
- package/dist/esm/components/provider/Provider.js +3 -2
- package/dist/esm/components/provider/Provider.js.map +1 -1
- package/dist/esm/components/pwaInstall/PwaInstall.d.ts +5 -0
- package/dist/esm/components/pwaInstall/PwaInstall.js +76 -0
- package/dist/esm/components/pwaInstall/PwaInstall.js.map +1 -0
- package/dist/esm/components/pwaInstall/PwaInstallTrigger.d.ts +3 -4
- package/dist/esm/components/pwaInstall/PwaInstallTrigger.js +7 -50
- package/dist/esm/components/pwaInstall/PwaInstallTrigger.js.map +1 -1
- package/dist/esm/components/pwaInstall/index.d.ts +1 -0
- package/dist/esm/components/pwaInstall/index.js +1 -0
- package/dist/esm/components/pwaInstall/index.js.map +1 -1
- package/dist/esm/components/pwaInstall/state.d.ts +4 -0
- package/dist/esm/components/pwaInstall/state.js +9 -0
- package/dist/esm/components/pwaInstall/state.js.map +1 -0
- package/dist/esm/platform.js +5 -4
- package/dist/esm/platform.js.map +1 -1
- package/package.json +1 -1
- package/src/components/provider/Provider.tsx +4 -0
- package/src/components/pwaInstall/PwaInstall.tsx +228 -0
- package/src/components/pwaInstall/PwaInstallTrigger.tsx +15 -209
- package/src/components/pwaInstall/index.ts +1 -0
- package/src/components/pwaInstall/state.ts +9 -0
- package/src/platform.ts +4 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PwaInstallTrigger.js","sourceRoot":"","sources":["../../../../src/components/pwaInstall/PwaInstallTrigger.tsx"],"names":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"PwaInstallTrigger.js","sourceRoot":"","sources":["../../../../src/components/pwaInstall/PwaInstallTrigger.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAIxD,MAAM,UAAU,iBAAiB,CAAC,EAGT;QAHS,EACjC,QAAQ,OAEgB,EADrB,IAAI,cAF0B,YAGjC,CADO;IAEP,MAAM,SAAS,GAAG,cAAc,EAAE,CAAC;IACnC,IAAI,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO,CACN,KAAC,MAAM,kBACN,KAAK,EAAC,SAAS,EACf,QAAQ,EAAC,OAAO,IACZ,IAAI,IACR,OAAO,EAAE,GAAG,EAAE;YACb,iBAAiB,CAAC,IAAI,GAAG,IAAI,CAAC;QAC/B,CAAC,YAEA,QAAQ,aAAR,QAAQ,cAAR,QAAQ,GAAI,CACZ,8BACC,KAAC,IAAI,IAAC,IAAI,EAAC,MAAM,GAAG,gBAClB,CACH,IACO,CACT,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/pwaInstall/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/pwaInstall/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../../src/components/pwaInstall/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;IACtC,IAAI,EAAE,KAAK;CACX,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc;IAC7B,iBAAiB,CAAC,IAAI,GAAG,IAAI,CAAC;AAC/B,CAAC"}
|
package/dist/esm/platform.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// @unocss-include
|
|
2
|
-
export const PRETEND_PWA =
|
|
3
|
-
localStorage.getItem('pretendPWA')
|
|
2
|
+
export const PRETEND_PWA = typeof localStorage !== 'undefined' &&
|
|
3
|
+
localStorage.getItem('pretendPWA') === 'true';
|
|
4
4
|
export const PRETEND_INSTALLABLE = typeof localStorage !== 'undefined' &&
|
|
5
5
|
localStorage.getItem('pretendInstallable') === 'true';
|
|
6
6
|
export const PRETEND_OS = (typeof localStorage !== 'undefined' && localStorage.getItem('pretendOS')) ||
|
|
7
7
|
null;
|
|
8
8
|
export function getIsPWAInstalled() {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
if (typeof window === 'undefined')
|
|
10
|
+
return false;
|
|
11
|
+
return PRETEND_PWA || window.matchMedia('(display-mode: standalone)').matches;
|
|
11
12
|
}
|
|
12
13
|
export function getOS() {
|
|
13
14
|
if (PRETEND_OS) {
|
package/dist/esm/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/platform.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GACvB,
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/platform.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import { useThemedTitleBar, useVisualViewportOffset } from '../../hooks.js';
|
|
|
3
3
|
import { useVirtualKeyboardBehavior } from '../../hooks/useVirtualKeyboardBehavior.js';
|
|
4
4
|
import { IconSpritesheet } from '../icon/index.js';
|
|
5
5
|
import { ParticleLayer } from '../particles/index.js';
|
|
6
|
+
import { PwaInstall } from '../pwaInstall/PwaInstall.js';
|
|
6
7
|
import { Toaster } from '../toasts/toasts.js';
|
|
7
8
|
import { TooltipProvider } from '../tooltip/index.js';
|
|
8
9
|
|
|
@@ -12,6 +13,7 @@ export interface ProviderProps {
|
|
|
12
13
|
disableViewportOffset?: boolean;
|
|
13
14
|
virtualKeyboardBehavior?: 'overlay' | 'displace';
|
|
14
15
|
disableTitleBarColor?: boolean;
|
|
16
|
+
manifestPath?: string;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -23,6 +25,7 @@ export function Provider({
|
|
|
23
25
|
disableViewportOffset,
|
|
24
26
|
virtualKeyboardBehavior = 'displace',
|
|
25
27
|
disableTitleBarColor,
|
|
28
|
+
manifestPath,
|
|
26
29
|
}: ProviderProps) {
|
|
27
30
|
useVisualViewportOffset(disableViewportOffset);
|
|
28
31
|
const supportedVirtualKeyboardBehavior =
|
|
@@ -35,6 +38,7 @@ export function Provider({
|
|
|
35
38
|
<>
|
|
36
39
|
<IconSpritesheet />
|
|
37
40
|
<Toaster />
|
|
41
|
+
<PwaInstall manifestPath={manifestPath} />
|
|
38
42
|
</>
|
|
39
43
|
);
|
|
40
44
|
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { DialogTriggerProps } from '@radix-ui/react-dialog';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useSnapshot } from 'valtio';
|
|
4
|
+
import {
|
|
5
|
+
getIsSafari,
|
|
6
|
+
getOS,
|
|
7
|
+
getSupportsPWAInstallPrompt,
|
|
8
|
+
} from '../../platform.js';
|
|
9
|
+
import { Box } from '../box/Box.js';
|
|
10
|
+
import { Button } from '../button/Button.js';
|
|
11
|
+
import { Dialog } from '../dialog/Dialog.js';
|
|
12
|
+
import { Icon } from '../icon/Icon.js';
|
|
13
|
+
import { Lightbox } from '../lightbox/Lightbox.js';
|
|
14
|
+
import { Ol } from '../lists/lists.js';
|
|
15
|
+
import { P } from '../typography/typography.js';
|
|
16
|
+
import {
|
|
17
|
+
AndroidAddToHomeIcon,
|
|
18
|
+
SafariAddToDockIcon,
|
|
19
|
+
SafariIcon,
|
|
20
|
+
SafariPlusSquareIcon,
|
|
21
|
+
SafariShareIcon,
|
|
22
|
+
} from './PlatformIcons.js';
|
|
23
|
+
import { pwaInstallerState } from './state.js';
|
|
24
|
+
import {
|
|
25
|
+
triggerDeferredInstall,
|
|
26
|
+
useIsInstalled,
|
|
27
|
+
useIsInstallReady,
|
|
28
|
+
} from './useIsInstallReady.js';
|
|
29
|
+
import { useWebManifest } from './useWebManifest.js';
|
|
30
|
+
|
|
31
|
+
export interface PwaInstall extends DialogTriggerProps {
|
|
32
|
+
manifestPath?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function PwaInstall({
|
|
36
|
+
children,
|
|
37
|
+
manifestPath,
|
|
38
|
+
asChild: _,
|
|
39
|
+
...rest
|
|
40
|
+
}: PwaInstall) {
|
|
41
|
+
const installed = useIsInstalled();
|
|
42
|
+
const manifest = useWebManifest(manifestPath);
|
|
43
|
+
const { open } = useSnapshot(pwaInstallerState);
|
|
44
|
+
|
|
45
|
+
const [showInstructions, setShowInstructions] = useState(false);
|
|
46
|
+
|
|
47
|
+
if (installed) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const primaryIcon = manifest?.icons?.[0];
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Dialog
|
|
55
|
+
open={open}
|
|
56
|
+
onOpenChange={(isOpen) => {
|
|
57
|
+
pwaInstallerState.open = isOpen;
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<Dialog.Content className="flex flex-col gap-xs" id="pwa-install-dialog">
|
|
61
|
+
<Dialog.Title className="flex flex-row gap-md items-center">
|
|
62
|
+
{primaryIcon && (
|
|
63
|
+
<img
|
|
64
|
+
src={primaryIcon.src}
|
|
65
|
+
alt={
|
|
66
|
+
primaryIcon.label ??
|
|
67
|
+
manifest?.short_name ??
|
|
68
|
+
manifest?.name ??
|
|
69
|
+
'App Icon'
|
|
70
|
+
}
|
|
71
|
+
className="inline-block w-1em h-1em rounded"
|
|
72
|
+
/>
|
|
73
|
+
)}
|
|
74
|
+
Install {manifest?.short_name ?? manifest?.name ?? 'App'}
|
|
75
|
+
</Dialog.Title>
|
|
76
|
+
{showInstructions ? (
|
|
77
|
+
<>
|
|
78
|
+
<Dialog.Description>
|
|
79
|
+
Follow the instructions below to install this app on your device.
|
|
80
|
+
</Dialog.Description>
|
|
81
|
+
<InstallInstructions />
|
|
82
|
+
<Dialog.Actions>
|
|
83
|
+
<Dialog.Close asChild>
|
|
84
|
+
<Button emphasis="ghost">Close</Button>
|
|
85
|
+
</Dialog.Close>
|
|
86
|
+
<Button onClick={() => setShowInstructions(false)}>
|
|
87
|
+
<Icon name="arrowLeft" /> Back
|
|
88
|
+
</Button>
|
|
89
|
+
</Dialog.Actions>
|
|
90
|
+
</>
|
|
91
|
+
) : (
|
|
92
|
+
<>
|
|
93
|
+
<Dialog.Description>
|
|
94
|
+
This site is also an app. You can install it right now for easier
|
|
95
|
+
access and more features.
|
|
96
|
+
</Dialog.Description>
|
|
97
|
+
{manifest?.description && (
|
|
98
|
+
<P className="mb-sm">{manifest.description}</P>
|
|
99
|
+
)}
|
|
100
|
+
<ManifestImageGallery manifestPath={manifestPath} />
|
|
101
|
+
<Dialog.Actions>
|
|
102
|
+
<Dialog.Close asChild>
|
|
103
|
+
<Button emphasis="ghost">Close</Button>
|
|
104
|
+
</Dialog.Close>
|
|
105
|
+
<InstallDeviceActions
|
|
106
|
+
showInstructions={() => setShowInstructions(true)}
|
|
107
|
+
/>
|
|
108
|
+
</Dialog.Actions>
|
|
109
|
+
</>
|
|
110
|
+
)}
|
|
111
|
+
</Dialog.Content>
|
|
112
|
+
</Dialog>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const supportsDirectInstall = getSupportsPWAInstallPrompt();
|
|
117
|
+
|
|
118
|
+
function InstallInstructions() {
|
|
119
|
+
const os = getOS();
|
|
120
|
+
if (os === 'iOS' || os === 'Mac OS') {
|
|
121
|
+
if (getIsSafari()) {
|
|
122
|
+
if (os === 'iOS') {
|
|
123
|
+
return (
|
|
124
|
+
<Ol>
|
|
125
|
+
<Ol.Item>
|
|
126
|
+
<SafariShareIcon /> Tap the Share button in the toolbar.
|
|
127
|
+
</Ol.Item>
|
|
128
|
+
<Ol.Item>
|
|
129
|
+
<SafariPlusSquareIcon /> Scroll down and tap "Add to Home Screen".
|
|
130
|
+
</Ol.Item>
|
|
131
|
+
</Ol>
|
|
132
|
+
);
|
|
133
|
+
} else {
|
|
134
|
+
return (
|
|
135
|
+
<Ol>
|
|
136
|
+
<Ol.Item>
|
|
137
|
+
<SafariShareIcon /> Tap the Share button in the toolbar.
|
|
138
|
+
</Ol.Item>
|
|
139
|
+
<Ol.Item>
|
|
140
|
+
<SafariAddToDockIcon /> Tap "Add to Dock".
|
|
141
|
+
</Ol.Item>
|
|
142
|
+
</Ol>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
return (
|
|
147
|
+
<>
|
|
148
|
+
<Ol>
|
|
149
|
+
<Ol.Item>
|
|
150
|
+
<SafariIcon /> Open this site in Safari to continue.
|
|
151
|
+
</Ol.Item>
|
|
152
|
+
</Ol>
|
|
153
|
+
<Box surface p>
|
|
154
|
+
Apple does not allow non-Safari browsers to install web apps.
|
|
155
|
+
</Box>
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<Ol>
|
|
163
|
+
<Ol.Item>
|
|
164
|
+
<Icon name="dots" className="rotate-90" /> Tap the menu button in the
|
|
165
|
+
toolbar.
|
|
166
|
+
</Ol.Item>
|
|
167
|
+
<Ol.Item>
|
|
168
|
+
<AndroidAddToHomeIcon /> Tap "Add to Home Screen."
|
|
169
|
+
</Ol.Item>
|
|
170
|
+
</Ol>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function ManifestImageGallery({ manifestPath }: { manifestPath?: string }) {
|
|
175
|
+
const manifest = useWebManifest(manifestPath);
|
|
176
|
+
|
|
177
|
+
if (!manifest?.screenshots?.length) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<Box overflow="auto-x" p="sm" gap className="h-240px">
|
|
183
|
+
{manifest.screenshots?.map((screenshot, index) => (
|
|
184
|
+
<Lightbox.Root key={screenshot.src}>
|
|
185
|
+
<Lightbox.Trigger asChild>
|
|
186
|
+
<Lightbox.Image
|
|
187
|
+
tabIndex={0}
|
|
188
|
+
key={index}
|
|
189
|
+
src={screenshot.src}
|
|
190
|
+
alt={screenshot.label || `Screenshot ${index + 1}`}
|
|
191
|
+
className="border border-default rounded-xs"
|
|
192
|
+
/>
|
|
193
|
+
</Lightbox.Trigger>
|
|
194
|
+
<Lightbox.Portal>
|
|
195
|
+
<Lightbox.Overlay className="z-10000" />
|
|
196
|
+
<Lightbox.Content className="z-10001">
|
|
197
|
+
<Lightbox.Image
|
|
198
|
+
src={screenshot.src}
|
|
199
|
+
alt={screenshot.label || `Screenshot ${index + 1}`}
|
|
200
|
+
/>
|
|
201
|
+
</Lightbox.Content>
|
|
202
|
+
</Lightbox.Portal>
|
|
203
|
+
</Lightbox.Root>
|
|
204
|
+
))}
|
|
205
|
+
</Box>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function InstallDeviceActions({
|
|
210
|
+
showInstructions,
|
|
211
|
+
}: {
|
|
212
|
+
showInstructions: () => void;
|
|
213
|
+
}) {
|
|
214
|
+
const ready = useIsInstallReady();
|
|
215
|
+
if (supportsDirectInstall && ready) {
|
|
216
|
+
return (
|
|
217
|
+
<Button onClick={() => triggerDeferredInstall()} emphasis="primary">
|
|
218
|
+
<Icon name="download" /> Install now
|
|
219
|
+
</Button>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<Button onClick={() => showInstructions()} emphasis="primary">
|
|
225
|
+
<Icon name="arrowRight" /> Add to Home
|
|
226
|
+
</Button>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
@@ -1,227 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
getIsSafari,
|
|
5
|
-
getOS,
|
|
6
|
-
getSupportsPWAInstallPrompt,
|
|
7
|
-
} from '../../platform.js';
|
|
8
|
-
import { Box } from '../box/Box.js';
|
|
9
|
-
import { Button } from '../button/Button.js';
|
|
10
|
-
import { Dialog } from '../dialog/Dialog.js';
|
|
1
|
+
import { Button, ButtonProps } from '../button/Button.js';
|
|
11
2
|
import { Icon } from '../icon/Icon.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { P } from '../typography/typography.js';
|
|
15
|
-
import {
|
|
16
|
-
AndroidAddToHomeIcon,
|
|
17
|
-
SafariAddToDockIcon,
|
|
18
|
-
SafariIcon,
|
|
19
|
-
SafariPlusSquareIcon,
|
|
20
|
-
SafariShareIcon,
|
|
21
|
-
} from './PlatformIcons.js';
|
|
22
|
-
import {
|
|
23
|
-
triggerDeferredInstall,
|
|
24
|
-
useIsInstalled,
|
|
25
|
-
useIsInstallReady,
|
|
26
|
-
} from './useIsInstallReady.js';
|
|
27
|
-
import { useWebManifest } from './useWebManifest.js';
|
|
3
|
+
import { pwaInstallerState } from './state.js';
|
|
4
|
+
import { useIsInstalled } from './useIsInstallReady.js';
|
|
28
5
|
|
|
29
|
-
export interface PwaInstallTriggerProps extends
|
|
30
|
-
manifestPath?: string;
|
|
31
|
-
}
|
|
6
|
+
export interface PwaInstallTriggerProps extends ButtonProps {}
|
|
32
7
|
|
|
33
8
|
export function PwaInstallTrigger({
|
|
34
9
|
children,
|
|
35
|
-
manifestPath,
|
|
36
|
-
asChild: _,
|
|
37
10
|
...rest
|
|
38
11
|
}: PwaInstallTriggerProps) {
|
|
39
12
|
const installed = useIsInstalled();
|
|
40
|
-
const manifest = useWebManifest(manifestPath);
|
|
41
|
-
|
|
42
|
-
const [showInstructions, setShowInstructions] = useState(false);
|
|
43
|
-
|
|
44
13
|
if (installed) {
|
|
45
14
|
return null;
|
|
46
15
|
}
|
|
47
16
|
|
|
48
|
-
const primaryIcon = manifest?.icons?.[0];
|
|
49
|
-
|
|
50
17
|
return (
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<Dialog.Title className="flex flex-row gap-md items-center">
|
|
61
|
-
{primaryIcon && (
|
|
62
|
-
<img
|
|
63
|
-
src={primaryIcon.src}
|
|
64
|
-
alt={
|
|
65
|
-
primaryIcon.label ??
|
|
66
|
-
manifest?.short_name ??
|
|
67
|
-
manifest?.name ??
|
|
68
|
-
'App Icon'
|
|
69
|
-
}
|
|
70
|
-
className="inline-block w-1em h-1em rounded"
|
|
71
|
-
/>
|
|
72
|
-
)}
|
|
73
|
-
Install {manifest?.short_name ?? manifest?.name ?? 'App'}
|
|
74
|
-
</Dialog.Title>
|
|
75
|
-
{showInstructions ? (
|
|
76
|
-
<>
|
|
77
|
-
<Dialog.Description>
|
|
78
|
-
Follow the instructions below to install this app on your device.
|
|
79
|
-
</Dialog.Description>
|
|
80
|
-
<InstallInstructions />
|
|
81
|
-
<Dialog.Actions>
|
|
82
|
-
<Dialog.Close asChild>
|
|
83
|
-
<Button emphasis="ghost">Close</Button>
|
|
84
|
-
</Dialog.Close>
|
|
85
|
-
<Button onClick={() => setShowInstructions(false)}>
|
|
86
|
-
<Icon name="arrowLeft" /> Back
|
|
87
|
-
</Button>
|
|
88
|
-
</Dialog.Actions>
|
|
89
|
-
</>
|
|
90
|
-
) : (
|
|
91
|
-
<>
|
|
92
|
-
<Dialog.Description>
|
|
93
|
-
This site is also an app. You can install it right now for easier
|
|
94
|
-
access and more features.
|
|
95
|
-
</Dialog.Description>
|
|
96
|
-
{manifest?.description && (
|
|
97
|
-
<P className="mb-sm">{manifest.description}</P>
|
|
98
|
-
)}
|
|
99
|
-
<ManifestImageGallery manifestPath={manifestPath} />
|
|
100
|
-
<Dialog.Actions>
|
|
101
|
-
<Dialog.Close asChild>
|
|
102
|
-
<Button emphasis="ghost">Close</Button>
|
|
103
|
-
</Dialog.Close>
|
|
104
|
-
<InstallDeviceActions
|
|
105
|
-
showInstructions={() => setShowInstructions(true)}
|
|
106
|
-
/>
|
|
107
|
-
</Dialog.Actions>
|
|
108
|
-
</>
|
|
109
|
-
)}
|
|
110
|
-
</Dialog.Content>
|
|
111
|
-
</Dialog>
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const supportsDirectInstall = getSupportsPWAInstallPrompt();
|
|
116
|
-
|
|
117
|
-
function InstallInstructions() {
|
|
118
|
-
const os = getOS();
|
|
119
|
-
if (os === 'iOS' || os === 'Mac OS') {
|
|
120
|
-
if (getIsSafari()) {
|
|
121
|
-
if (os === 'iOS') {
|
|
122
|
-
return (
|
|
123
|
-
<Ol>
|
|
124
|
-
<Ol.Item>
|
|
125
|
-
<SafariShareIcon /> Tap the Share button in the toolbar.
|
|
126
|
-
</Ol.Item>
|
|
127
|
-
<Ol.Item>
|
|
128
|
-
<SafariPlusSquareIcon /> Scroll down and tap "Add to Home Screen".
|
|
129
|
-
</Ol.Item>
|
|
130
|
-
</Ol>
|
|
131
|
-
);
|
|
132
|
-
} else {
|
|
133
|
-
return (
|
|
134
|
-
<Ol>
|
|
135
|
-
<Ol.Item>
|
|
136
|
-
<SafariShareIcon /> Tap the Share button in the toolbar.
|
|
137
|
-
</Ol.Item>
|
|
138
|
-
<Ol.Item>
|
|
139
|
-
<SafariAddToDockIcon /> Tap "Add to Dock".
|
|
140
|
-
</Ol.Item>
|
|
141
|
-
</Ol>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
} else {
|
|
145
|
-
return (
|
|
18
|
+
<Button
|
|
19
|
+
color="primary"
|
|
20
|
+
emphasis="light"
|
|
21
|
+
{...rest}
|
|
22
|
+
onClick={() => {
|
|
23
|
+
pwaInstallerState.open = true;
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
{children ?? (
|
|
146
27
|
<>
|
|
147
|
-
<
|
|
148
|
-
<Ol.Item>
|
|
149
|
-
<SafariIcon /> Open this site in Safari to continue.
|
|
150
|
-
</Ol.Item>
|
|
151
|
-
</Ol>
|
|
152
|
-
<Box surface p>
|
|
153
|
-
Apple does not allow non-Safari browsers to install web apps.
|
|
154
|
-
</Box>
|
|
28
|
+
<Icon name="star" /> Install
|
|
155
29
|
</>
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return (
|
|
161
|
-
<Ol>
|
|
162
|
-
<Ol.Item>
|
|
163
|
-
<Icon name="dots" className="rotate-90" /> Tap the menu button in the
|
|
164
|
-
toolbar.
|
|
165
|
-
</Ol.Item>
|
|
166
|
-
<Ol.Item>
|
|
167
|
-
<AndroidAddToHomeIcon /> Tap "Add to Home Screen."
|
|
168
|
-
</Ol.Item>
|
|
169
|
-
</Ol>
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function ManifestImageGallery({ manifestPath }: { manifestPath?: string }) {
|
|
174
|
-
const manifest = useWebManifest(manifestPath);
|
|
175
|
-
|
|
176
|
-
if (!manifest?.screenshots?.length) {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return (
|
|
181
|
-
<Box overflow="auto-x" p="sm" gap className="h-240px">
|
|
182
|
-
{manifest.screenshots?.map((screenshot, index) => (
|
|
183
|
-
<Lightbox.Root key={screenshot.src}>
|
|
184
|
-
<Lightbox.Trigger asChild>
|
|
185
|
-
<Lightbox.Image
|
|
186
|
-
tabIndex={0}
|
|
187
|
-
key={index}
|
|
188
|
-
src={screenshot.src}
|
|
189
|
-
alt={screenshot.label || `Screenshot ${index + 1}`}
|
|
190
|
-
className="border border-default rounded-xs"
|
|
191
|
-
/>
|
|
192
|
-
</Lightbox.Trigger>
|
|
193
|
-
<Lightbox.Portal>
|
|
194
|
-
<Lightbox.Overlay className="z-10000" />
|
|
195
|
-
<Lightbox.Content className="z-10001">
|
|
196
|
-
<Lightbox.Image
|
|
197
|
-
src={screenshot.src}
|
|
198
|
-
alt={screenshot.label || `Screenshot ${index + 1}`}
|
|
199
|
-
/>
|
|
200
|
-
</Lightbox.Content>
|
|
201
|
-
</Lightbox.Portal>
|
|
202
|
-
</Lightbox.Root>
|
|
203
|
-
))}
|
|
204
|
-
</Box>
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function InstallDeviceActions({
|
|
209
|
-
showInstructions,
|
|
210
|
-
}: {
|
|
211
|
-
showInstructions: () => void;
|
|
212
|
-
}) {
|
|
213
|
-
const ready = useIsInstallReady();
|
|
214
|
-
if (supportsDirectInstall && ready) {
|
|
215
|
-
return (
|
|
216
|
-
<Button onClick={() => triggerDeferredInstall()} emphasis="primary">
|
|
217
|
-
<Icon name="download" /> Install now
|
|
218
|
-
</Button>
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<Button onClick={() => showInstructions()} emphasis="primary">
|
|
224
|
-
<Icon name="arrowRight" /> Add to Home
|
|
30
|
+
)}
|
|
225
31
|
</Button>
|
|
226
32
|
);
|
|
227
33
|
}
|
package/src/platform.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const PRETEND_PWA =
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
typeof localStorage !== 'undefined' &&
|
|
3
|
+
localStorage.getItem('pretendPWA') === 'true';
|
|
4
4
|
|
|
5
5
|
export const PRETEND_INSTALLABLE =
|
|
6
6
|
typeof localStorage !== 'undefined' &&
|
|
@@ -11,10 +11,8 @@ export const PRETEND_OS =
|
|
|
11
11
|
null;
|
|
12
12
|
|
|
13
13
|
export function getIsPWAInstalled() {
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
window.matchMedia('(display-mode: standalone)').matches
|
|
17
|
-
);
|
|
14
|
+
if (typeof window === 'undefined') return false;
|
|
15
|
+
return PRETEND_PWA || window.matchMedia('(display-mode: standalone)').matches;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
export function getOS() {
|