Skip to content

Commit dc25751

Browse files
committed
docs: add more advanced playground example
1 parent d8b3f68 commit dc25751

File tree

11 files changed

+361
-55
lines changed

11 files changed

+361
-55
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
- PWA support with [`nuxt-pwa-module`](https://github.com/kevinmarrec/nuxt-pwa-module)
2222
- Works out-of-the-box with [Capacitor](https://capacitorjs.com/) to build mobile apps
2323

24+
**In progress**
25+
26+
- [ ] PWA Elements
27+
2428
## Quick setup
2529

2630
1. Add `nuxt-ionic` dependency to your project
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script setup lang="ts">
2+
defineProps({ name: String })
3+
</script>
4+
5+
<template>
6+
<div id="container">
7+
<strong>{{ name }}</strong>
8+
<p>Explore <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
9+
</div>
10+
</template>
11+
12+
<style scoped>
13+
#container {
14+
text-align: center;
15+
position: absolute;
16+
left: 0;
17+
right: 0;
18+
top: 50%;
19+
transform: translateY(-50%);
20+
}
21+
22+
#container strong {
23+
font-size: 20px;
24+
line-height: 26px;
25+
}
26+
27+
#container p {
28+
font-size: 16px;
29+
line-height: 22px;
30+
color: #8c8c8c;
31+
margin: 0;
32+
}
33+
34+
#container a {
35+
text-decoration: none;
36+
}
37+
</style>
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { Capacitor } from '@capacitor/core'
2+
import {
3+
Camera,
4+
CameraSource,
5+
CameraResultType,
6+
Photo,
7+
} from '@capacitor/camera'
8+
import { Filesystem, Directory } from '@capacitor/filesystem'
9+
import { Storage } from '@capacitor/storage'
10+
11+
export function usePhotoGallery() {
12+
const photos = ref<UserPhoto[]>([])
13+
const PHOTO_STORAGE = 'photos'
14+
15+
const loadSaved = async () => {
16+
const photoList = await Storage.get({ key: PHOTO_STORAGE })
17+
const photosInStorage = photoList.value ? JSON.parse(photoList.value) : []
18+
19+
// If running on the web...
20+
if (!isPlatform('hybrid')) {
21+
for (const photo of photosInStorage) {
22+
const file = await Filesystem.readFile({
23+
path: photo.filepath,
24+
directory: Directory.Data,
25+
})
26+
// Web platform only: Load the photo as base64 data
27+
photo.webviewPath = `data:image/jpeg;base64,${file.data}`
28+
}
29+
}
30+
31+
photos.value = photosInStorage
32+
}
33+
34+
const convertBlobToBase64 = (blob: Blob) =>
35+
new Promise((resolve, reject) => {
36+
const reader = new FileReader()
37+
reader.onerror = reject
38+
reader.onload = () => {
39+
resolve(reader.result)
40+
}
41+
reader.readAsDataURL(blob)
42+
})
43+
44+
const savePicture = async (
45+
photo: Photo,
46+
fileName: string
47+
): Promise<UserPhoto> => {
48+
let base64Data: string
49+
// "hybrid" will detect Cordova or Capacitor;
50+
if (isPlatform('hybrid')) {
51+
const file = await Filesystem.readFile({
52+
// eslint-disable-next-line
53+
path: photo.path!,
54+
})
55+
base64Data = file.data
56+
} else {
57+
// Fetch the photo, read as a blob, then convert to base64 format
58+
// eslint-disable-next-line
59+
const response = await fetch(photo.webPath!)
60+
const blob = await response.blob()
61+
base64Data = (await convertBlobToBase64(blob)) as string
62+
}
63+
const savedFile = await Filesystem.writeFile({
64+
path: fileName,
65+
data: base64Data,
66+
directory: Directory.Data,
67+
})
68+
69+
if (isPlatform('hybrid')) {
70+
// Display the new image by rewriting the 'file://' path to HTTP
71+
// Details: https://ionicframework.com/docs/building/webview#file-protocol
72+
return {
73+
filepath: savedFile.uri,
74+
webviewPath: Capacitor.convertFileSrc(savedFile.uri),
75+
}
76+
} else {
77+
// Use webPath to display the new image instead of base64 since it's
78+
// already loaded into memory
79+
return {
80+
filepath: fileName,
81+
webviewPath: photo.webPath,
82+
}
83+
}
84+
}
85+
86+
const takePhoto = async () => {
87+
const photo = await Camera.getPhoto({
88+
resultType: CameraResultType.Uri,
89+
source: CameraSource.Camera,
90+
quality: 100,
91+
})
92+
const fileName = new Date().getTime() + '.jpeg'
93+
const savedFileImage = await savePicture(photo, fileName)
94+
95+
photos.value = [savedFileImage, ...photos.value]
96+
}
97+
98+
const deletePhoto = async (photo: UserPhoto) => {
99+
// Remove this photo from the Photos reference data array
100+
photos.value = photos.value.filter(p => p.filepath !== photo.filepath)
101+
102+
// delete photo file from filesystem
103+
const filename = photo.filepath.substr(photo.filepath.lastIndexOf('/') + 1)
104+
await Filesystem.deleteFile({
105+
path: filename,
106+
directory: Directory.Data,
107+
})
108+
}
109+
110+
const cachePhotos = () => {
111+
Storage.set({
112+
key: PHOTO_STORAGE,
113+
value: JSON.stringify(photos.value),
114+
})
115+
}
116+
117+
onMounted(loadSaved)
118+
119+
watch(photos, cachePhotos)
120+
121+
return {
122+
photos,
123+
takePhoto,
124+
deletePhoto,
125+
}
126+
}
127+
128+
export interface UserPhoto {
129+
filepath: string
130+
webviewPath?: string
131+
}

