Skip to content

Commit 1547741

Browse files
committed
Enhance QR Code Generator with custom logo and color options. Implement LogoUploader and ColorSchemePicker components for user-defined styles. Update deployment workflow to include base path configuration for production builds.
1 parent f7d1e78 commit 1547741

6 files changed

Lines changed: 370 additions & 7 deletions

File tree

.github/workflows/deploy-pages.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- name: Build with Dioxus
3030
run: |
3131
cd qrcode-app
32-
dx bundle -r --package qrcode-app
32+
dx bundle -r --package qrcode-app --base-path qrcode-generator
3333
3434
- name: Upload artifact
3535
uses: actions/upload-pages-artifact@v3

qrcode-app/Dioxus.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ out_dir = "dist"
44

55
[web.app]
66
title = "QR Code Generator"
7-
base_path = "qrcode-generator"
7+
# base_path = "qrcode-generator" # Uncommented for local dev, set via CLI for production
88

99
[web.watcher]
1010
reload_html = true

qrcode-app/assets/tailwind.css

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
'Noto Color Emoji';
88
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
99
monospace;
10+
--color-red-500: oklch(63.7% 0.237 25.331);
11+
--color-red-600: oklch(57.7% 0.245 27.325);
1012
--color-green-50: oklch(98.2% 0.018 155.826);
1113
--color-green-200: oklch(92.5% 0.084 155.995);
1214
--color-green-400: oklch(79.2% 0.209 151.711);
15+
--color-green-600: oklch(62.7% 0.194 149.214);
1316
--color-green-700: oklch(52.7% 0.154 150.069);
1417
--color-green-800: oklch(44.8% 0.119 151.328);
1518
--color-green-900: oklch(39.3% 0.095 152.535);
@@ -46,6 +49,7 @@
4649
--font-weight-bold: 700;
4750
--tracking-wider: 0.05em;
4851
--leading-relaxed: 1.625;
52+
--radius-lg: 0.5rem;
4953
--radius-xl: 0.75rem;
5054
--radius-2xl: 1rem;
5155
--radius-3xl: 1.5rem;
@@ -205,6 +209,9 @@
205209
.pointer-events-none {
206210
pointer-events: none;
207211
}
212+
.visible {
213+
visibility: visible;
214+
}
208215
.absolute {
209216
position: absolute;
210217
}
@@ -244,6 +251,9 @@
244251
max-width: 96rem;
245252
}
246253
}
254+
.mt-2 {
255+
margin-top: calc(var(--spacing) * 2);
256+
}
247257
.mt-10 {
248258
margin-top: calc(var(--spacing) * 10);
249259
}
@@ -253,6 +263,9 @@
253263
.block {
254264
display: block;
255265
}
266+
.contents {
267+
display: contents;
268+
}
256269
.flex {
257270
display: flex;
258271
}
@@ -268,6 +281,9 @@
268281
.h-6 {
269282
height: calc(var(--spacing) * 6);
270283
}
284+
.h-10 {
285+
height: calc(var(--spacing) * 10);
286+
}
271287
.h-80 {
272288
height: calc(var(--spacing) * 80);
273289
}
@@ -283,6 +299,12 @@
283299
.w-6 {
284300
width: calc(var(--spacing) * 6);
285301
}
302+
.w-12 {
303+
width: calc(var(--spacing) * 12);
304+
}
305+
.w-32 {
306+
width: calc(var(--spacing) * 32);
307+
}
286308
.w-80 {
287309
width: calc(var(--spacing) * 80);
288310
}
@@ -304,6 +326,12 @@
304326
.scale-\[1\.02\] {
305327
scale: 1.02;
306328
}
329+
.transform {
330+
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
331+
}
332+
.cursor-pointer {
333+
cursor: pointer;
334+
}
307335
.grid-cols-1 {
308336
grid-template-columns: repeat(1, minmax(0, 1fr));
309337
}
@@ -338,6 +366,13 @@
338366
margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));
339367
}
340368
}
369+
.space-y-4 {
370+
:where(& > :not(:last-child)) {
371+
--tw-space-y-reverse: 0;
372+
margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
373+
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
374+
}
375+
}
341376
.space-y-8 {
342377
:where(& > :not(:last-child)) {
343378
--tw-space-y-reverse: 0;
@@ -360,6 +395,9 @@
360395
.rounded-full {
361396
border-radius: calc(infinity * 1px);
362397
}
398+
.rounded-lg {
399+
border-radius: var(--radius-lg);
400+
}
363401
.rounded-xl {
364402
border-radius: var(--radius-xl);
365403
}
@@ -377,6 +415,9 @@
377415
.border-slate-200 {
378416
border-color: var(--color-slate-200);
379417
}
418+
.border-slate-300 {
419+
border-color: var(--color-slate-300);
420+
}
380421
.border-white\/50 {
381422
border-color: color-mix(in srgb, #fff 50%, transparent);
382423
@supports (color: color-mix(in lab, red, red)) {
@@ -395,6 +436,9 @@
395436
.bg-purple-100 {
396437
background-color: var(--color-purple-100);
397438
}
439+
.bg-red-500 {
440+
background-color: var(--color-red-500);
441+
}
398442
.bg-slate-50\/50 {
399443
background-color: color-mix(in srgb, oklch(98.4% 0.003 247.858) 50%, transparent);
400444
@supports (color: color-mix(in lab, red, red)) {
@@ -478,12 +522,21 @@
478522
.p-8 {
479523
padding: calc(var(--spacing) * 8);
480524
}
525+
.px-3 {
526+
padding-inline: calc(var(--spacing) * 3);
527+
}
481528
.px-4 {
482529
padding-inline: calc(var(--spacing) * 4);
483530
}
484531
.px-6 {
485532
padding-inline: calc(var(--spacing) * 6);
486533
}
534+
.py-1\.5 {
535+
padding-block: calc(var(--spacing) * 1.5);
536+
}
537+
.py-2 {
538+
padding-block: calc(var(--spacing) * 2);
539+
}
487540
.py-3 {
488541
padding-block: calc(var(--spacing) * 3);
489542
}
@@ -546,6 +599,9 @@
546599
.text-\[\#4d3695\] {
547600
color: #4d3695;
548601
}
602+
.text-green-600 {
603+
color: var(--color-green-600);
604+
}
549605
.text-green-700 {
550606
color: var(--color-green-700);
551607
}
@@ -573,6 +629,9 @@
573629
.uppercase {
574630
text-transform: uppercase;
575631
}
632+
.italic {
633+
font-style: italic;
634+
}
576635
.opacity-20 {
577636
opacity: 20%;
578637
}
@@ -691,6 +750,59 @@
691750
color: var(--color-purple-900);
692751
}
693752
}
753+
.file\:mr-4 {
754+
&::file-selector-button {
755+
margin-right: calc(var(--spacing) * 4);
756+
}
757+
}
758+
.file\:cursor-pointer {
759+
&::file-selector-button {
760+
cursor: pointer;
761+
}
762+
}
763+
.file\:rounded-lg {
764+
&::file-selector-button {
765+
border-radius: var(--radius-lg);
766+
}
767+
}
768+
.file\:border-0 {
769+
&::file-selector-button {
770+
border-style: var(--tw-border-style);
771+
border-width: 0px;
772+
}
773+
}
774+
.file\:bg-\[\#4d3695\] {
775+
&::file-selector-button {
776+
background-color: #4d3695;
777+
}
778+
}
779+
.file\:px-4 {
780+
&::file-selector-button {
781+
padding-inline: calc(var(--spacing) * 4);
782+
}
783+
}
784+
.file\:py-2 {
785+
&::file-selector-button {
786+
padding-block: calc(var(--spacing) * 2);
787+
}
788+
}
789+
.file\:text-sm {
790+
&::file-selector-button {
791+
font-size: var(--text-sm);
792+
line-height: var(--tw-leading, var(--text-sm--line-height));
793+
}
794+
}
795+
.file\:font-semibold {
796+
&::file-selector-button {
797+
--tw-font-weight: var(--font-weight-semibold);
798+
font-weight: var(--font-weight-semibold);
799+
}
800+
}
801+
.file\:text-white {
802+
&::file-selector-button {
803+
color: var(--color-white);
804+
}
805+
}
694806
.placeholder\:text-slate-400 {
695807
&::placeholder {
696808
color: var(--color-slate-400);
@@ -710,6 +822,13 @@
710822
}
711823
}
712824
}
825+
.hover\:bg-red-600 {
826+
&:hover {
827+
@media (hover: hover) {
828+
background-color: var(--color-red-600);
829+
}
830+
}
831+
}
713832
.hover\:bg-slate-50 {
714833
&:hover {
715834
@media (hover: hover) {
@@ -731,6 +850,15 @@
731850
}
732851
}
733852
}
853+
.hover\:file\:bg-\[\#3d2875\] {
854+
&:hover {
855+
@media (hover: hover) {
856+
&::file-selector-button {
857+
background-color: #3d2875;
858+
}
859+
}
860+
}
861+
}
734862
.focus\:border-transparent {
735863
&:focus {
736864
border-color: transparent;
@@ -1006,6 +1134,26 @@
10061134
}
10071135
}
10081136
}
1137+
@property --tw-rotate-x {
1138+
syntax: "*";
1139+
inherits: false;
1140+
}
1141+
@property --tw-rotate-y {
1142+
syntax: "*";
1143+
inherits: false;
1144+
}
1145+
@property --tw-rotate-z {
1146+
syntax: "*";
1147+
inherits: false;
1148+
}
1149+
@property --tw-skew-x {
1150+
syntax: "*";
1151+
inherits: false;
1152+
}
1153+
@property --tw-skew-y {
1154+
syntax: "*";
1155+
inherits: false;
1156+
}
10091157
@property --tw-space-y-reverse {
10101158
syntax: "*";
10111159
inherits: false;
@@ -1210,6 +1358,11 @@
12101358
@layer properties {
12111359
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
12121360
*, ::before, ::after, ::backdrop {
1361+
--tw-rotate-x: initial;
1362+
--tw-rotate-y: initial;
1363+
--tw-rotate-z: initial;
1364+
--tw-skew-x: initial;
1365+
--tw-skew-y: initial;
12131366
--tw-space-y-reverse: 0;
12141367
--tw-border-style: solid;
12151368
--tw-gradient-position: initial;

qrcode-app/src/components/home.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use dioxus::prelude::*;
22
use qrcode_lib::fancy::FancyQr;
33
use gloo_timers::future::sleep;
44
use std::time::Duration;
5-
use crate::types::{QrStyle, get_style_options};
6-
use super::{Header, UrlInput, StyleSelector, PreviewPanel, Footer};
5+
use crate::types::{QrStyle, get_custom_style_options};
6+
use super::{Header, UrlInput, StyleSelector, PreviewPanel, Footer, LogoUploader, ColorSchemePicker};
77

88
const LOGO_SVG: &str = include_str!("../../assets/logo-icon.svg");
99

@@ -13,11 +13,21 @@ pub fn Home() -> Element {
1313
let style = use_signal(|| QrStyle::GradientMinimal);
1414
let mut svg_output = use_signal(|| String::new());
1515
let mut copying = use_signal(|| false);
16+
17+
// Custom logo and colors
18+
let custom_logo = use_signal(|| Option::<String>::None);
19+
let background_color = use_signal(|| "#FFFFFF".to_string());
20+
let data_color = use_signal(|| "#4d3695".to_string());
21+
let finder_color = use_signal(|| "#4d3695".to_string());
1622

1723
// Generate QR code when inputs change
1824
use_effect(move || {
1925
let url = content();
2026
let current_style = style();
27+
let logo = custom_logo();
28+
let bg = background_color();
29+
let data = data_color();
30+
let finder = finder_color();
2131

2232
if url.is_empty() {
2333
return;
@@ -28,13 +38,16 @@ pub fn Home() -> Element {
2838
Err(_) => return,
2939
};
3040

31-
let logo_base64 = if !LOGO_SVG.is_empty() {
32-
base64_encode_svg(LOGO_SVG)
41+
// Use custom logo if provided, otherwise use default
42+
let logo_svg = logo.as_deref().unwrap_or(LOGO_SVG);
43+
let logo_base64 = if !logo_svg.is_empty() {
44+
base64_encode_svg(logo_svg)
3345
} else {
3446
String::new()
3547
};
3648

37-
let options = get_style_options(current_style, &logo_base64);
49+
// Use custom colors if provided
50+
let options = get_custom_style_options(current_style, &logo_base64, &bg, &data, &finder);
3851
let svg = qr.render_svg(&options);
3952
svg_output.set(svg);
4053
});
@@ -87,6 +100,12 @@ pub fn Home() -> Element {
87100
Header {}
88101
UrlInput { value: content }
89102
StyleSelector { selected: style }
103+
LogoUploader { custom_logo: custom_logo }
104+
ColorSchemePicker {
105+
background_color: background_color,
106+
data_color: data_color,
107+
finder_color: finder_color
108+
}
90109
}
91110
}
92111

0 commit comments

Comments
 (0)