Skip to content

Commit d8acdbc

Browse files
committed
style updates
1 parent 28926ee commit d8acdbc

File tree

3 files changed

+95
-91
lines changed

3 files changed

+95
-91
lines changed

public/global.css

-63
Original file line numberDiff line numberDiff line change
@@ -1,63 +0,0 @@
1-
html, body {
2-
position: relative;
3-
width: 100%;
4-
height: 100%;
5-
}
6-
7-
body {
8-
color: #333;
9-
margin: 0;
10-
padding: 8px;
11-
box-sizing: border-box;
12-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
13-
}
14-
15-
a {
16-
color: rgb(0,100,200);
17-
text-decoration: none;
18-
}
19-
20-
a:hover {
21-
text-decoration: underline;
22-
}
23-
24-
a:visited {
25-
color: rgb(0,80,160);
26-
}
27-
28-
label {
29-
display: block;
30-
}
31-
32-
input, button, select, textarea {
33-
font-family: inherit;
34-
font-size: inherit;
35-
-webkit-padding: 0.4em 0;
36-
padding: 0.4em;
37-
margin: 0 0 0.5em 0;
38-
box-sizing: border-box;
39-
border: 1px solid #ccc;
40-
border-radius: 2px;
41-
}
42-
43-
input:disabled {
44-
color: #ccc;
45-
}
46-
47-
button {
48-
color: #333;
49-
background-color: #f4f4f4;
50-
outline: none;
51-
}
52-
53-
button:disabled {
54-
color: #999;
55-
}
56-
57-
button:not(:disabled):active {
58-
background-color: #ddd;
59-
}
60-
61-
button:focus {
62-
border-color: #666;
63-
}

src/App.svelte