playground/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
"@capacitor/ios": "^3.6.0",
1717
"@capacitor/keyboard": "1.2.3",
1818
"@capacitor/status-bar": "1.0.8",
19+
"@capacitor/camera": "^1.0.0",
20+
"@capacitor/filesystem": "^1.0.0",
21+
"@capacitor/storage": "^1.0.0",
1922
"nuxt": "^3.0.0-rc.4",
2023
"nuxt-ionic": "latest"
2124
},

playground/pages/index.vue

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,6 @@
1-
<template>
2-
<ion-page>
3-
<ion-header translucent>
4-
<ion-toolbar class="toolbar">
5-
<ion-thumbnail slot="start">
6-
<ion-img src="/icon.png" />
7-
</ion-thumbnail>
8-
<ion-title> Nuxt Ionic </ion-title>
9-
</ion-toolbar>
10-
</ion-header>
11-
12-
<ion-content :fullscreen="true">
13-
<ion-header collapse="condense">
14-
<ion-toolbar>
15-
<ion-title size="large"> Blank </ion-title>
16-
</ion-toolbar>
17-
</ion-header>
18-
19-
<ion-list>
20-
<ion-item>
21-
<ion-checkbox slot="start" />
22-
<ion-label>
23-
<h1>Create Idea</h1>
24-
<ion-note>Run Idea By Brandy</ion-note>
25-
</ion-label>
26-
<ion-badge slot="end" color="success"> 5 Days </ion-badge>
27-
</ion-item>
28-
</ion-list>
29-
30-
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
31-
<ion-fab-button router-link="/new">
32-
<ion-icon :icon="add"></ion-icon>
33-
</ion-fab-button>
34-
</ion-fab>
35-
</ion-content>
36-
</ion-page>
37-
</template>
38-
391
<script setup>
40-
import { add } from 'ionicons/icons'
41-
useHead({
42-
title: 'Nuxt Ionic',
43-
})
2+
const router = useIonRouter()
3+
await router.push('/tabs/tab1')
444
</script>
5+
6+
<template><ion-page /></template>

playground/pages/new.vue

Lines changed: 0 additions & 13 deletions
This file was deleted.

playground/pages/tabs.vue

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<script setup lang="ts">
2+
import { images, square, triangle } from 'ionicons/icons'
3+
useHead({
4+
title: 'Nuxt Ionic',
5+
})
6+
</script>
7+
8+
<template>
9+
<ion-page>
10+
<ion-content>
11+
<ion-tabs>
12+
<ion-router-outlet />
13+
<ion-tab-bar slot="bottom">
14+
<ion-tab-button tab="tab1" href="/tabs/tab1">
15+
<ion-icon :icon="triangle" />
16+
<ion-label>Tab 1</ion-label>
17+
</ion-tab-button>
18+
19+
<ion-tab-button tab="tab2" href="/tabs/tab2">
20+
<ion-icon :icon="images" />
21+
<ion-label>Photos</ion-label>
22+
</ion-tab-button>
23+
24+
<ion-tab-button tab="tab3" href="/tabs/tab3">
25+
<ion-icon :icon="square" />
26+
<ion-label>Tab 3</ion-label>
27+
</ion-tab-button>
28+
</ion-tab-bar>
29+
</ion-tabs>
30+
</ion-content>
31+
</ion-page>
32+
</template>

playground/pages/tabs/tab1.vue

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script setup lang="ts">
2+
import { add } from 'ionicons/icons'
3+
4+
definePageMeta({
5+
alias: ['/', '/tabs'],
6+
})
7+
</script>
8+
9+
<template>
10+
<ion-page>
11+
<ion-header translucent>
12+
<ion-toolbar class="toolbar">
13+
<ion-thumbnail slot="start">
14+
<ion-img src="/icon.png" />
15+
</ion-thumbnail>
16+
<ion-title> Nuxt Ionic </ion-title>
17+
</ion-toolbar>
18+
</ion-header>
19+
<ion-content :fullscreen="true">
20+
<ion-header collapse="condense">
21+
<ion-toolbar>
22+
<ion-title size="large">Tab 1</ion-title>
23+
</ion-toolbar>
24+
</ion-header>
25+
26+
<ion-list>
27+
<ion-item>
28+
<ion-checkbox slot="start" />
29+
<ion-label>
30+
<h1>Create Idea</h1>
31+
<ion-note>Run Idea By Brandy</ion-note>
32+
</ion-label>
33+
<ion-badge slot="end" color="success"> 5 Days </ion-badge>
34+
</ion-item>
35+
</ion-list>
36+
</ion-content>
37+
</ion-page>
38+
</template>

playground/pages/tabs/tab2.vue

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script setup lang="ts">
2+
import { actionSheetController } from '@ionic/vue'
3+
import { camera, trash, close } from 'ionicons/icons'
4+
const { photos, takePhoto, deletePhoto } = usePhotoGallery()
5+
6+
const showActionSheet = async (photo: UserPhoto) => {
7+
const actionSheet = await actionSheetController.create({
8+
header: 'Photos',
9+
buttons: [
10+
{
11+
text: 'Delete',
12+
role: 'destructive',
13+
icon: trash,
14+
handler: () => {
15+
deletePhoto(photo)
16+
},
17+
},
18+
{
19+
text: 'Cancel',
20+
icon: close,
21+
role: 'cancel',
22+
handler: () => {
23+
// Nothing to do, action sheet is automatically closed
24+
},
25+
},
26+
],
27+
})
28+
await actionSheet.present()
29+
}
30+
</script>
31+
32+
<template>
33+
<ion-page>
34+
<ion-header>
35+
<ion-toolbar>
36+
<ion-title>Photo Gallery</ion-title>
37+
</ion-toolbar>
38+
</ion-header>
39+
<ion-content :fullscreen="true">
40+
<ion-header collapse="condense">
41+
<ion-toolbar>
42+
<ion-title size="large">Photo Gallery</ion-title>
43+
</ion-toolbar>
44+
</ion-header>
45+
<ion-grid>
46+
<ion-row>
47+
<ion-col size="6" :key="photo" v-for="photo in photos">
48+
<ion-img
49+
:src="photo.webviewPath"
50+
@click="showActionSheet(photo)"
51+
></ion-img>
52+
</ion-col>
53+
</ion-row>
54+
</ion-grid>
55+
56+
<ion-fab vertical="bottom" horizontal="center" slot="fixed">
57+
<ion-fab-button @click="takePhoto()">
58+
<ion-icon :icon="camera"></ion-icon>
59+
</ion-fab-button>
60+
</ion-fab>
61+
</ion-content>
62+
</ion-page>
63+
</template>

0 commit comments

Comments
 (0)