+71-16
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,76 @@
44
import vCard from "vcf";
55
import cntl from "cntl";
66
import UploadIcon from "./UploadIcon.svelte";
7+
import DownloadIcon from "./DownloadIcon.svelte";
8+
import {fly} from "svelte/transition"
79
810
const fReader = new FileReader();
911
/**@type {vCard[]} */
1012
let contacts = [];
1113
14+
/**
15+
* @typedef UpdatePayload
16+
* @property {Number} index
17+
* @property {string} blob
18+
*/
19+
20+
/**
21+
* @type {Object<string,UpdatePayload>}
22+
*/
23+
let contactsToUpdate = {};
24+
1225
const uploadBtnStyle = cntl`
1326
flex flex-col items-center p-4 bg-white
1427
text-blue rounded-lg shadow-sm tracking-wide
1528
uppercase border border-blue cursor-pointer
1629
hover:bg-blue-400 hover:text-white hover:shadow-lg
1730
transform hover:scale-105 transition duration-200
18-
`;
31+
`;
32+
33+
const addToUpdate = (index, blob) => {
34+
contactsToUpdate[index] = {
35+
index,
36+
blob,
37+
};
38+
};
39+
40+
const removeFromUpdate = (index) => {
41+
contactsToUpdate[index] = null;
42+
delete contactsToUpdate[index];
43+
};
1944
2045
fReader.onload = (e) => {
2146
if (e.target.readyState != 2) return;
22-
if (e.target.error) {
23-
alert("Error Reading file.");
24-
return;
25-
}
2647
try {
27-
contacts = vCard.parse(e.target.result);
48+
contacts = vCard.parse(e.target.result).filter((vc) => {
49+
return !(vc.data.photo && vc.data.photo.valueOf());
50+
});
2851
} catch (error) {
29-
alert("Error reading contact(s).");
52+
alert("Error reading contact(s) from file.");
3053
}
3154
};
3255
</script>
3356

34-
<main class="container text-center mx-auto px-24">
35-
<h1 class="uppercase text-red-500 font-bold text-5xl mt-12 mb-4 underline">
57+
<main class="container text-center mx-auto lg:p-24 p-5 min-h-screen">
58+
<h1 class="uppercase text-red-500 font-bold text-5xl mb-4 underline">
3659
🖼 Image 💪🏾 Contact 📇
3760
</h1>
3861
{#if contacts && contacts.length > 0}
3962
<div>
4063
<p class="text-sm">Start Over?</p>
4164
<button
4265
on:click={() => (contacts = [])}
43-
class="px-3 py-2 rounded shadow-sm m-1 mb-5 transition duration-200 hover:bg-red-400 transform hover:scale-105">Clear
44-
66+
class={cntl`px-3 py-2 rounded
67+
shadow-sm m-1 mb-5 transition
68+
duration-200 hover:bg-red-400
69+
transform hover:scale-105 border-red-900 border`}>
70+
Clear ❌
4571
</button>
4672
</div>
4773
{:else}
48-
<div>
49-
<p class="text-md py-4 lowercase">Click Below to continue</p>
50-
<div class="flex items-center justify-center bg-grey-lighter mb-12">
74+
<div class="mb-12">
75+
<p class="text-md py-4">Click Below to continue</p>
76+
<div class="flex items-center justify-center bg-grey-lighter">
5177
<label class={uploadBtnStyle}>
5278
<UploadIcon />
5379
<span class="mt-2 text-sm leading-normal">Select vcard file</span>
@@ -58,13 +84,42 @@
5884
accept=".vcf" />
5985
</label>
6086
</div>
87+
<p class="block text-sm py-4">
88+
Only contacts without photos will be loaded.
89+
</p>
6190
</div>
6291
{/if}
63-
<div class="grid grid-cols-2 lg:grid-cols-3 gap-12">
92+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12 lg:gap-16 px-8">
6493
{#each contacts as contact, i}
6594
{#if contact.data.fn.valueOf() && true}
66-
<ContactCard name={contact.data.fn.valueOf()} />
95+
<ContactCard
96+
name={contact.data.fn.valueOf()}
97+
on:applyphoto={(e) => addToUpdate(`${i}`, e.detail.data)}
98+
on:unapplyphoto={() => removeFromUpdate(`${i}`)} />
6799
{/if}
68100
{/each}
69101
</div>
102+
{#if contacts && contacts.length > 0}
103+
<button
104+
transition:fly={{y: 100, duration: 1000}}
105+
class={cntl`rounded-full bg-blue-500 p-5
106+
text-white shadow-xl transform transition
107+
hover:scale-110 hover:shadow-2xl fixed
108+
fab focus:outline-none`}
109+
>
110+
<DownloadIcon />
111+
</button>
112+
{/if}
70113
</main>
114+
115+
<style>
116+
.fab {
117+
right: 2rem;
118+
bottom: 2rem;
119+
}
120+
@media(min-width: 768px) {
121+
.fab {
122+
right: 8rem;
123+
}
124+
}
125+
</style>

src/ContactCard.svelte

+24-12
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22
import "a-avataaar";
33
import { blur } from "svelte/transition";
44
import cntl from "cntl";
5-
import { onMount } from "svelte";
5+
import { onMount, createEventDispatcher } from "svelte";
66
import { updateSVgWithStyle } from "./utils";
77
import Canvg from "canvg";
88
import DownloadIcon from "./DownloadIcon.svelte";
99
10-
export const name = "John Smith";
11-
export const contactIndex = null;
10+
const dispatch = createEventDispatcher()
11+
12+
export let name = "John Smith";
13+
1214
let svgFile = "";
1315
let canvasRef;
1416
let svgString;
1517
let svgCtx;
16-
let pngBlob;
1718
let applyImage = false;
1819
1920
onMount(() => {
@@ -33,17 +34,18 @@
3334
shadow bg-transparent hover:bg-blue-400
3435
hover:text-white focus:shadow-outline
3536
focus:outline-none px-4 py-2 rounded
36-
text-sm cursor-pointer no-underline
37+
text-sm cursor-pointer no-underline
38+
border border-gray-700 hover:border-gray-300
3739
hover:no-underline ${applyImage ? `bg-white` : `bg-transparent`}
3840
`;
3941
}
4042
41-
const setImageBlob = (event) => {
42-
applyImage = event.target.checked;
43+
const handleCheck = () => {
4344
if (applyImage) {
4445
Canvg.fromString(svgCtx, svgString).render();
45-
pngBlob = canvasRef.toDataURL("image/png");
46-
console.log("set image blob");
46+
dispatch("applyphoto", {data: canvasRef.toDataURL("image/png").split(';base64,')[1]})
47+
} else {
48+
dispatch("unapplyphoto")
4749
}
4850
};
4951
@@ -60,17 +62,26 @@
6062
.card {
6163
min-width: 160px;
6264
}
65+
.avatar {
66+
--avataaar-width: 80px;
67+
}
68+
69+
@media(min-width: 768px) {
70+
.card {
71+
min-width: 120px;
72+
}
73+
}
6374
</style>
6475

6576
<div
6677
class="transform hover:scale-105 transition duration-200 ease-in"
6778
transition:blur>
6879
<div class="rounded-full">
69-
<a-avataaar identifier={name} on:svgchange={handleSvgChange} />
80+
<a-avataaar identifier={name} on:svgchange={handleSvgChange} class="avatar" />
7081
</div>
7182
<div class={cardStyle}>
7283
<p>{name}</p>
73-
<hr class="border-gray-500" />
84+
<hr class="border-gray-500 my-2" />
7485
<div class="flex mt-5 items-center">
7586
<div class="m-auto">
7687
<a
@@ -91,7 +102,8 @@
91102
class="mr-1 leading-tight"
92103
type="checkbox"
93104
download={`${name}-avatar.svg`}
94-
on:change={setImageBlob} />
105+
bind:checked={applyImage}
106+
on:change={handleCheck} />
95107
<span>{applyImage ? 'Remove' : 'Update'}</span>
96108
</label>
97109
</div>

0 commit comments

Comments
 (0)