Compare commits

..

15 Commits

Author SHA1 Message Date
3004dd4ade UI overhaul rebase 2026-01-28 20:42:47 +01:00
6e78d1c181 temp 2026-01-28 19:52:47 +01:00
dea49ffd39 feat: UI overhaul
All checks were successful
actionlint check / actionlint check (pull_request) Successful in 9s
conventional commit messages check / conventional commit messages check (pull_request) Successful in 10s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 6s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 20s
GitLeaks check / GitLeaks check (pull_request) Successful in 15s
checkov check / checkov check (pull_request) Successful in 1m11s
hadolint check / hadolint check (pull_request) Successful in 15s
htmlhint check / htmlhint check (pull_request) Successful in 27s
markdownlint check / markdownlint check (pull_request) Successful in 25s
Prettier check / Prettier check (pull_request) Successful in 26s
Stylelint check / Stylelint check (pull_request) Successful in 19s
ShellCheck check / ShellCheck check (pull_request) Successful in 40m25s
yamllint check / yamllint check (pull_request) Successful in 40m9s
Rust check / Rust check (pull_request) Successful in 1h0m51s
2026-01-28 19:46:53 +01:00
be1a21b746 fix: text wrapping (#128) 2026-01-25 10:21:12 +00:00
fa749b652f fix: text wrapping
Some checks failed
actionlint check / actionlint check (pull_request) Successful in 7s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 3s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 6s
GitLeaks check / GitLeaks check (pull_request) Successful in 9s
conventional commit messages check / conventional commit messages check (pull_request) Failing after 5s
hadolint check / hadolint check (pull_request) Successful in 19s
markdownlint check / markdownlint check (pull_request) Successful in 37s
htmlhint check / htmlhint check (pull_request) Successful in 43s
Prettier check / Prettier check (pull_request) Successful in 40s
ShellCheck check / ShellCheck check (pull_request) Successful in 48s
Stylelint check / Stylelint check (pull_request) Successful in 37s
checkov check / checkov check (pull_request) Successful in 1m46s
yamllint check / yamllint check (pull_request) Successful in 31s
Rust check / Rust check (pull_request) Successful in 16m41s
2026-01-25 10:35:54 +01:00
d9efaaae6e fix: ability to access the someday maybe category list (#127) 2026-01-25 09:27:23 +00:00
428e52ba3d fix: ability to access the someday maybe category list
Some checks failed
actionlint check / actionlint check (pull_request) Successful in 7s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 4s
conventional commit messages check / conventional commit messages check (pull_request) Failing after 6s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 7s
GitLeaks check / GitLeaks check (pull_request) Successful in 15s
hadolint check / hadolint check (pull_request) Successful in 15s
htmlhint check / htmlhint check (pull_request) Successful in 36s
Prettier check / Prettier check (pull_request) Successful in 54s
checkov check / checkov check (pull_request) Successful in 1m20s
markdownlint check / markdownlint check (pull_request) Successful in 57s
ShellCheck check / ShellCheck check (pull_request) Successful in 28s
Stylelint check / Stylelint check (pull_request) Successful in 27s
yamllint check / yamllint check (pull_request) Successful in 30s
Rust check / Rust check (pull_request) Successful in 23m59s
2026-01-25 09:41:46 +01:00
8dbb1dc48e feat: make the cursor of subtask buttons pointer (#124) 2026-01-24 20:53:48 +00:00
ee950aa4a9 feat: make the cursor of subtask buttons pointer
All checks were successful
actionlint check / actionlint check (pull_request) Successful in 7s
conventional commit messages check / conventional commit messages check (pull_request) Successful in 6s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 3s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 8s
GitLeaks check / GitLeaks check (pull_request) Successful in 12s
hadolint check / hadolint check (pull_request) Successful in 41s
htmlhint check / htmlhint check (pull_request) Successful in 1m1s
checkov check / checkov check (pull_request) Successful in 1m18s
markdownlint check / markdownlint check (pull_request) Successful in 59s
Prettier check / Prettier check (pull_request) Successful in 27s
ShellCheck check / ShellCheck check (pull_request) Successful in 34s
Stylelint check / Stylelint check (pull_request) Successful in 32s
yamllint check / yamllint check (pull_request) Successful in 34s
Rust check / Rust check (pull_request) Successful in 16m14s
2026-01-24 21:34:30 +01:00
439cc012f0 chore: make the Rust builds locked (#123) 2026-01-24 20:08:34 +00:00
803fe36ed1 feat: update Font Awesome to 7.1.0 (#122) 2026-01-24 20:08:26 +00:00
80918b98d9 chore: make the Rust builds locked
All checks were successful
actionlint check / actionlint check (pull_request) Successful in 7s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 4s
conventional commit messages check / conventional commit messages check (pull_request) Successful in 7s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 10s
GitLeaks check / GitLeaks check (pull_request) Successful in 13s
hadolint check / hadolint check (pull_request) Successful in 39s
checkov check / checkov check (pull_request) Successful in 1m36s
htmlhint check / htmlhint check (pull_request) Successful in 1m19s
markdownlint check / markdownlint check (pull_request) Successful in 1m14s
Prettier check / Prettier check (pull_request) Successful in 50s
ShellCheck check / ShellCheck check (pull_request) Successful in 2m1s
Stylelint check / Stylelint check (pull_request) Successful in 2m4s
yamllint check / yamllint check (pull_request) Successful in 2m20s
Rust check / Rust check (pull_request) Successful in 21m31s
2026-01-24 20:00:16 +01:00
d4235ef2ab feat: update Font Awesome to 7.1.0
All checks were successful
actionlint check / actionlint check (pull_request) Successful in 39s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 8s
conventional commit messages check / conventional commit messages check (pull_request) Successful in 10s
GitLeaks check / GitLeaks check (pull_request) Successful in 46s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 48s
hadolint check / hadolint check (pull_request) Successful in 51s
checkov check / checkov check (pull_request) Successful in 2m37s
htmlhint check / htmlhint check (pull_request) Successful in 1m9s
markdownlint check / markdownlint check (pull_request) Successful in 50s
Prettier check / Prettier check (pull_request) Successful in 30s
ShellCheck check / ShellCheck check (pull_request) Successful in 55s
Stylelint check / Stylelint check (pull_request) Successful in 54s
yamllint check / yamllint check (pull_request) Successful in 1m6s
Rust check / Rust check (pull_request) Successful in 24m52s
2026-01-24 17:52:43 +01:00
d0a124ee75 fix: font displaying (#120) 2026-01-24 11:30:45 +00:00
67a8a3fae6 fix: font displaying
All checks were successful
actionlint check / actionlint check (pull_request) Successful in 6s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 6s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 3s
conventional commit messages check / conventional commit messages check (pull_request) Successful in 6s
GitLeaks check / GitLeaks check (pull_request) Successful in 13s
hadolint check / hadolint check (pull_request) Successful in 33s
htmlhint check / htmlhint check (pull_request) Successful in 40s
checkov check / checkov check (pull_request) Successful in 1m18s
markdownlint check / markdownlint check (pull_request) Successful in 57s
Prettier check / Prettier check (pull_request) Successful in 34s
ShellCheck check / ShellCheck check (pull_request) Successful in 32s
Stylelint check / Stylelint check (pull_request) Successful in 30s
yamllint check / yamllint check (pull_request) Successful in 34s
Rust check / Rust check (pull_request) Successful in 11m1s
2026-01-24 12:16:16 +01:00
100 changed files with 324232 additions and 991 deletions

View File

@@ -40,6 +40,6 @@ jobs:
- name: rustfmt check
run: cargo fmt --all --check
- name: Clippy check
run: cargo clippy --all-targets --all-features -- --deny warnings
run: cargo clippy --locked --all-targets --all-features -- --deny warnings
- name: test check
run: cargo test --all --all-targets --all-features
run: cargo --locked test --all --all-targets --all-features

8
Cargo.lock generated
View File

@@ -1285,6 +1285,13 @@ dependencies = [
"tracing",
]
[[package]]
name = "dioxus-free-icons"
version = "0.10.0"
dependencies = [
"dioxus",
]
[[package]]
name = "dioxus-fullstack"
version = "0.7.3"
@@ -5398,6 +5405,7 @@ dependencies = [
"diesel",
"diesel_migrations",
"dioxus",
"dioxus-free-icons",
"dioxus-html",
"dioxus-i18n",
"feruca",

View File

@@ -38,6 +38,11 @@ async-std = { version = "1.13.2", optional = true }
dioxus-i18n = { path = "dioxus-i18n" }
voca_rs = "1.15.2"
load-dotenv = "0.1.2"
# TODO: Switch to upstream once it merges the changes.
dioxus-free-icons = { path = "dioxus-free-icons/packages/lib", features = [
"font-awesome-regular",
"font-awesome-solid",
] }
[features]
default = ["web"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,203 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
sodipodi:docname="icon.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
inkscape:zoom="1.1020922"
inkscape:cx="188.27826"
inkscape:cy="204.15715"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="display:inline;fill:#27272a;fill-opacity:1;stroke:none;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="rect1"
width="512"
height="512"
x="0"
y="0"
sodipodi:insensitive="true"
inkscape:label="background"
ry="128.00018"
sodipodi:type="rect"
rx="129.98714" />
<g
id="g17"
inkscape:label="logo"
transform="translate(8)">
<g
id="g8"
inkscape:label="ring">
<g
id="g7"
inkscape:label="back">
<circle
style="fill:#d97706;fill-opacity:1;stroke:#d97706;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="path1"
cx="224"
cy="256"
r="128"
inkscape:label="ring back" />
<rect
style="fill:#d97706;fill-opacity:1;stroke:none;stroke-width:21.8936;stroke-dasharray:none;stroke-opacity:1"
id="rect2"
width="48"
height="288"
x="224"
y="112"
inkscape:label="rect2" />
</g>
<g
id="g1"
transform="translate(-4.163147,-0.69235229)"
inkscape:label="front">
<circle
style="fill:#27272a;fill-opacity:1;stroke:none;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="path1-5-2"
cx="276.16315"
cy="256.69235"
r="128" />
<circle
style="fill:none;fill-opacity:1;stroke:#fbbf24;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="path1-5"
cx="276.16315"
cy="256.69235"
r="128" />
</g>
</g>
<g
id="g16"
inkscape:label="tick"
transform="translate(16.000231,-8.3918418e-5)">
<g
id="g6"
transform="rotate(45,-57.96574,415.4208)"
style="fill:#d97706;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:label="back">
<rect
style="fill:#d97706;fill-opacity:1;stroke:none;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="rect9"
width="32.000458"
height="32"
x="273.94067"
y="210.74516"
transform="rotate(-45,-57.96574,415.4208)" />
<rect
style="fill:#d97706;fill-opacity:1;stroke:none;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="rect8"
width="32.000458"
height="32"
x="206.05841"
y="233.37257"
transform="rotate(-45,-57.96574,415.4208)" />
<rect
style="fill:#d97706;fill-opacity:1;stroke:none;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="rect7"
height="16"
x="228.686"
y="285.255"
width="32"
transform="rotate(-45,-57.96574,415.4208)" />
<rect
style="fill:#d97706;fill-opacity:1;stroke:none;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="rect5"
width="64"
height="32"
x="0"
y="100" />
<rect
style="fill:#d97706;fill-opacity:1;stroke:none;stroke-width:39.1918;stroke-dasharray:none;stroke-opacity:1"
id="rect6"
width="32"
height="96"
x="32"
y="36" />
</g>
<g
id="g4"
transform="rotate(45,-41.965512,454.04877)"
style="fill:#fbbf24;fill-opacity:1;stroke:none;stroke-opacity:1"
inkscape:label="front">
<rect
style="fill:#fbbf24;fill-opacity:1;stroke:none;stroke-width:32;stroke-dasharray:none;stroke-opacity:1"
id="rect3"
width="64"
height="32"
x="0"
y="100" />
<rect
style="fill:#fbbf24;fill-opacity:1;stroke:none;stroke-width:39.1918;stroke-dasharray:none;stroke-opacity:1"
id="rect4"
width="32"
height="96"
x="32"
y="36" />
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.4 KiB

View File

@@ -3,8 +3,8 @@
"short_name": "Todo Baggins",
"start_url": "/",
"display": "standalone",
"background_color": "#27272a",
"theme_color": "#27272a",
"background_color": "#101828",
"theme_color": "#b89a2e",
"icons": [
{
"src": "/assets/images/icon.png",

View File

@@ -1,17 +0,0 @@
@layer base {
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("/assets/fonts/inter_variable.woff2") format("woff2");
}
@font-face {
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url("/assets/fonts/inter_variable_italic.woff2") format("woff2");
}
}

View File

@@ -8,56 +8,37 @@ input[type="range"] {
background: transparent;
}
input[type="range"]::-moz-range-thumb {
input[type="range"]::-moz-range-thumb,
input[type="range"]::-webkit-slider-thumb {
width: 1.25rem;
height: 1.25rem;
background: rgba(228 228 231);
background: var(--color-gray-400);
filter: drop-shadow(0 var(--spacing) 0 var(--color-gray-500));
border: 0;
border-radius: 0.5rem;
cursor: pointer;
}
input[type="range"]::-moz-range-progress {
background: #525259;
input[type="range"]::-webkit-slider-thumb {
position: relative;
top: -9px;
}
input[type="range"]::-moz-range-track,
input[type="range"]::-webkit-slider-runnable-track {
background: var(--color-gray-800-muted);
height: 0.5rem;
filter: drop-shadow(
0 calc(0px - var(--spacing)) 0 var(--color-gray-900-muted)
);
border-radius: 0.25rem;
}
input[type="range"]::-moz-range-track {
background: rgba(39 39 42 / 50%);
height: 0.5rem;
border-radius: 0.25rem;
}
input[type="range"].input-range-reverse::-moz-range-progress {
background: #2d2d31;
height: 0.5rem;
border-radius: 0.25rem;
}
input[type="range"].input-range-reverse::-moz-range-track {
background: rgba(113 113 122 / 50%);
height: 0.5rem;
border-radius: 0.25rem;
}
input[type="range"]::-webkit-slider-thumb {
width: 1.25rem;
height: 1.25rem;
background: rgba(228 228 231);
border: 0;
border-radius: 0.5rem;
position: relative;
top: -0.4rem;
transform: translateY(3px);
}
input[type="range"]::-webkit-slider-runnable-track {
background: rgba(39 39 42 / 50%);
height: 0.5rem;
border-radius: 0.25rem;
}
input[type="range"].input-range-reverse::-webkit-slider-runnable-track {
background: rgba(39 39 42 / 50%);
height: 0.5rem;
border-radius: 0.25rem;
position: relative;
top: 3px;
}

View File

@@ -0,0 +1,7 @@
select {
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640'%3E%3Cpath fill='%239ca3af' d='M300.3 440.8C312.9 451 331.4 450.3 343.1 438.6L471.1 310.6C480.3 301.4 483 287.7 478 275.7C473 263.7 461.4 256 448.5 256L192.5 256C179.6 256 167.9 263.8 162.9 275.8C157.9 287.8 160.7 301.5 169.9 310.6L297.9 438.6L300.3 440.8z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-size: 2rem;
background-position: right 0.5rem center;
}

View File

@@ -0,0 +1,48 @@
name-template: "Release v$RESOLVED_VERSION 🦀"
tag-template: "v$RESOLVED_VERSION"
categories:
- title: "🚀 Features"
label: "feature"
- title: "🐛 Bug Fixes"
label: "bug"
- title: "♻️ Refactor"
label: "refactor"
- title: "📝 Documentation"
label: "documentation"
- title: "🧰 Maintenance"
labels:
- "chore"
- "dependencies"
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- "major"
minor:
labels:
- "minor"
patch:
labels:
- "patch"
default: patch
template: |
## Changes
$CHANGES
autolabeler:
- label: feature
branch:
- "/^feat(ure)?[/-].+/"
- label: bug
branch:
- "/^fix[/-].+/"
- label: refactor
branch:
- "/(refactor|refactoring)[/-].+/"
- label: documentation
branch:
- "/doc(s|umentation)[/-].+/"
- label: chore
branch:
- "/^chore[/-].+/"

View File

@@ -0,0 +1,25 @@
name: Cargo Build & Test
on:
workflow_dispatch:
push:
branches: [ "main" ]
pull_request:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
name: Rust project - latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install the nightly toolchain
uses: dtolnay/rust-toolchain@nightly
- name: Install linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt update && sudo apt install build-essential libssl-dev pkg-config libglib2.0-dev libgtk-3-dev
- run: cargo build --verbose
- run: cargo test --verbose

View File

@@ -0,0 +1,19 @@
name: Cargo Publish
on:
release:
types: [published]
env:
CARGO_TERM_COLOR: always
jobs:
publish:
name: Publish to crate.io
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: cargo publish
working-directory: ./packages/lib
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

View File

@@ -0,0 +1,23 @@
name: Release Drafter
on:
push:
branches:
- main
# pull_request event is required only for autolabeler
pull_request:
# Only following types are handled by the action, but one can default to all as well
types: [opened, reopened, synchronize]
# pull_request_target event is required for autolabeler to support PRs from forks
# pull_request_target:
# types: [opened, reopened, synchronize]
jobs:
update_release_draft:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

16
dioxus-free-icons/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# Added by cargo
/target
/Cargo.lock

27
dioxus-free-icons/.gitmodules vendored Normal file
View File

@@ -0,0 +1,27 @@
[submodule "icon_resources/font-awesome"]
path = icon_resources/font-awesome
url = https://github.com/FortAwesome/Font-Awesome
[submodule "icon_resources/heroicons"]
path = icon_resources/heroicons
url = https://github.com/tailwindlabs/heroicons
[submodule "icon_resources/ionicons"]
path = icon_resources/ionicons
url = https://github.com/ionic-team/ionicons
[submodule "icon_resources/octicons"]
path = icon_resources/octicons
url = https://github.com/primer/octicons
[submodule "icon_resources/bootstrap"]
path = icon_resources/bootstrap
url = https://github.com/twbs/icons
[submodule "icon_resources/feather"]
path = icon_resources/feather
url = https://github.com/feathericons/feather
[submodule "icon_resources/lucide"]
path = icon_resources/lucide
url = https://github.com/lucide-icons/lucide
[submodule "icon_resources/material-design-icons"]
path = icon_resources/material-design-icons
url = https://github.com/google/material-design-icons
[submodule "icon_resources/vscode-codicons"]
path = icon_resources/vscode-codicons
url = https://github.com/microsoft/vscode-codicons.git

View File

@@ -0,0 +1,24 @@
# monorepo only work when using virtual manifest
# see: https://github.com/rust-lang/cargo/issues/7467#issuecomment-867632379
[workspace]
resolver = "2"
members = ["packages/codegen", "packages/example", "packages/lib"]
[workspace.dependencies]
dioxus = { git = "https://github.com/matous-volf/dioxus", rev = "627d5ca5b80aeed57c23e253024665f103117f5e", default-features = false, features = [
"html",
"macro",
"signals",
] }
[profile]
[profile.wasm-dev]
inherits = "dev"
opt-level = 1
[profile.server-dev]
inherits = "dev"
[profile.android-dev]
inherits = "dev"

25
dioxus-free-icons/LICENSE Normal file
View File

@@ -0,0 +1,25 @@
MIT License
Copyright (c) 2022-Present Daiki Nishikawa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
Icons are taken from the other projects
so please check each project licences accordingly.

117
dioxus-free-icons/README.md Normal file
View File

@@ -0,0 +1,117 @@
[![Discord Server](https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square)](https://discord.gg/sKJSVNSCDJ)
[![Crates.io](https://img.shields.io/crates/v/dioxus-free-icons)](https://crates.io/crates/dioxus-free-icons)
# dioxus-free-icons 🙂
Use free svg icons in your [Dioxus](https://dioxuslabs.com/) projects easily with dioxus-free-icons.
More information about this crate can be found in the [crate documentation](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/).
## Install
To use `dioxus-free-icons`, add this to your Cargo.toml:
```toml
[dependencies]
dioxus-free-icons = { version = "0.9", features = ["font-awesome-brands"] }
```
### Support features
The following features are available. Please see [react-icons site](https://react-icons.github.io/react-icons) to check the icon name and icon design.
- [bootstrap](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/bs_icons/index.html)
- [codicons](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/vs_icons/index.html)
- [font-awesome-brands](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/fa_brands_icons/index.html)
- [font-awesome-regular](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/fa_regular_icons/index.html)
- [font-awesome-solid](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/fa_solid_icons/index.html)
- [feather](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/fi_icons/index.html)
- [octicons](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/go_icons/index.html)
- [hero-icons-outline](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/hi_outline_icons/index.html)
- [hero-icons-solid](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/hi_solid_icons/index.html)
- [ionicons](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/io_icons/index.html)
- [lucide](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/ld_icons/index.html)
- [material-design-icons-action](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_action_icons/index.html)
- [material-design-icons-alert](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_alert_icons/index.html)
- [material-design-icons-av](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_av_icons/index.html)
- [material-design-icons-communication](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_communication_icons/index.html)
- [material-design-icons-content](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_content_icons/index.html)
- [material-design-icons-device](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/io_icons/index.html)
- [material-design-icons-editor](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_editor_icons/index.html)
- [material-design-icons-file](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_file_icons/index.html)
- [material-design-icons-hardware](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_hardware_icons/index.html)
- [material-design-icons-home](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_home_icons/index.html)
- [material-design-icons-image](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_image_icons/index.html)
- [material-design-icons-maps](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_maps_icons/index.html)
- [material-design-icons-navigation](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_navigation_icons/index.html)
- [material-design-icons-notification](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_notification_icons/index.html)
- [material-design-icons-places](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_places_icons/index.html)
- [material-design-icons-social](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_social_icons/index.html)
- [material-design-icons-toggle](https://docs.rs/dioxus-free-icons/latest/dioxus_free_icons/icons/md_toggle_icons/index.html)
## Example
This library provides Icon component, which will generate SVG for a Font Awesome icon.
```rust
use dioxus::prelude::*;
use dioxus_free_icons::icons::fa_brands_icons::FaRust;
use dioxus_free_icons::Icon;
fn RustIcon() -> Element {
rsx!(
Icon {
width: 30,
height: 30,
fill: "black",
icon: FaRust,
}
)
}
```
## License
This project is licensed under the MIT license.
### Icon
Icon Library|License|Version
---|---|---
[Bootstrap Icons](https://icons.getbootstrap.com/)|[MIT License](https://github.com/twbs/icons/blob/main/LICENSE.md)| [1.8.3](https://github.com/twbs/icons/tree/v1.8.3)
[Codicons](https://microsoft.github.io/vscode-codicons/dist/codicon.html)|[MIT License](https://github.com/microsoft/vscode-codicons/blob/main/LICENSE-CODE) & [CC BY 4.0 License](https://github.com/microsoft/vscode-codicons/blob/main/LICENSE) | [0.0.36](https://github.com/microsoft/vscode-codicons/tree/0.0.36)
[Feather](https://feathericons.com/)|[MIT License](https://github.com/feathericons/feather/blob/master/LICENSE)| [4.29.0](https://github.com/feathericons/feather/tree/v4.29.0)
[Font Awesome](https://fontawesome.com/)|[CC BY 4.0 License](https://creativecommons.org/licenses/by/4.0/)| [7.1.0](https://github.com/FortAwesome/Font-Awesome/tree/7.1.0)
[Heroicons](https://heroicons.com/)|[MIT License](https://github.com/tailwindlabs/heroicons/blob/master/LICENSE)| [1.0.6](https://github.com/tailwindlabs/heroicons/tree/v1.0.6)
[Ionicons](https://ionic.io/ionicons)|[MIT License](https://github.com/ionic-team/ionicons/blob/main/LICENSE)| [6.0.2](https://github.com/ionic-team/ionicons/tree/v6.0.2)
[Lucide](https://lucide.dev)|[ISC License](https://github.com/lucide-icons/lucide/blob/main/LICENSE)| [0.555.0](https://github.com/lucide-icons/lucide/tree/v0.555.0)
[Material Design icons](https://developers.google.com/fonts/docs/material_icons)|[Apache License 2.0](https://github.com/google/material-design-icons/blob/master/LICENSE)| [4.0.0](https://github.com/google/material-design-icons/tree/4.0.0)
[Octicons](https://primer.style/octicons/)|[MIT License](https://github.com/primer/octicons/blob/main/LICENSE)| [17.3.0](https://github.com/primer/octicons/tree/v17.3.0)
## Contribution
The project welcomes all contributions from anyone willing to work in good faith with other contributors and the community.
In particular, contributions regarding support for other free icons such as Material Design icons or Ionicons are welcome.
This library aims to be a react-icons-like library for dioxus projects.
### Development
```sh
// generate icon files
cd packages/codegen
cargo run
```
### Preview
```sh
cd packages/exmaple
cargo install dioxus-cli
dx serve
```
### Update icons
1. checkout a new tag in the icon resource submodule
2. create new icon files
3. Update README.md and check the LICENSE

View File

@@ -0,0 +1,13 @@
[package]
name = "dioxus-fontawesome-examples"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
codegen = "0.1.3"
heck = "0.4.0"
regex = "1.6.0"
scraper = "0.13.0"
walkdir = "2.3.2"

View File

@@ -0,0 +1,215 @@
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::{ffi::OsStr, path::Path};
use heck::ToSnakeCase;
use heck::ToUpperCamelCase;
use regex::Regex;
use scraper::node::Element;
use scraper::ElementRef;
use scraper::Html;
use walkdir::WalkDir;
const ICON_TEMPLATE: &str = r#"#[derive(Copy, Clone, Debug, PartialEq)]
pub struct {ICON_NAME};
impl IconShape for {ICON_NAME} {
fn view_box(&self) -> &str {
"{VIEW_BOX}"
}
fn xmlns(&self) -> &str {
"{XMLNS}"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
({FILL_COLOR}, {STROKE_COLOR}, {STROKE_WIDTH})
}
fn stroke_linecap(&self) -> &str {
"{STROKE_LINECAP}"
}
fn stroke_linejoin(&self) -> &str {
"{STROKE_LINEJOIN}"
}
fn child_elements(&self) -> Element {
rsx! {
{CHILD_ELEMENTS}
}
}
}
"#;
pub fn create_icon_file(svg_path: &str, output_path: &str, icon_prefix: &str) {
let files = collect_svg_files(svg_path, icon_prefix);
let icon_file = files
.into_iter()
.map(|file| {
let svg_str = fs::read_to_string(&file).unwrap();
let fragment = Html::parse_fragment(&svg_str);
let elements = fragment
.tree
.nodes()
.filter_map(|node| {
if node.value().is_element() {
let element = ElementRef::wrap(node).unwrap().value();
if !element.attrs.is_empty() {
return Some(element);
}
}
None
})
.collect::<Vec<_>>();
let svg_element = &elements[0];
let svg_child_elements = &elements[1..];
let icon_name = icon_name(&file, icon_prefix);
let (view_box, xmlns) = extract_svg_attrs(svg_element);
let child_elements = extract_svg_child_elements(svg_child_elements, icon_prefix);
let (fill_color, stroke_color, stroke_width) = extract_svg_colors(icon_prefix);
let stroke_linecap = extract_stroke_linecap(icon_prefix);
let stroke_linejoin = extract_stroke_linejoin(icon_prefix);
ICON_TEMPLATE
.replace("{ICON_NAME}", &format!("{}{}", icon_prefix, &icon_name))
.replace("{VIEW_BOX}", &view_box)
.replace("{XMLNS}", &xmlns)
.replace("{CHILD_ELEMENTS}", &child_elements)
.replace("{FILL_COLOR}", &fill_color)
.replace("{STROKE_COLOR}", &stroke_color)
.replace("{STROKE_WIDTH}", &stroke_width)
.replace("{STROKE_LINECAP}", &stroke_linecap)
.replace("{STROKE_LINEJOIN}", &stroke_linejoin)
})
.collect::<Vec<_>>()
.join("\n");
// write to file
let mut file = File::create(output_path).unwrap();
file.write_all(
format!(
"{}\n\n{}",
"use super::super::IconShape;\nuse dioxus::prelude::*;", icon_file
)
.as_bytes(),
)
.unwrap();
file.flush().unwrap();
}
fn collect_svg_files(svg_path: &str, icon_prefix: &str) -> Vec<PathBuf> {
let dir_entries = WalkDir::new(svg_path)
.sort_by_file_name()
.into_iter()
.filter_map(|e| e.ok())
.collect::<Vec<_>>();
dir_entries
.into_iter()
.filter(|e| match icon_prefix {
"Go" => {
let re = Regex::new(r".*-16.svg$").unwrap();
return re.is_match(e.path().to_str().unwrap());
}
"Md" => {
let split_vec = e.path().components().collect::<Vec<_>>();
return split_vec.iter().any(|c| c.as_os_str() == "materialicons")
&& e.file_name().to_str().unwrap() == "24px.svg";
}
_ => return e.path().extension() == Some(OsStr::new("svg")),
})
.map(|dir| PathBuf::from(dir.path()))
.collect::<Vec<_>>()
}
fn icon_name(path: &Path, icon_prefix: &str) -> String {
match icon_prefix {
"Go" => {
let filename = path.file_name().unwrap().to_str().unwrap();
let name = filename.split('.').next().unwrap();
name.replace("-16", "").to_upper_camel_case()
}
"Md" => {
let split_vec = path.components().collect::<Vec<_>>();
let name = split_vec[split_vec.len() - 3];
name.as_os_str().to_str().unwrap().to_upper_camel_case()
}
_ => {
let filename = path.file_name().unwrap().to_str().unwrap();
let name = filename.split('.').next().unwrap();
name.to_upper_camel_case()
}
}
}
fn extract_svg_attrs(element: &Element) -> (String, String) {
let view_box = element.attr("viewBox").unwrap_or("0 0 16 16");
let xmlns = element
.attr("xmlns")
.unwrap_or("http://www.w3.org/2000/svg");
(String::from(view_box), String::from(xmlns))
}
fn extract_svg_colors(icon_prefix: &str) -> (&str, &str, &str) {
match icon_prefix {
"Fi" => ("\"none\"", "user_color", "\"2\""),
"Ld" => ("\"none\"", "user_color", "\"2\""),
"Io" => ("user_color", "user_color", "\"0\""),
_ => ("user_color", "\"none\"", "\"0\""),
}
}
fn extract_stroke_linecap(icon_prefix: &str) -> &str {
match icon_prefix {
"Ld" => "round",
"Fi" => "round",
_ => "butt",
}
}
fn extract_stroke_linejoin(icon_prefix: &str) -> &str {
match icon_prefix {
"Ld" => "round",
"Fi" => "round",
_ => "miter",
}
}
fn extract_svg_child_elements(elements: &[&Element], icon_prefix: &str) -> String {
let elements = match icon_prefix {
"Md" => &elements[1..],
_ => elements,
};
elements
.iter()
.map(|element| {
let tag_name = element.name();
let mut element_attrs = element
.attrs()
.filter_map(|(name, value)| {
let value = if icon_prefix == "Io" {
value.replace("fill:none;stroke:#000;", "")
} else {
value.to_string()
};
let re = Regex::new(r"^data-.*$").unwrap();
if !re.is_match(name) && name != "fill" {
Some(format!(
" {}: \"{}\",",
name.to_snake_case(),
value
))
} else {
None
}
})
.collect::<Vec<_>>();
element_attrs.sort();
let attrs_str = element_attrs.join("\n");
" {TAG_NAME} {\n{ATTRS}\n }"
.replace("{TAG_NAME}", tag_name)
.replace("{ATTRS}", &attrs_str)
})
.collect::<Vec<_>>()
.join("\n")
}

View File

@@ -0,0 +1,79 @@
mod create_icon_file;
fn main() {
const OUTPUT_BASE_PATH: &str = "../lib/src/icons";
// create font awesome icons
const FA_SVG_BASE_PATH: &str = "../../icon_resources/font-awesome/svgs";
for icon_type in vec!["brands", "regular", "solid"].into_iter() {
let svg_path = format!("{}/{}", FA_SVG_BASE_PATH, icon_type);
let output_path = format!("{}/fa_{}_icons.rs", OUTPUT_BASE_PATH, icon_type);
create_icon_file::create_icon_file(&svg_path, &output_path, "Fa");
}
// create hero icons
const HI_SVG_BASE_PATH: &str = "../../icon_resources/heroicons/src";
for icon_type in vec!["outline", "solid"].into_iter() {
let svg_path = format!("{}/{}", HI_SVG_BASE_PATH, icon_type);
let output_path = format!("{}/hi_{}_icons.rs", OUTPUT_BASE_PATH, icon_type);
create_icon_file::create_icon_file(&svg_path, &output_path, "Hi");
}
// create ionicons
const IO_SVG_BASE_PATH: &str = "../../icon_resources/ionicons/src/svg";
let output_path = format!("{}/io_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(IO_SVG_BASE_PATH, &output_path, "Io");
// create octicons
const GO_SVG_BASE_PATH: &str = "../../icon_resources/octicons/icons";
let go_output_path = format!("{}/go_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(GO_SVG_BASE_PATH, &go_output_path, "Go");
// create bootstrap icons
const BS_SVG_BASE_PATH: &str = "../../icon_resources/bootstrap/icons";
let bs_output_path = format!("{}/bs_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(BS_SVG_BASE_PATH, &bs_output_path, "Bs");
// create feather icons
const FI_SVG_BASE_PATH: &str = "../../icon_resources/feather/icons";
let fi_output_path = format!("{}/fi_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(FI_SVG_BASE_PATH, &fi_output_path, "Fi");
// create feather icons
const LD_SVG_BASE_PATH: &str = "../../icon_resources/lucide/icons";
let ld_output_path = format!("{}/ld_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(LD_SVG_BASE_PATH, &ld_output_path, "Ld");
// create material design icons
const MI_SVG_BASE_PATH: &str = "../../icon_resources/material-design-icons/src";
for icon_type in vec![
"action",
"alert",
"av",
"communication",
"content",
"device",
"editor",
"file",
"hardware",
"home",
"image",
"maps",
"navigation",
"notification",
"places",
"social",
"toggle",
]
.into_iter()
{
let svg_path = format!("{}/{}", MI_SVG_BASE_PATH, icon_type);
let output_path = format!("{}/md_{}_icons.rs", OUTPUT_BASE_PATH, icon_type);
create_icon_file::create_icon_file(&svg_path, &output_path, "Md");
}
// create vscode-codicons
const VS_SVG_BASE_PATH: &str = "../../icon_resources/vscode-codicons/src/icons";
let vs_output_path = format!("{}/vsc_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(VS_SVG_BASE_PATH, &vs_output_path, "Vsc");
}

View File

@@ -0,0 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# Added by cargo
/target
/dist

View File

@@ -0,0 +1,19 @@
[package]
name = "hello-dioxus"
version = "0.1.0"
authors = ["nissy-dev <nd.12021218@gmail.com>"]
edition = "2021"
[dependencies]
dioxus = { workspace = true, features = ["launch", "web"] }
dioxus-free-icons = { path = "../lib", features = ["font-awesome-brands"] }
log = "0.4.6"
# WebAssembly Debug
wasm-logger = "0.2.0"
console_error_panic_hook = "0.1.7"
[profile.release]
lto = true
opt-level = 's'

View File

@@ -0,0 +1,42 @@
[application]
# App (Project) Name
name = "hello-dioxus"
# Dioxus App Default Platform
# desktop, web, mobile, ssr
default_platform = "web"
# `build` & `serve` dist path
out_dir = "dist"
# resource (public) file folder
asset_dir = "public"
[web.app]
# HTML title tag content
title = "dioxus | ⛺"
[web.watcher]
# when watcher trigger, regenerate the `index.html`
reload_html = true
# which files or dirs will be watcher monitoring
watch_path = ["src", "public"]
# include `assets` in web platform
[web.resource]
# CSS style file
style = []
# Javascript code file
script = []
[web.resource.dev]
# Javascript code file
# serve: [dev-server] only
script = []

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-Present Daiki Nishikawa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,11 @@
# Example
> a template for starting a dioxus project to be used with [dioxus-cli](https://github.com/DioxusLabs/cli)
## Usage
#### Start a `dev-server` for the project:
```
dioxus serve
```

View File

@@ -0,0 +1,28 @@
use dioxus::prelude::*;
use dioxus_free_icons::icons::fa_brands_icons::FaRust;
use dioxus_free_icons::Icon;
fn main() {
// init debug tool for WebAssembly
wasm_logger::init(wasm_logger::Config::default());
console_error_panic_hook::set_once();
launch(app);
}
fn app() -> Element {
rsx! (
div {
style: "text-align: center;",
h1 { "🌗 Dioxus 🚀" }
h3 { "Frontend that scales." }
p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." },
Icon {
width: 60,
height: 60,
icon: FaRust,
}
}
)
}

View File

@@ -0,0 +1,45 @@
[package]
name = "dioxus-free-icons"
version = "0.10.0"
edition = "2021"
authors = ["Daiki Nishikawa <nd.12021218@gmail.com>", "Marc Espín <mespinsanz@gmail.com>"]
description = "Use free svg icons in your Dioxus projects easily with dioxus-free-icons."
license = "MIT"
repository = "https://github.com/dioxus-community/dioxus-free-icons"
readme = "../../README.md"
[dependencies]
dioxus = { workspace = true }
[features]
font-awesome-brands = []
font-awesome-regular = []
font-awesome-solid = []
bootstrap = []
feather = []
octicons = []
hero-icons-outline = []
hero-icons-solid = []
ionicons = []
lucide = []
material-design-icons-action = []
material-design-icons-alert = []
material-design-icons-av = []
material-design-icons-communication = []
material-design-icons-content = []
material-design-icons-device = []
material-design-icons-editor = []
material-design-icons-file = []
material-design-icons-hardware = []
material-design-icons-home = []
material-design-icons-image = []
material-design-icons-maps = []
material-design-icons-navigation = []
material-design-icons-notification = []
material-design-icons-places = []
material-design-icons-social = []
material-design-icons-toggle = []
codicons = []
[package.metadata.docs.rs]
all-features = true

View File

@@ -0,0 +1,46 @@
MIT License
Copyright (c) 2022-Present Daiki Nishikawa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
Icons are taken from the Font Awesome projects,
so please check the project license accordingly.
Bootstrap Icons - https://github.com/twbs/icons
License: MIT License https://github.com/twbs/icons/blob/main/LICENSE.md
Feather - https://feathericons.com/
License: MIT https://github.com/feathericons/feather/blob/master/LICENSE
Font Awesome - https://fontawesome.com/
License: CC BY 4.0 License https://fontawesome.com/license/free
Heroicons - https://heroicons.com/
License: MIT License https://github.com/tailwindlabs/heroicons/blob/master/LICENSE
Ionicons - https://ionic.io/ionicons/
License: MIT License https://github.com/ionic-team/ionicons/blob/main/LICENSE
Material Design icons - https://developers.google.com/fonts/docs/material_icons
License: Apache License 2.0 https://github.com/google/material-design-icons/blob/master/LICENSE
Octicons - https://primer.style/octicons/
License: MIT https://github.com/primer/octicons/blob/master/LICENSE

View File

@@ -0,0 +1,65 @@
use dioxus::prelude::*;
/// Icon shape trait
pub trait IconShape {
fn view_box(&self) -> &str;
fn xmlns(&self) -> &str;
fn child_elements(&self) -> Element;
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
("none", user_color, "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
}
/// Icon component Props
#[derive(PartialEq, Props, Clone)]
pub struct IconProps<T: IconShape + Clone + PartialEq + 'static> {
/// The icon shape to use.
pub icon: T,
/// The height of the `<svg>` element. Defaults to 20. Pass None to omit.
#[props(default = Some(20))]
pub height: Option<u32>,
/// The width of the `<svg>` element. Defaults to 20. Pass None to omit.
#[props(default = Some(20))]
pub width: Option<u32>,
/// The color to use for filling the icon. Defaults to "currentColor".
#[props(default = "currentColor".to_string())]
pub fill: String,
/// An class for the `<svg>` element.
#[props(default = "".to_string())]
pub class: String,
/// An accessible, short-text description for the icon.
pub title: Option<String>,
/// The style of the `<svg>` element.
pub style: Option<String>,
}
/// Icon component which generates SVG elements
#[allow(non_snake_case)]
pub fn Icon<T: IconShape + Clone + PartialEq + 'static>(props: IconProps<T>) -> Element {
let (fill, stroke, stroke_width) = props.icon.fill_and_stroke(&props.fill);
rsx!(
svg {
class: "{props.class}",
style: props.style,
height: props.height.map(|height| height.to_string()),
width: props.width.map(|width| width.to_string()),
view_box: "{props.icon.view_box()}",
xmlns: "{props.icon.xmlns()}",
fill,
stroke,
stroke_width,
stroke_linecap: "{props.icon.stroke_linecap()}",
stroke_linejoin: "{props.icon.stroke_linejoin()}",
if let Some(title_text) = props.title {
title { "{title_text}" }
}
{props.icon.child_elements()}
}
)
}

View File

@@ -0,0 +1,56 @@
#[cfg(feature = "bootstrap")]
pub mod bs_icons;
#[cfg(feature = "font-awesome-brands")]
pub mod fa_brands_icons;
#[cfg(feature = "font-awesome-regular")]
pub mod fa_regular_icons;
#[cfg(feature = "font-awesome-solid")]
pub mod fa_solid_icons;
#[cfg(feature = "feather")]
pub mod fi_icons;
#[cfg(feature = "octicons")]
pub mod go_icons;
#[cfg(feature = "hero-icons-outline")]
pub mod hi_outline_icons;
#[cfg(feature = "hero-icons-solid")]
pub mod hi_solid_icons;
#[cfg(feature = "ionicons")]
pub mod io_icons;
#[cfg(feature = "lucide")]
pub mod ld_icons;
#[cfg(feature = "material-design-icons-action")]
pub mod md_action_icons;
#[cfg(feature = "material-design-icons-alert")]
pub mod md_alert_icons;
#[cfg(feature = "material-design-icons-av")]
pub mod md_av_icons;
#[cfg(feature = "material-design-icons-communication")]
pub mod md_communication_icons;
#[cfg(feature = "material-design-icons-content")]
pub mod md_content_icons;
#[cfg(feature = "material-design-icons-device")]
pub mod md_device_icons;
#[cfg(feature = "material-design-icons-editor")]
pub mod md_editor_icons;
#[cfg(feature = "material-design-icons-file")]
pub mod md_file_icons;
#[cfg(feature = "material-design-icons-hardware")]
pub mod md_hardware_icons;
#[cfg(feature = "material-design-icons-home")]
pub mod md_home_icons;
#[cfg(feature = "material-design-icons-image")]
pub mod md_image_icons;
#[cfg(feature = "material-design-icons-maps")]
pub mod md_maps_icons;
#[cfg(feature = "material-design-icons-navigation")]
pub mod md_navigation_icons;
#[cfg(feature = "material-design-icons-notification")]
pub mod md_notification_icons;
#[cfg(feature = "material-design-icons-places")]
pub mod md_places_icons;
#[cfg(feature = "material-design-icons-social")]
pub mod md_social_icons;
#[cfg(feature = "material-design-icons-toggle")]
pub mod md_toggle_icons;
#[cfg(feature = "codicons")]
pub mod vsc_icons;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
use super::super::IconShape;
use dioxus::prelude::*;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdAddAlert;
impl IconShape for MdAddAlert {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M10.01 21.01c0 1.1.89 1.99 1.99 1.99s1.99-.89 1.99-1.99h-3.98zm8.87-4.19V11c0-3.25-2.25-5.97-5.29-6.69v-.72C13.59 2.71 12.88 2 12 2s-1.59.71-1.59 1.59v.72C7.37 5.03 5.12 7.75 5.12 11v5.82L3 18.94V20h18v-1.06l-2.12-2.12zM16 13.01h-3v3h-2v-3H8V11h3V8h2v3h3v2.01z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdAutoDelete;
impl IconShape for MdAutoDelete {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
polygon {
points: "15,2 11.5,2 10.5,1 5.5,1 4.5,2 1,2 1,4 15,4",
}
path {
d: "M16,9c-0.7,0-1.37,0.1-2,0.29V5H2v12c0,1.1,0.9,2,2,2h5.68c1.12,2.36,3.53,4,6.32,4c3.87,0,7-3.13,7-7 C23,12.13,19.87,9,16,9z M16,21c-2.76,0-5-2.24-5-5s2.24-5,5-5s5,2.24,5,5S18.76,21,16,21z",
}
polygon {
points: "16.5,12 15,12 15,17 18.6,19.1 19.4,17.9 16.5,16.2",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdError;
impl IconShape for MdError {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdErrorOutline;
impl IconShape for MdErrorOutline {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdNotificationImportant;
impl IconShape for MdNotificationImportant {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M18 16v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2zm-5 0h-2v-2h2v2zm0-4h-2V8h2v4zm-1 10c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdWarning;
impl IconShape for MdWarning {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z",
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,818 @@
use super::super::IconShape;
use dioxus::prelude::*;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdApproval;
impl IconShape for MdApproval {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M4 16v6h16v-6c0-1.1-.9-2-2-2H6c-1.1 0-2 .9-2 2zm14 2H6v-2h12v2zM12 2C9.24 2 7 4.24 7 7l5 7 5-7c0-2.76-2.24-5-5-5zm0 9L9 7c0-1.66 1.34-3 3-3s3 1.34 3 3l-3 4z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdAttachEmail;
impl IconShape for MdAttachEmail {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M21,10V4c0-1.1-0.9-2-2-2H3C1.9,2,1.01,2.9,1.01,4L1,16c0,1.1,0.9,2,2,2h11v-5c0-1.66,1.34-3,3-3H21z M11,11L3,6V4l8,5 l8-5v2L11,11z",
}
path {
d: "M21,14v4c0,1.1-0.9,2-2,2s-2-0.9-2-2v-4.5c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5V18h2v-4.5c0-1.38-1.12-2.5-2.5-2.5 S15,12.12,15,13.5V18c0,2.21,1.79,4,4,4s4-1.79,4-4v-4H21z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdAttachment;
impl IconShape for MdAttachment {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M2 12.5C2 9.46 4.46 7 7.5 7H18c2.21 0 4 1.79 4 4s-1.79 4-4 4H9.5C8.12 15 7 13.88 7 12.5S8.12 10 9.5 10H17v2H9.41c-.55 0-.55 1 0 1H18c1.1 0 2-.9 2-2s-.9-2-2-2H7.5C5.57 9 4 10.57 4 12.5S5.57 16 7.5 16H17v2H7.5C4.46 18 2 15.54 2 12.5z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCloud;
impl IconShape for MdCloud {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCloudCircle;
impl IconShape for MdCloudCircle {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.5 14H8c-1.66 0-3-1.34-3-3s1.34-3 3-3l.14.01C8.58 8.28 10.13 7 12 7c2.21 0 4 1.79 4 4h.5c1.38 0 2.5 1.12 2.5 2.5S17.88 16 16.5 16z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCloudDone;
impl IconShape for MdCloudDone {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM10 17l-3.5-3.5 1.41-1.41L10 14.17 15.18 9l1.41 1.41L10 17z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCloudDownload;
impl IconShape for MdCloudDownload {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCloudOff;
impl IconShape for MdCloudOff {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19.35 10.04C18.67 6.59 15.64 4 12 4c-1.48 0-2.85.43-4.01 1.17l1.46 1.46C10.21 6.23 11.08 6 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3 0 1.13-.64 2.11-1.56 2.62l1.45 1.45C23.16 18.16 24 16.68 24 15c0-2.64-2.05-4.78-4.65-4.96zM3 5.27l2.75 2.74C2.56 8.15 0 10.77 0 14c0 3.31 2.69 6 6 6h11.73l2 2L21 20.73 4.27 4 3 5.27zM7.73 10l8 8H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h1.73z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCloudQueue;
impl IconShape for MdCloudQueue {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h.71C7.37 7.69 9.48 6 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3s-1.34 3-3 3z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCloudUpload;
impl IconShape for MdCloudUpload {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCreateNewFolder;
impl IconShape for MdCreateNewFolder {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20 6h-8l-2-2H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-1 8h-3v3h-2v-3h-3v-2h3V9h2v3h3v2z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdDriveFileMove;
impl IconShape for MdDriveFileMove {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20 6h-8l-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-6 12v-3h-4v-4h4V8l5 5-5 5z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdDriveFileMoveOutline;
impl IconShape for MdDriveFileMoveOutline {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10zm-8.01-9l-1.41 1.41L12.16 12H8v2h4.16l-1.59 1.59L11.99 17 16 13.01 11.99 9z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdDriveFileRenameOutline;
impl IconShape for MdDriveFileRenameOutline {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M18.41 5.8L17.2 4.59c-.78-.78-2.05-.78-2.83 0l-2.68 2.68L3 15.96V20h4.04l8.74-8.74 2.63-2.63c.79-.78.79-2.05 0-2.83zM6.21 18H5v-1.21l8.66-8.66 1.21 1.21L6.21 18zM11 20l4-4h6v4H11z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdDriveFolderUpload;
impl IconShape for MdDriveFolderUpload {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10zM8 13.01l1.41 1.41L11 12.84V17h2v-4.16l1.59 1.59L16 13.01 12.01 9 8 13.01z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdFileDownload;
impl IconShape for MdFileDownload {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdFileDownloadDone;
impl IconShape for MdFileDownloadDone {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M5 18h14v2H5v-2zm4.6-2.7L5 10.7l2-1.9 2.6 2.6L17 4l2 2-9.4 9.3z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdFileUpload;
impl IconShape for MdFileUpload {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdFolder;
impl IconShape for MdFolder {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdFolderOpen;
impl IconShape for MdFolderOpen {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdFolderShared;
impl IconShape for MdFolderShared {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdGridView;
impl IconShape for MdGridView {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M0 0h24v24H0z",
}
path {
d: "M3 3v8h8V3H3zm6 6H5V5h4v4zm-6 4v8h8v-8H3zm6 6H5v-4h4v4zm4-16v8h8V3h-8zm6 6h-4V5h4v4zm-6 4v8h8v-8h-8zm6 6h-4v-4h4v4z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdRequestQuote;
impl IconShape for MdRequestQuote {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M14,2H6C4.9,2,4.01,2.9,4.01,4L4,20c0,1.1,0.89,2,1.99,2H18c1.1,0,2-0.9,2-2V8L14,2z M15,12h-4v1h3c0.55,0,1,0.45,1,1v3 c0,0.55-0.45,1-1,1h-1v1h-2v-1H9v-2h4v-1h-3c-0.55,0-1-0.45-1-1v-3c0-0.55,0.45-1,1-1h1V9h2v1h2V12z M13,8V3.5L17.5,8H13z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdRuleFolder;
impl IconShape for MdRuleFolder {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20,6h-8l-2-2H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M7.83,16L5,13.17 l1.41-1.41l1.41,1.41l3.54-3.54l1.41,1.41L7.83,16z M17.41,13L19,14.59L17.59,16L16,14.41L14.41,16L13,14.59L14.59,13L13,11.41 L14.41,10L16,11.59L17.59,10L19,11.41L17.41,13z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdSnippetFolder;
impl IconShape for MdSnippetFolder {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M15.88,10.5l1.62,1.62v3.38l-3,0v-5H15.88z M22,8v10c0,1.1-0.9,2-2,2H4c-1.1,0-2-0.9-2-2L2.01,6C2.01,4.9,2.9,4,4,4h6l2,2 h8C21.1,6,22,6.9,22,8z M19,11.5L16.5,9H13v8l6,0V11.5z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdTextSnippet;
impl IconShape for MdTextSnippet {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20.41,8.41l-4.83-4.83C15.21,3.21,14.7,3,14.17,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9.83 C21,9.3,20.79,8.79,20.41,8.41z M7,7h7v2H7V7z M17,17H7v-2h10V17z M17,13H7v-2h10V13z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdTopic;
impl IconShape for MdTopic {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M20,6h-8l-2-2H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M14,16H6v-2h8V16z M18,12H6v-2h12V12z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdUploadFile;
impl IconShape for MdUploadFile {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11zM8 15.01l1.41 1.41L11 14.84V19h2v-4.16l1.59 1.59L16 15.01 12.01 11z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdWorkspacesFilled;
impl IconShape for MdWorkspacesFilled {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M6 13c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm6-10C9.8 3 8 4.8 8 7s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm6 10c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdWorkspacesOutline;
impl IconShape for MdWorkspacesOutline {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M6 15c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2m0-2c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm6-8c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2m0-2C9.8 3 8 4.8 8 7s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm6 12c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2m0-2c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4z",
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
use super::super::IconShape;
use dioxus::prelude::*;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdSensorDoor;
impl IconShape for MdSensorDoor {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M18,2H6C4.9,2,4,2.9,4,4v18h16V4C20,2.9,19.1,2,18,2z M15.5,13.5c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5 S17,11.17,17,12S16.33,13.5,15.5,13.5z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdSensorWindow;
impl IconShape for MdSensorWindow {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M18,4v16H6V4H18 M18,2H6C4.9,2,4,2.9,4,4v16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C20,2.9,19.1,2,18,2L18,2z M7,19h10v-6H7 V19z M10,10h4v1h3V5H7v6h3V10z",
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,298 @@
use super::super::IconShape;
use dioxus::prelude::*;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCheckBox;
impl IconShape for MdCheckBox {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdCheckBoxOutlineBlank;
impl IconShape for MdCheckBoxOutlineBlank {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdIndeterminateCheckBox;
impl IconShape for MdIndeterminateCheckBox {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M17,13H7v-2h10V13z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdRadioButtonChecked;
impl IconShape for MdRadioButtonChecked {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdRadioButtonUnchecked;
impl IconShape for MdRadioButtonUnchecked {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdStar;
impl IconShape for MdStar {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M0 0h24v24H0z",
}
path {
d: "M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdStarBorder;
impl IconShape for MdStarBorder {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdStarHalf;
impl IconShape for MdStarHalf {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M22,9.24l-7.19-0.62L12,2L9.19,8.63L2,9.24l5.46,4.73L5.82,21L12,17.27L18.18,21l-1.63-7.03L22,9.24z M12,15.4V6.1 l1.71,4.04l4.38,0.38l-3.32,2.88l1,4.28L12,15.4z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdStarOutline;
impl IconShape for MdStarOutline {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdToggleOff;
impl IconShape for MdToggleOff {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M17 7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h10c2.76 0 5-2.24 5-5s-2.24-5-5-5zM7 15c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z",
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MdToggleOn;
impl IconShape for MdToggleOn {
fn view_box(&self) -> &str {
"0 0 24 24"
}
fn xmlns(&self) -> &str {
"http://www.w3.org/2000/svg"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
(user_color, "none", "0")
}
fn stroke_linecap(&self) -> &str {
"butt"
}
fn stroke_linejoin(&self) -> &str {
"miter"
}
fn child_elements(&self) -> Element {
rsx! {
path {
d: "M17 7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h10c2.76 0 5-2.24 5-5s-2.24-5-5-5zm0 8c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z",
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
//! # dioxus-free-icons
//!
//! Use free svg icons in your Dioxus projects easily with dioxus-free-icons.
//! This library provides Icon component, which will generate SVG for a Font Awesome icon.
//!
//! Basic usage:
//! ```ignore
//! use dioxus::prelude::*;
//! use dioxus_free_icons::icons::fa_brands_icons::FaRust;
//! use dioxus_free_icons::Icon;
//!
//! fn RustIcon() -> Element {
//! rsx!(
//! Icon {
//! width: 30,
//! height: 30,
//! fill: "black",
//! icon: Icon::FaRust,
//! }
//! )
//! }
//! ```
mod icon_component;
/// a collections of free icons
pub mod icons;
pub use crate::icon_component::{Icon, IconProps, IconShape};

View File

@@ -12,6 +12,7 @@ services:
- ./Cargo.toml:/srv/app/Cargo.toml
- ./diesel.toml:/srv/app/diesel.toml
- ./Dioxus.toml:/srv/app/Dioxus.toml
- ./tailwind.css:/srv/app/tailwind.css
restart: always
ports: ["8000:8000"]
depends_on: ["db"]

View File

@@ -21,7 +21,7 @@ ENV ANDROID_SDK_ROOT=/opt/android-sdk
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN apt-get update && apt-get install -y --no-install-recommends \
openjdk-17-jdk-headless=17.0.17+10-1~deb12u1 \
openjdk-17-jdk-headless=17.0.18+8-1~deb12u1 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& rustup target add aarch64-linux-android \
@@ -39,7 +39,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& keytool -genkeypair -noprompt -keystore /tmp/android_keystore.jks -alias key -keyalg RSA -keysize 2048 -validity 3660 -dname "CN=" -storepass 123456 -keypass 123456 \
&& export ANDROID_HOME="$ANDROID_SDK_ROOT" \
&& export ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION" \
&& dx bundle --platform android --target aarch64-linux-android --release \
&& dx bundle --locked --platform android --target aarch64-linux-android --release \
&& java -jar /tmp/bundletool-all.jar build-apks --bundle=/srv/app/target/dx/todo_baggins/release/android/app/app/build/outputs/bundle/release/TodoBaggins-aarch64-linux-android.aab --output=/tmp/todo_baggins.apks --mode=universal --ks=/tmp/android_keystore.jks --ks-key-alias=key --ks-pass=pass:123456 \
&& mkdir -p /srv/app/bundle \
&& unzip -qp /tmp/todo_baggins.apks universal.apk > /srv/app/bundle/todo_baggins.apk
@@ -47,7 +47,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
FROM builder_base AS builder_web
RUN dx bundle --release
RUN dx bundle --locked --release
FROM debian:bookworm@sha256:b877a1a3fdf02469440f1768cf69c9771338a875b7add5e80c45b756c92ac20a AS runner_web

View File

@@ -1,4 +1,5 @@
use crate::internationalization::get_language_identifier;
use crate::route::Route;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
@@ -7,15 +8,15 @@ use dioxus_i18n::prelude::*;
use dioxus_i18n::unic_langid::langid;
const FAVICON: Asset = asset!("/assets/favicon.ico");
const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css");
#[used]
static FONTS_DIRECTORY: Asset = asset!(
"/assets/fonts",
AssetOptions::builder().with_hash_suffix(false)
);
const FONTS_CSS: Asset = asset!("/assets/styles/fonts.css");
const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css");
const INPUT_NUMBER_ARROWS_CSS: Asset = asset!("/assets/styles/input_number_arrows.css");
const INPUT_RANGE_CSS: Asset = asset!("/assets/styles/input_range.css");
const SELECT_ARROW_CSS: Asset = asset!("/assets/styles/select_arrow.css");
const MANIFEST: Asset = asset!("/assets/manifest.json");
#[component]
@@ -35,14 +36,13 @@ pub(crate) fn App() -> Element {
rsx! {
document::Link { rel: "icon", href: FAVICON }
document::Stylesheet { href: TAILWIND_CSS }
document::Stylesheet { href: FONTS_CSS }
document::Stylesheet { href: INPUT_NUMBER_ARROWS_CSS }
document::Stylesheet { href: INPUT_RANGE_CSS }
document::Stylesheet { href: SELECT_ARROW_CSS }
document::Link { rel: "manifest", href: MANIFEST, crossorigin: "use-credentials" }
document::Script { src: "https://kit.fontawesome.com/3c1b409f8f.js" }
div {
class: "min-h-screen pt-4 pb-36 flex flex-col text-zinc-200 bg-zinc-800",
class: "min-h-screen py-4 flex flex-col text-gray-300 bg-gray-900",
Router::<Route> {}
}
}

View File

@@ -1,78 +1,22 @@
use crate::components::error_boundary_message::ErrorBoundaryMessage;
use crate::components::navigation::Navigation;
use crate::components::project_form::ProjectForm;
use crate::components::task_form::TaskForm;
use crate::models::project::Project;
use crate::models::task::Task;
use crate::route::Route;
use dioxus::prelude::*;
#[component]
pub(crate) fn BottomPanel(display_form: Signal<bool>) -> Element {
// A signal for delaying the application of styles.
#[allow(clippy::redundant_closure)]
let mut expanded = use_signal(|| display_form());
pub(crate) fn BottomPanel() -> Element {
let navigation_expanded = use_signal(|| false);
let current_route = use_route();
let mut project_being_edited = use_context::<Signal<Option<Project>>>();
let mut task_being_edited = use_context::<Signal<Option<Task>>>();
use_effect(use_reactive(&display_form, move |display_form| {
if display_form() {
expanded.set(true);
} else {
spawn(async move {
// Necessary for a smooth not instant height transition.
#[cfg(not(feature = "server"))]
async_std::task::sleep(std::time::Duration::from_millis(500)).await;
/* The check is necessary for the situation when the user expands the panel while
it is being closed. */
if !display_form() {
expanded.set(false);
}
});
}
}));
rsx! {
div {
class: format!(
"flex flex-col pointer-events-auto bg-zinc-700/50 rounded-t-xl border-t-zinc-600 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] overflow-y-scroll {}",
match (display_form(), current_route, navigation_expanded()) {
(false, _, false) => "h-[66px]",
(false, _, true) => "h-[130px]",
(true, Route::ProjectsPage, _) => "h-[130px]",
(true, _, _) => "h-[506px]",
"flex flex-col pointer-events-auto bg-gray-800 transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] overflow-y-scroll {}",
if navigation_expanded() {
"h-[130px]"
} else {
"h-[66px]"
}
),
if expanded() {
ErrorBoundaryMessage {
match current_route {
Route::ProjectsPage => rsx! {
ProjectForm {
project: project_being_edited(),
on_successful_submit: move |_| {
display_form.set(false);
project_being_edited.set(None);
}
}
},
_ => rsx! {
TaskForm {
task: task_being_edited(),
on_successful_submit: move |_| {
display_form.set(false);
task_being_edited.set(None);
}
}
}
}
}
} else {
Navigation {
expanded: navigation_expanded,
}
Navigation {
is_expanded: navigation_expanded,
}
}
}

View File

@@ -0,0 +1,29 @@
use dioxus::prelude::*;
#[component]
pub(crate) fn ButtonPrimary(
class: Option<String>,
children: Element,
#[props(extends = GlobalAttributes, extends = button)] attributes: Vec<Attribute>,
// TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/4019 gets resolved.
onclick: Option<Callback<Event<MouseData>>>,
) -> Element {
rsx! {
button {
class: format!(
"cursor-pointer pb-[6px] hover:pb-[7px] active:pb-[2px] mt-[1px] hover:mt-0 active:mt-[5px] hover:*:drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] active:*:drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] transition-all duration-150 {}",
class.unwrap_or("".to_owned())
),
onclick: move |event| {
if let Some(onclick) = onclick {
onclick.call(event);
}
},
..attributes,
div {
class: "py-3.5 px-4 flex flex-row justify-center items-center bg-amber-300-muted drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)] text-amber-700-muted rounded-xl transition-all duration-150",
{children}
}
}
}
}

View File

@@ -0,0 +1,29 @@
use dioxus::prelude::*;
#[component]
pub(crate) fn ButtonSecondary(
class: Option<String>,
children: Element,
#[props(extends = GlobalAttributes, extends = button)] attributes: Vec<Attribute>,
// TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/4019 gets resolved.
onclick: Option<Callback<Event<MouseData>>>,
) -> Element {
rsx! {
button {
class: format!(
"cursor-pointer pb-[6px] hover:pb-[7px] active:pb-[2px] mt-[1px] hover:mt-0 active:mt-[5px] hover:*:drop-shadow-[0_7px_0_var(--color-gray-800)] active:*:drop-shadow-[0_2px_0_var(--color-gray-800)] transition-all duration-150 {}",
class.unwrap_or("".to_owned())
),
onclick: move |event| {
if let Some(onclick) = onclick {
onclick.call(event);
}
},
..attributes,
div {
class: "py-3.5 px-4 flex flex-row justify-center items-center bg-gray-600 drop-shadow-[0_6px_0_var(--color-gray-800)] rounded-xl transition-all duration-150",
{children}
}
}
}
}

View File

@@ -28,11 +28,12 @@ pub(crate) fn CategoryCalendarTaskList() -> Element {
div {
class: "flex flex-col gap-4",
div {
class: "px-8 flex flex-row items-center gap-2 font-bold",
class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold",
div {
class: "pt-1",
{
date_current.format_localized(t!(
date_current.format_localized(
t!(
if date_current.year() == Local::now().year() {
"date-weekday-format"
} else {

View File

@@ -1,7 +1,12 @@
use crate::components::select_button::SelectButton;
use crate::models::category::Category;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::icons::fa_regular_icons::FaLightbulb;
use dioxus_free_icons::icons::fa_solid_icons::{
FaCalendarDays, FaHourglassHalf, FaInbox, FaSignsPost, FaWater,
};
#[component]
pub(crate) fn CategoryInput(
@@ -10,93 +15,51 @@ pub(crate) fn CategoryInput(
) -> Element {
rsx! {
div {
class: format!("flex flex-row gap-2 {}", class.unwrap_or("")),
button {
r#type: "button",
class: format!(
"py-2 rounded-lg grow basis-0 {} cursor-pointer",
if selected_category() == Category::SomedayMaybe { "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
class: format!("grid grid-cols-3 gap-3 {}", class.unwrap_or("")),
SelectButton {
icon: FaLightbulb,
is_selected: matches!(selected_category(), Category::SomedayMaybe),
on_select: move |_| {
selected_category.set(Category::SomedayMaybe);
},
i {
class: "fa-solid fa-question"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg grow basis-0 {} cursor-pointer",
if selected_category() == Category::LongTerm { "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
}
SelectButton {
icon: FaWater,
is_selected: matches!(selected_category(), Category::LongTerm),
on_select: move |_| {
selected_category.set(Category::LongTerm);
},
i {
class: "fa-solid fa-water"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg grow basis-0 {} cursor-pointer",
if let Category::WaitingFor(_) = selected_category() { "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
}
SelectButton {
icon: FaHourglassHalf,
is_selected: matches!(selected_category(), Category::WaitingFor(_)),
on_select: move |_| {
selected_category.set(Category::WaitingFor(String::new()));
},
i {
class: "fa-solid fa-hourglass-half"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg grow basis-0 {} cursor-pointer",
if selected_category() == Category::NextSteps { "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
}
SelectButton {
icon: FaSignsPost,
is_selected: matches!(selected_category(), Category::NextSteps),
on_select: move |_| {
selected_category.set(Category::NextSteps);
},
i {
class: "fa-solid fa-forward"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg grow basis-0 {} cursor-pointer",
if let Category::Calendar { .. } = selected_category() { "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
}
SelectButton {
icon: FaCalendarDays,
is_selected: matches!(selected_category(), Category::Calendar { .. }),
on_select: move |_| {
selected_category.set(Category::Calendar {
date: chrono::Local::now().date_naive(),
reoccurrence: None,
time: None,
});
},
i {
class: "fa-solid fa-calendar-days"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg grow basis-0 {} cursor-pointer",
if selected_category() == Category::Inbox { "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
}
SelectButton {
icon: FaInbox,
is_selected: matches!(selected_category(), Category::Inbox),
on_select: move |_| {
selected_category.set(Category::Inbox);
},
i {
class: "fa-solid fa-inbox"
}
}
}

View File

@@ -1,11 +1,12 @@
use crate::components::task_list::TaskList;
use crate::components::task_list_item::TaskListItem;
use crate::hooks::use_tasks_with_subtasks_in_category;
use crate::internationalization::LocaleFromLanguageIdentifier;
use crate::models::category::Category;
use crate::models::task::TaskWithSubtasks;
use chrono::Local;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_solid_icons::{FaCalendarCheck, FaCalendarXmark, FaWater};
use dioxus_i18n::t;
use dioxus_i18n::use_i18n::i18n;
use voca_rs::Voca;
@@ -45,34 +46,21 @@ pub(crate) fn CategoryTodayTaskList() -> Element {
rsx! {
div {
class: "pt-4 flex flex-col gap-8",
div {
class: "flex flex-col gap-4",
if !long_term_tasks.is_empty() {
div {
class: "px-8 flex flex-row items-center gap-2 font-bold",
i {
class: "fa-solid fa-water text-xl w-6 text-center"
}
class: "flex flex-col gap-4",
div {
class: "mt-1",
{t!("long-term")._upper_first()}
}
}
div {
for task in long_term_tasks {
div {
key: "{task.task.id}",
class: format!(
"px-8 pt-5 {} flex flex-row gap-4",
if task.task.deadline.is_some() {
"pb-0.5"
} else {
"pb-5"
}
),
TaskListItem {
task: task.clone()
}
class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold",
Icon {
class: "mx-1.5",
icon: FaWater
}
div {
{t!("long-term")._upper_first()}
}
}
TaskList {
tasks: long_term_tasks
}
}
}
@@ -80,12 +68,14 @@ pub(crate) fn CategoryTodayTaskList() -> Element {
div {
class: "flex flex-col gap-4",
div {
class: "px-8 flex flex-row items-center gap-2 font-bold",
i {
class: "fa-solid fa-calendar-xmark text-xl w-6 text-center"
class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold",
Icon {
class: "mx-1.25",
height: 22,
width: 22,
icon: FaCalendarXmark
}
div {
class: "mt-1",
{t!("overdue")._upper_first()}
}
}
@@ -98,12 +88,14 @@ pub(crate) fn CategoryTodayTaskList() -> Element {
div {
class: "flex flex-col gap-4",
div {
class: "px-8 flex flex-row items-center gap-2 font-bold",
i {
class: "fa-solid fa-calendar-check text-xl w-6 text-center"
class: "px-7 flex flex-row items-center gap-2 text-gray-500 font-bold",
Icon {
class: "mx-1.25",
height: 22,
width: 22,
icon: FaCalendarCheck
}
div {
class: "mt-1",
{
let format = t!("date-weekday-format");
let today_date = today_date.format_localized(

View File

@@ -0,0 +1,31 @@
use crate::components::project_form::PROJECT_BEING_EDITED;
use crate::components::{button_primary::ButtonPrimary, task_form::TASK_BEING_EDITED};
use crate::route::Route;
use dioxus::prelude::*;
use dioxus_free_icons::{Icon, icons::fa_solid_icons::FaGavel};
#[component]
pub(crate) fn CreateButton() -> Element {
let navigator = use_navigator();
let current_route = use_route();
rsx! {
ButtonPrimary {
class: "pointer-events-auto m-4 self-end *:rounded-full! *:p-4",
onclick: move |_| {
*TASK_BEING_EDITED.write() = None;
*PROJECT_BEING_EDITED.write() = None;
navigator.push(
match current_route {
Route::ProjectsPage => Route::ProjectFormPage,
_ => Route::TaskFormPage,
}
);
},
Icon {
icon: FaGavel,
height: 24,
width: 24
}
}
}
}

View File

@@ -1,6 +1,8 @@
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_solid_icons::FaTriangleExclamation;
#[component]
pub(crate) fn ErrorBoundaryMessage(children: Element, class: Option<String>) -> Element {
@@ -11,8 +13,11 @@ pub(crate) fn ErrorBoundaryMessage(children: Element, class: Option<String>) ->
div {
class: "grow flex flex-col justify-center items-center",
div {
i {
class: "text-3xl fa-solid fa-triangle-exclamation"
Icon {
class: "text-gray-500",
icon: FaTriangleExclamation,
height: 32,
width: 32
}
}
}

View File

@@ -1,25 +0,0 @@
use crate::models::project::Project;
use crate::models::task::Task;
use dioxus::prelude::*;
#[component]
pub(crate) fn FormOpenButton(opened: Signal<bool>) -> Element {
let mut project_being_edited = use_context::<Signal<Option<Project>>>();
let mut task_being_edited = use_context::<Signal<Option<Task>>>();
rsx! {
button {
class: "pointer-events-auto m-4 py-3 px-5 self-end text-center bg-zinc-300/50 rounded-xl border-t-zinc-200 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] text-2xl text-zinc-200 cursor-pointer",
onclick: move |_| {
if opened() {
project_being_edited.set(None);
task_being_edited.set(None);
}
opened.set(!opened());
},
i {
class: format!("min-w-6 fa-solid {}", if opened() { "fa-xmark" } else { "fa-plus" }),
}
}
}
}

47
src/components/input.rs Normal file
View File

@@ -0,0 +1,47 @@
use dioxus::prelude::*;
#[component]
pub(crate) fn Input(
class: Option<String>,
name: String,
r#type: String,
id: Option<String>,
#[props(extends = GlobalAttributes, extends = input)] attributes: Vec<Attribute>,
// TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/5271 gets resolved.
autofocus: Option<bool>,
// TODO: Remove this once https://github.com/DioxusLabs/dioxus/issues/4019 gets resolved.
oninput: Option<Callback<Event<FormData>>>,
onchange: Option<Callback<Event<FormData>>>,
) -> Element {
rsx! {
input {
class: format!(
"pt-3 pb-2.25 {} bg-gray-800-muted enabled:hover:bg-gray-800 enabled:focus:bg-gray-800 drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl outline-0 {} transition-all duration-150 {}",
match r#type.as_str() {
"date" => "ps-3.25 pe-3",
_ => "px-4"
},
match r#type.as_str() {
"text" | "number" => "",
_ => "enabled:cursor-pointer"
},
class.unwrap_or("".to_owned())
),
name: name.clone(),
r#type,
id: id.unwrap_or(format!("input_{}", name)),
autofocus,
oninput: move |event| {
if let Some(oninput) = oninput {
oninput.call(event);
}
},
onchange: move |event| {
if let Some(onchange) = oninput {
onchange.call(event);
}
},
..attributes
}
}
}

View File

@@ -0,0 +1,21 @@
use dioxus::prelude::*;
use dioxus_free_icons::{Icon, IconShape};
#[component]
pub(crate) fn InputLabel<I: IconShape + Clone + PartialEq + 'static>(
icon: I,
r#for: Option<String>,
) -> Element {
rsx! {
label {
r#for,
class: "mt-0.5 min-w-7 flex flex-row justify-center items-center",
Icon {
class: "text-gray-600",
icon,
height: 16,
width: 16
}
}
}
}

View File

@@ -1,16 +1,21 @@
pub(crate) mod app;
pub(crate) mod bottom_panel;
pub(crate) mod button_primary;
pub(crate) mod button_secondary;
pub(crate) mod category_calendar_task_list;
pub(crate) mod category_input;
pub(crate) mod category_today_task_list;
pub(crate) mod create_button;
pub(crate) mod error_boundary_message;
pub(crate) mod form_open_button;
pub(crate) mod input;
pub(crate) mod input_label;
pub(crate) mod navigation;
pub(crate) mod navigation_item;
pub(crate) mod project_form;
pub(crate) mod project_list;
pub(crate) mod project_select;
pub(crate) mod reoccurrence_input;
pub(crate) mod reoccurrence_interval_input;
pub(crate) mod select_button;
pub(crate) mod sticky_bottom;
pub(crate) mod subtasks_form;
pub(crate) mod task_form;

View File

@@ -1,78 +1,72 @@
use crate::components::navigation_item::NavigationItem;
use crate::route::Route;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_regular_icons::FaLightbulb;
use dioxus_free_icons::icons::fa_solid_icons::{
FaBars, FaCalendarDay, FaCalendarDays, FaHourglassHalf, FaInbox, FaList, FaSignsPost,
FaTrashCan, FaVolcano,
};
#[component]
pub(crate) fn Navigation(expanded: Signal<bool>) -> Element {
pub(crate) fn Navigation(is_expanded: Signal<bool>) -> Element {
rsx! {
div {
class: "grid grid-cols-5 justify-stretch",
button {
class: format!(
"py-4 text-center text-2xl {} cursor-pointer",
if expanded() { "text-zinc-200" }
else { "text-zinc-500" }
"py-2 flex flex-row justify-center items-center cursor-pointer",
),
onclick: move |_| expanded.set(!expanded()),
i {
class: "fa-solid fa-bars"
onclick: move |_| is_expanded.set(!is_expanded()),
div {
class: format!("pt-2.5 px-4 {} transition-all duration-150",
if is_expanded() { "pb-2 mt-1 bg-gray-900 text-gray-400 rounded-xl drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-950)]" }
else { "pb-3 bg-gray-800 rounded-xl drop-shadow-[0_0_0_var(--color-gray-950)] text-gray-600" }
),
Icon {
icon: FaBars,
height: 24,
width: 24
}
}
},
NavigationItem {
route: Route::CategoryNextStepsPage,
i {
class: "fa-solid fa-forward"
}
icon: FaSignsPost
},
NavigationItem {
route: Route::CategoryCalendarPage,
i {
class: "fa-solid fa-calendar-days"
}
icon: FaCalendarDays
},
NavigationItem {
route: Route::CategoryTodayPage,
i {
class: "fa-solid fa-calendar-day"
}
icon: FaCalendarDay
},
NavigationItem {
route: Route::CategoryInboxPage,
i {
class: "fa-solid fa-inbox"
}
icon: FaInbox
},
{if expanded() {
{if is_expanded() {
rsx! {
NavigationItem {
route: Route::ProjectsPage,
i {
class: "fa-solid fa-list"
}
icon: FaList
},
NavigationItem {
route: Route::CategoryTrashPage,
i {
class: "fa-solid fa-trash-can"
}
icon: FaTrashCan
},
NavigationItem {
route: Route::CategoryDonePage,
i {
class: "fa-solid fa-check"
}
icon: FaVolcano
},
NavigationItem {
route: Route::CategoryLongTermPage,
i {
class: "fa-solid fa-water"
}
route: Route::CategorySomedayMaybePage,
icon: FaLightbulb
},
NavigationItem {
route: Route::CategoryWaitingForPage,
i {
class: "fa-solid fa-hourglass-half"
}
icon: FaHourglassHalf
}
}
} else { VNode::empty() }}

View File

@@ -1,19 +1,31 @@
use crate::route::Route;
use dioxus::prelude::*;
use dioxus_free_icons::{Icon, IconShape};
#[component]
pub(crate) fn NavigationItem(route: Route, children: Element) -> Element {
pub(crate) fn NavigationItem<I: IconShape + Clone + PartialEq + 'static>(
route: Route,
icon: I,
) -> Element {
let current_route = use_route::<Route>();
rsx! {
Link {
to: route.clone(),
class: format!(
"py-4 text-center text-2xl {}",
if current_route == route { "text-zinc-200" }
else { "text-zinc-500" }
"py-2.5 flex flex-row justify-center items-center hover:*:bg-gray-900 active:*:text-gray-400",
),
children
div {
class: format!("pt-2.5 px-4 {} transition-all duration-150",
if current_route == route { "pb-2 mt-1 bg-gray-900 text-gray-400 rounded-xl drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-950)]" }
else { "pb-3 bg-gray-800 rounded-xl drop-shadow-[0_0_0_var(--color-gray-950)] text-gray-600" }
),
Icon {
icon,
height: 24,
width: 24
}
}
}
}
}

View File

@@ -1,73 +1,113 @@
use crate::components::button_primary::ButtonPrimary;
use crate::components::button_secondary::ButtonSecondary;
use crate::components::input::Input;
use crate::components::input_label::InputLabel;
use crate::models::project::Project;
use crate::server::projects::{create_project, delete_project, edit_project};
use dioxus::core_macro::{component, rsx};
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_solid_icons::{FaFeatherPointed, FaStamp, FaTrashCan, FaXmark};
pub(crate) static PROJECT_BEING_EDITED: GlobalSignal<Option<Project>> = Signal::global(|| None);
#[component]
pub(crate) fn ProjectForm(
project: Option<Project>,
on_successful_submit: EventHandler<()>,
) -> Element {
pub(crate) fn ProjectForm() -> Element {
let navigator = use_navigator();
let project = PROJECT_BEING_EDITED();
let project_for_submit = project.clone();
rsx! {
form {
class: "px-4 flex flex-col gap-4",
onsubmit: move |event| {
event.prevent_default();
let project = project_for_submit.clone();
async move {
let new_project = event.parsed_values().unwrap();
if let Some(project) = project {
let _ = edit_project(project.id, new_project).await;
let result = if let Some(project) = project {
edit_project(project.id, new_project).await
} else {
let _ = create_project(new_project).await;
create_project(new_project).await
};
if result.is_ok() {
navigator.go_back();
}
on_successful_submit.call(());
}
},
class: "p-4 flex flex-col gap-4",
id: "form_project",
div {
class: "flex flex-row items-center gap-3",
label {
r#for: "input_title",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-pen-clip text-zinc-400/50"
}
InputLabel {
icon: FaFeatherPointed,
r#for: "input_title"
}
input {
Input {
class: "grow",
name: "title",
required: true,
initial_value: project.as_ref().map(|project| project.title.to_owned()),
r#type: "text",
class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg",
id: "input_title"
initial_value: project.as_ref().map(|project| project.title.to_owned()),
}
}
div {
class: "flex flex-row justify-between mt-auto",
button {
r#type: "button",
class: "py-2 px-4 bg-zinc-300/50 rounded-lg cursor-pointer",
onclick: move |_| {
}
div {
class: "px-4 grid grid-cols-3 gap-3 mt-auto",
ButtonSecondary {
r#type: "button",
class: "grow",
onclick: {
let project = project.clone();
move |_| {
let project = project.clone();
async move {
if let Some(project) = project {
let _ = delete_project(project.id).await;
let result = delete_project(project.id).await;
if result.is_ok() {
/* TODO: Might not work on mobile due to
https://dioxuslabs.com/learn/0.7/essentials/router/navigation#history-buttons.
*/
navigator.go_back();
}
} else {
navigator.go_back();
}
}
}
},
Icon {
icon: FaTrashCan,
height: 16,
width: 16
}
}
if project.is_some() {
div {
class: "grow flex flex-col items-stretch",
GoBackButton {
ButtonSecondary {
/* TODO: Replace w-full` with proper flexbox styling once
https://github.com/DioxusLabs/dioxus/issues/5269 is solved. */
class: "w-full",
r#type: "button",
Icon {
icon: FaXmark,
height: 16,
width: 16
}
on_successful_submit.call(());
}
},
i {
class: "fa-solid fa-trash-can"
}
}
button {
r#type: "submit",
class: "py-2 px-4 bg-zinc-300/50 rounded-lg cursor-pointer",
i {
class: "fa-solid fa-floppy-disk"
}
} else {
div {}
}
ButtonPrimary {
form: "form_project",
r#type: "submit",
Icon {
icon: FaStamp,
height: 16,
width: 16
}
}
}

View File

@@ -1,24 +1,22 @@
use crate::{hooks::use_projects, models::project::Project};
use crate::route::Route;
use crate::{components::project_form::PROJECT_BEING_EDITED, hooks::use_projects};
use dioxus::prelude::*;
#[component]
pub(crate) fn ProjectList() -> Element {
let navigator = use_navigator();
let projects = use_projects()?;
let mut project_being_edited = use_context::<Signal<Option<Project>>>();
rsx! {
div {
class: "flex flex-col",
for project in projects {
div {
class: "px-7 py-4 hover:bg-gray-800 font-medium text-pretty wrap-anywhere select-none transition-all duration-150 cursor-pointer",
key: "{project.id}",
class: format!(
"px-8 py-4 select-none {}",
if project_being_edited().is_some_and(|p| p.id == project.id) {
"bg-zinc-700"
} else { "" }
),
onclick: move |_| project_being_edited.set(Some(project.clone())),
onclick: move |_| {
*PROJECT_BEING_EDITED.write() = Some(project.clone());
navigator.push(Route::ProjectFormPage);
},
{project.title.clone()}
}
}

View File

@@ -10,8 +10,8 @@ pub(crate) fn ProjectSelect(initial_selected_id: Option<i32>) -> Element {
rsx! {
select {
name: "project_id",
class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow cursor-pointer",
id: "input_project",
class: "px-4 pt-3 pb-2.25 bg-gray-800-muted enabled:hover:bg-gray-800 enabled:active:bg-gray-800 drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl grow cursor-pointer",
id: "input_project_id",
option {
value: 0,
{t!("none")}

View File

@@ -1,76 +0,0 @@
use crate::models::category::ReoccurrenceInterval;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
#[component]
pub(crate) fn ReoccurrenceIntervalInput(
reoccurrence_interval: Signal<Option<ReoccurrenceInterval>>,
class_buttons: Option<&'static str>,
) -> Element {
rsx! {
button {
r#type: "button",
class: format!(
"py-2 rounded-lg {} {} cursor-pointer",
class_buttons.unwrap_or(""),
if reoccurrence_interval().is_none() { "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
reoccurrence_interval.set(None);
},
i {
class: "fa-solid fa-ban"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg {} {} cursor-pointer",
class_buttons.unwrap_or(""),
if let Some(ReoccurrenceInterval::Day) = reoccurrence_interval()
{ "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
reoccurrence_interval.set(Some(ReoccurrenceInterval::Day))
},
i {
class: "fa-solid fa-sun"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg {} {} cursor-pointer",
class_buttons.unwrap_or(""),
if let Some(ReoccurrenceInterval::Month) = reoccurrence_interval()
{ "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
reoccurrence_interval.set(Some(ReoccurrenceInterval::Month))
},
i {
class: "fa-solid fa-moon"
}
},
button {
r#type: "button",
class: format!(
"py-2 rounded-lg {} {} cursor-pointer",
class_buttons.unwrap_or(""),
if let Some(ReoccurrenceInterval::Year) = reoccurrence_interval()
{ "bg-zinc-500/50" }
else { "bg-zinc-800/50" }
),
onclick: move |_| {
reoccurrence_interval.set(Some(ReoccurrenceInterval::Year))
},
i {
class: "fa-solid fa-earth-europe"
}
}
}
}

View File

@@ -0,0 +1,44 @@
use crate::components::select_button::SelectButton;
use crate::models::category::ReoccurrenceInterval;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::icons::fa_solid_icons::{FaBan, FaEarthEurope, FaMoon, FaSun};
#[component]
pub(crate) fn ReoccurrenceIntervalInput(
reoccurrence_interval: Signal<Option<ReoccurrenceInterval>>,
class_buttons: Option<&'static str>,
) -> Element {
rsx! {
// TODO: Abstract into SelectButton. Make it sank into the surface by default, like other inputs (abstract those too haha), and rise it up on selection (rationale: it will influence what is on the surface).
SelectButton {
icon: FaBan,
is_selected: reoccurrence_interval().is_none(),
on_select: move |_| {
reoccurrence_interval.set(None);
}
}
SelectButton {
icon: FaSun,
is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Day)),
on_select: move |_| {
reoccurrence_interval.set(Some(ReoccurrenceInterval::Day))
}
}
SelectButton {
icon: FaMoon,
is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Month)),
on_select: move |_| {
reoccurrence_interval.set(Some(ReoccurrenceInterval::Month));
}
}
SelectButton {
icon: FaEarthEurope,
is_selected: matches!(reoccurrence_interval(), Some(ReoccurrenceInterval::Year)),
on_select: move |_| {
reoccurrence_interval.set(Some(ReoccurrenceInterval::Year));
}
}
}
}

View File

@@ -0,0 +1,28 @@
use dioxus::prelude::*;
use dioxus_free_icons::{Icon, IconShape};
#[component]
pub(crate) fn SelectButton<I: IconShape + Clone + PartialEq + 'static>(
icon: I,
is_selected: bool,
on_select: Callback,
) -> Element {
rsx! {
button {
r#type: "button",
class: format!(
"pt-4.5 flex flex-row justify-center items-center {} rounded-xl transition-all duration-150",
if is_selected { "pb-3.75 bg-gray-900 drop-shadow-[0_0_0_var(--color-gray-900-muted)]" }
else { "pb-2.75 mt-1 bg-gray-800-muted hover:bg-gray-800 drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] text-gray-400 cursor-pointer" }
),
onclick: move |_| {
on_select.call(());
},
Icon {
icon,
height: 16,
width: 16
}
},
}
}

View File

@@ -1,3 +1,6 @@
use crate::components::button_secondary::ButtonSecondary;
use crate::components::input::Input;
use crate::components::input_label::InputLabel;
use crate::hooks::use_subtasks_of_task;
use crate::models::subtask::NewSubtask;
use crate::models::task::Task;
@@ -5,131 +8,132 @@ use crate::server::subtasks::{create_subtask, delete_subtask, edit_subtask};
use dioxus::core_macro::{component, rsx};
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_solid_icons::{FaGavel, FaListCheck, FaTrashCan};
#[component]
pub(crate) fn SubtasksForm(task: Task) -> Element {
let subtasks = use_subtasks_of_task(task.id)?;
let mut new_title = use_signal(String::new);
rsx! {
form {
class: "flex flex-row items-center gap-3",
onsubmit: move |event| {
event.prevent_default();
let task = task.clone();
async move {
let new_subtask = NewSubtask {
task_id: task.id,
title: event.get("title").first().cloned().and_then(|value| match value {
FormValue::Text(value) => Some(value),
FormValue::File(_) => None
}).unwrap(),
is_completed: false
};
let _ = create_subtask(new_subtask).await;
new_title.set(String::new());
}
},
label {
r#for: "input_new_title",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-list-check text-zinc-400/50"
}
}
div {
class: "grow grid grid-cols-6 gap-2",
input {
name: "title",
required: true,
value: new_title,
r#type: "text",
class: "grow py-2 px-3 col-span-5 bg-zinc-800/50 rounded-lg",
id: "input_new_title",
onchange: move |event| new_title.set(event.value())
}
button {
r#type: "submit",
class: "py-2 col-span-1 bg-zinc-800/50 rounded-lg",
i {
class: "fa-solid fa-plus"
}
}
}
}
for subtask in subtasks {
div {
key: "{subtask.id}",
div {
class: "flex flex-col gap-3",
form {
class: "flex flex-row items-center gap-3",
i {
class: format!(
"{} min-w-6 text-center text-2xl text-zinc-400/50",
if subtask.is_completed {
"fa solid fa-square-check"
} else {
"fa-regular fa-square"
}
),
onclick: {
let subtask = subtask.clone();
move |_| {
let subtask = subtask.clone();
async move {
let new_subtask = NewSubtask {
task_id: subtask.task_id,
title: subtask.title.clone(),
is_completed: !subtask.is_completed
};
let _ = edit_subtask(
subtask.id,
new_subtask
).await;
}
}
onsubmit: move |event| {
event.prevent_default();
let task = task.clone();
async move {
let new_subtask = NewSubtask {
task_id: task.id,
title: event.get("new_title").first().cloned().and_then(|value| match value {
FormValue::Text(value) => Some(value),
FormValue::File(_) => None
}).unwrap(),
is_completed: false
};
let _ = create_subtask(new_subtask).await;
new_title.set(String::new());
}
},
InputLabel {
icon: FaListCheck,
r#for: "input_new_title"
}
div {
class: "grow grid grid-cols-6 gap-2",
input {
class: "grow flex flex-row items-end gap-3",
Input {
class: "grow",
name: "new_title",
r#type: "text",
class: "grow py-2 px-3 col-span-5 bg-zinc-800/50 rounded-lg",
id: "input_title_{subtask.id}",
initial_value: subtask.title.clone(),
onchange: {
let subtask = subtask.clone();
move |event: Event<FormData>| {
let subtask = subtask.clone();
async move {
let new_subtask = NewSubtask {
task_id: subtask.task_id,
title: event.value(),
is_completed: subtask.is_completed
};
if new_subtask.title.is_empty() {
let _ = delete_subtask(subtask.id).await;
} else {
let _ = edit_subtask(
subtask.id,
new_subtask
).await;
}
}
}
required: true,
value: new_title,
onchange: move |event: Event<FormData>| new_title.set(event.value())
}
ButtonSecondary {
r#type: "submit",
Icon {
icon: FaGavel,
height: 16,
width: 16
}
}
}
}
for subtask in subtasks {
div {
key: "{subtask.id}",
class: "flex flex-row items-center gap-3",
button {
r#type: "button",
class: "py-2 col-span-1 bg-zinc-800/50 rounded-lg",
class: "mt-1.5 hover:mt-1 hover:pb-0.5 min-w-7 cursor-pointer transition-all duration-150",
onclick: {
let subtask = subtask.clone();
move |_| {
let subtask = subtask.clone();
async move {
let _ = delete_subtask(subtask.id).await;
let new_subtask = NewSubtask {
task_id: subtask.task_id,
title: subtask.title.clone(),
is_completed: !subtask.is_completed
};
let _ = edit_subtask(
subtask.id,
new_subtask
).await;
}
}
},
i {
class: "fa-solid fa-trash-can"
div {
class: format!("grow h-7 w-7 mb-[4px] drop-shadow-[0_1px_0_var(--color-gray-800),0_1px_0_var(--color-gray-800),0_1px_0_var(--color-gray-800),0_1px_0_var(--color-gray-800)] rounded-full {}",
if subtask.is_completed {"bg-gray-600"} else {"border-3 border-gray-600"}
)
}
}
div {
class: "grow flex flex-row items-end gap-3",
Input {
class: "grow",
name: "title_edit_{subtask.id}",
r#type: "text",
initial_value: subtask.title.clone(),
onchange: {
let subtask = subtask.clone();
move |event: Event<FormData>| {
let subtask = subtask.clone();
async move {
let new_subtask = NewSubtask {
task_id: subtask.task_id,
title: event.value(),
is_completed: subtask.is_completed
};
if new_subtask.title.is_empty() {
let _ = delete_subtask(subtask.id).await;
} else {
let _ = edit_subtask(
subtask.id,
new_subtask
).await;
}
}
}
}
}
ButtonSecondary {
r#type: "button",
onclick: {
let subtask = subtask.clone();
move |_| {
let subtask = subtask.clone();
async move {
let _ = delete_subtask(subtask.id).await;
}
}
},
Icon {
icon: FaTrashCan,
height: 16,
width: 16
}
}
}
}

View File

@@ -1,16 +1,24 @@
use crate::components::button_primary::ButtonPrimary;
use crate::components::button_secondary::ButtonSecondary;
use crate::components::category_input::CategoryInput;
use crate::components::input::Input;
use crate::components::input_label::InputLabel;
use crate::components::project_select::ProjectSelect;
use crate::components::reoccurrence_input::ReoccurrenceIntervalInput;
use crate::components::reoccurrence_interval_input::ReoccurrenceIntervalInput;
use crate::components::subtasks_form::SubtasksForm;
use crate::models::category::{CalendarTime, Category, Reoccurrence};
use crate::models::task::NewTask;
use crate::models::task::Task;
use crate::route::Route;
use crate::server::tasks::{create_task, delete_task, edit_task};
use chrono::Duration;
use dioxus::core_macro::{component, rsx};
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_solid_icons::{
FaBell, FaBomb, FaClock, FaFeatherPointed, FaHourglassEnd, FaList, FaRepeat, FaScroll, FaStamp,
FaTrashCan, FaXmark,
};
use dioxus_i18n::t;
use serde::{Deserialize, Serialize};
@@ -46,25 +54,19 @@ struct InputData {
project_id: Option<String>,
}
pub(crate) static TASK_BEING_EDITED: GlobalSignal<Option<Task>> = Signal::global(|| None);
pub(crate) static LATEST_VISITED_CATEGORY: GlobalSignal<Category> =
Signal::global(|| Category::Inbox);
#[component]
pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()>) -> Element {
let route = use_route::<Route>();
pub(crate) fn TaskForm() -> Element {
let navigator = use_navigator();
let task = TASK_BEING_EDITED();
let selected_category = use_signal(|| {
if let Some(task) = &task {
task.category.clone()
} else {
match route {
Route::CategorySomedayMaybePage => Category::SomedayMaybe,
Route::CategoryWaitingForPage => Category::WaitingFor(String::new()),
Route::CategoryNextStepsPage => Category::NextSteps,
Route::CategoryCalendarPage | Route::CategoryTodayPage => Category::Calendar {
date: chrono::Local::now().date_naive(),
reoccurrence: None,
time: None,
},
Route::CategoryLongTermPage => Category::LongTerm,
_ => Category::Inbox,
}
LATEST_VISITED_CATEGORY()
}
});
let category_calendar_reoccurrence_interval = use_signal(|| {
@@ -104,9 +106,9 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
rsx! {
div {
class: "p-4 flex flex-col gap-4",
class: "grow px-4 flex flex-col gap-6.5",
form {
class: "flex flex-col gap-4",
class: "flex flex-col gap-8",
id: "form_task",
onsubmit: move |event| {
event.prevent_default();
@@ -148,50 +150,45 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
project_id: input_data.project_id
.and_then(|deadline| deadline.parse().ok()).filter(|&id| id > 0),
};
if let Some(task) = task {
let _ = edit_task(task.id, new_task).await;
let result = if let Some(task) = task {
edit_task(task.id, new_task).await
} else {
let _ = create_task(new_task).await;
create_task(new_task).await
};
if result.is_ok() {
navigator.go_back();
}
on_successful_submit.call(());
}
},
div {
class: "flex flex-row items-center gap-3",
label {
InputLabel {
r#for: "input_title",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-pen-clip text-zinc-400/50"
},
icon: FaFeatherPointed
},
input {
Input {
class: "grow",
name: "title",
required: true,
initial_value: task.as_ref().map(|task| task.title.clone()),
r#type: "text",
class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg",
id: "input_title"
},
autofocus: true
}
},
div {
class: "flex flex-row items-center gap-3",
label {
r#for: "input_project",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-list text-zinc-400/50"
}
InputLabel {
r#for: "input_project_id",
icon: FaList
},
SuspenseBoundary {
fallback: |_| {
rsx ! {
select {
class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow cursor-pointer",
class: "px-4 pt-3 pb-2.25 bg-gray-800-muted drop-shadow-[0_calc(0px_-_var(--spacing))_0_var(--color-gray-900-muted)] rounded-xl grow cursor-pointer",
option {
value: 0,
{t!("none")}
},
value: 0
}
}
}
},
@@ -202,86 +199,69 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
},
div {
class: "flex flex-row items-center gap-3",
label {
r#for: "input_deadline",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-bomb text-zinc-400/50"
}
InputLabel {
icon: FaBomb,
r#for: "input_deadline"
},
input {
Input {
name: "deadline",
initial_value: task.as_ref().and_then(|task| task.deadline)
.map(|deadline| deadline.format("%Y-%m-%d").to_string()),
r#type: "date",
class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow basis-0 cursor-pointer",
id: "input_deadline"
class: "grow basis-0"
}
},
div {
class: "flex flex-row items-center gap-3",
label {
class: "min-w-6 text-center",
i {
class: "fa-solid fa-layer-group text-zinc-400/50"
}
InputLabel {
icon: FaScroll
},
CategoryInput {
selected_category: selected_category,
class: "grow"
class: "grow",
selected_category: selected_category
}
}
match selected_category() {
Category::WaitingFor(waiting_for) => rsx! {
div {
class: "flex flex-row items-center gap-3",
label {
r#for: "input_deadline",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-hourglass-end text-zinc-400/50"
}
InputLabel {
icon: FaHourglassEnd,
r#for: "input_category_waiting_for",
},
input {
Input {
class: "grow",
name: "category_waiting_for",
required: true,
initial_value: waiting_for,
r#type: "text",
class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow",
id: "input_category_waiting_for"
},
}
}
},
Category::Calendar { date, reoccurrence, time } => rsx! {
div {
class: "flex flex-row items-center gap-3",
label {
r#for: "input_category_calendar_date",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-clock text-zinc-400/50"
}
InputLabel {
icon: FaClock,
r#for: "input_category_calendar_date"
},
div {
class: "grow flex flex-row gap-2",
input {
r#type: "date",
class: "grow flex flex-row gap-3",
Input {
class: "grow",
name: "category_calendar_date",
r#type: "date",
required: true,
initial_value: date.format("%Y-%m-%d").to_string(),
class:
"py-2 px-3 bg-zinc-800/50 rounded-lg grow cursor-pointer",
id: "input_category_calendar_date"
},
input {
r#type: "time",
Input {
class: "grow",
name: "category_calendar_time",
r#type: "time",
initial_value: time.map(|calendar_time|
calendar_time.time.format("%H:%M").to_string()
),
class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow cursor-pointer",
id: "input_category_calendar_time",
oninput: move |event| {
oninput: move |event: Event<FormData>| {
category_calendar_has_time.set(!event.value().is_empty());
}
}
@@ -289,54 +269,46 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
},
div {
class: "flex flex-row items-center gap-3",
label {
r#for: "category_calendar_reoccurrence_length",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-repeat text-zinc-400/50"
}
InputLabel {
icon: FaRepeat,
r#for: "category_calendar_reoccurrence_length"
},
div {
class: "grow grid grid-cols-6 gap-2",
class: "grow grid grid-cols-5 items-end gap-3",
ReoccurrenceIntervalInput {
reoccurrence_interval: category_calendar_reoccurrence_interval
},
input {
Input {
class: "text-right",
r#type: "number",
inputmode: "numeric",
name: "category_calendar_reoccurrence_length",
disabled: category_calendar_reoccurrence_interval().is_none(),
required: true,
min: 1,
min: "1",
initial_value: category_calendar_reoccurrence_interval().map_or(
String::new(),
|_| reoccurrence.map_or(1, |reoccurrence|
reoccurrence.length).to_string()
),
class: "py-2 px-3 bg-zinc-800/50 rounded-lg col-span-2 text-right",
id: "category_calendar_reoccurrence_length"
)
}
}
},
if category_calendar_has_time() {
div {
class: "flex flex-row items-center gap-3",
label {
r#for: "category_calendar_reminder_offset_index",
class: "min-w-6 text-center",
i {
class: "fa-solid fa-bell text-zinc-400/50"
}
InputLabel {
r#for: "input_category_calendar_reminder_offset_index",
icon: FaBell
},
input {
r#type: "range",
class: "grow",
name: "category_calendar_reminder_offset_index",
r#type: "range",
min: 0,
max: REMINDER_OFFSETS.len() as i64 - 1,
initial_value: category_calendar_reminder_offset_index()
.to_string(),
class: "grow input-range-reverse cursor-pointer",
id: "category_calendar_has_reminder",
oninput: move |event| {
category_calendar_reminder_offset_index.set(
event.value().parse().unwrap()
@@ -344,8 +316,8 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
}
},
label {
r#for: "category_calendar_reminder_offset_index",
class: "pr-3 min-w-16 text-right",
r#for: "category_calendar_reminder_offset_index",
{REMINDER_OFFSETS[category_calendar_reminder_offset_index()]
.map(
|offset| if offset.num_hours() < 1 {
@@ -371,17 +343,20 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
}
}
}
div {
class: "flex flex-row justify-between mt-auto",
button {
r#type: "button",
class: "py-2 px-4 bg-zinc-300/50 rounded-lg cursor-pointer",
onclick: move |_| {
}
div {
class: "px-4 grid grid-cols-3 gap-3 mt-auto",
ButtonSecondary {
r#type: "button",
class: "grow",
onclick: {
let task = task.clone();
move |_| {
let task = task.clone();
async move {
if let Some(task) = task {
if let Category::Trash = task.category {
let _ = delete_task(task.id).await;
let result = if let Category::Trash = task.category {
delete_task(task.id).await
} else {
let new_task = NewTask {
title: task.title.to_owned(),
@@ -389,23 +364,53 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
category: Category::Trash,
project_id: task.project_id
};
let _ = edit_task(task.id, new_task).await;
edit_task(task.id, new_task).await.map(|_| ())
};
if result.is_ok() {
/* TODO: Might not work on mobile due to
https://dioxuslabs.com/learn/0.7/essentials/router/navigation#history-buttons.
*/
navigator.go_back();
}
} else {
navigator.go_back();
}
}
}
},
Icon {
icon: FaTrashCan,
height: 16,
width: 16
}
}
if task.is_some() {
div {
class: "grow flex flex-col items-stretch",
GoBackButton {
ButtonSecondary {
/* TODO: Replace w-full` with proper flexbox styling once
https://github.com/DioxusLabs/dioxus/issues/5269 is solved. */
class: "w-full",
r#type: "button",
Icon {
icon: FaXmark,
height: 16,
width: 16
}
on_successful_submit.call(());
}
},
i {
class: "fa-solid fa-trash-can"
}
}
button {
form: "form_task",
r#type: "submit",
class: "py-2 px-4 bg-zinc-300/50 rounded-lg cursor-pointer",
i {
class: "fa-solid fa-floppy-disk"
}
} else {
div {}
}
ButtonPrimary {
form: "form_task",
r#type: "submit",
Icon {
icon: FaStamp,
height: 16,
width: 16
}
}
}

View File

@@ -1,6 +1,8 @@
use crate::components::task_form::TASK_BEING_EDITED;
use crate::components::task_list_item::TaskListItem;
use crate::models::category::Category;
use crate::models::task::{Task, TaskWithSubtasks};
use crate::models::task::TaskWithSubtasks;
use crate::route::Route;
use crate::server::tasks::complete_task;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
@@ -8,7 +10,7 @@ use dioxus::prelude::*;
#[component]
pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>) -> Element {
let mut task_being_edited = use_context::<Signal<Option<Task>>>();
let navigator = use_navigator();
rsx! {
div {
class: format!("flex flex-col {}", class.unwrap_or("")),
@@ -16,34 +18,31 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
div {
key: "{task.task.id}",
class: format!(
"px-8 pt-4 {} flex flex-row gap-4 select-none {}",
"px-7 pt-3.75 {} flex flex-row items-start gap-4 hover:bg-gray-800 cursor-pointer select-none transition-all duration-150",
if task.task.deadline.is_some() || !task.subtasks.is_empty() {
"pb-0.5"
"pb-0.25"
} else if let Category::Calendar { time, .. } = &task.task.category {
if time.is_some() {
"pb-0.5"
"pb-0.25"
} else {
"pb-4"
"pb-3.75"
}
} else {
"pb-4"
"pb-3.75"
},
if task_being_edited().is_some_and(|t| t.id == task.task.id) {
"bg-zinc-700"
} else { "" }
),
onclick: {
let task = task.clone();
move |_| task_being_edited.set(Some(task.task.clone()))
move |_| {
*TASK_BEING_EDITED.write() = Some(task.task.clone());
navigator.push(Route::TaskFormPage);
}
},
i {
button {
class: format!(
"{} text-3xl align-middle h-9 text-zinc-500",
if let Category::Done = task.task.category {
"fa solid fa-square-check"
} else {
"fa-regular fa-square cursor-pointer"
}
"mt-0.5 hover:mt-0 hover:pb-0.5 transition-all duration-150 cursor-pointer {}",
if let Category::Done = task.task.category { "pointer-events-none" }
else { "" }
),
onclick: {
move |event: Event<MouseData>| {
@@ -53,10 +52,22 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
let _ = complete_task(task.task.id).await;
}
}
},
div {
class: format!("h-8 w-8 rounded-full {}",
if let Category::Done = task.task.category {
"mt-[3px] mb-[2px] bg-amber-300-muted"
} else {
"mb-[5px] border-3 border-amber-300-muted drop-shadow-[0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted),0_1px_0_var(--color-amber-700-muted)]"
}
)
}
},
TaskListItem {
task: task.clone()
div {
class: "mt-1.5",
TaskListItem {
task: task.clone()
}
}
}
}

View File

@@ -5,6 +5,8 @@ use chrono::{Datelike, Local};
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_solid_icons::{FaBomb, FaClock, FaListCheck};
use dioxus_i18n::prelude::i18n;
use dioxus_i18n::t;
use voca_rs::Voca;
@@ -14,19 +16,21 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
let today_date = Local::now().date_naive();
rsx! {
div {
class: "flex flex-col",
class: "pt-0.75 flex flex-col",
div {
class: "mt-1 grow font-medium",
class: "grow font-medium text-pretty wrap-anywhere",
{task.task.title}
},
div {
class: "flex flex-row gap-4",
if let Some(deadline) = task.task.deadline {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-bomb"
},
class: "flex flex-row items-center gap-1 text-sm text-gray-500",
Icon {
icon: FaBomb,
height: 14,
width: 14
}
{
format!(
" {}",
@@ -72,10 +76,12 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
if let Category::Calendar { time, .. } = task.task.category {
if let Some(calendar_time) = time {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-clock"
},
class: "flex flex-row items-center gap-1 text-sm text-gray-500",
Icon {
icon: FaClock,
height: 14,
width: 14
}
{
let format = t!("time-format");
format!(" {}", calendar_time.time.format(format.as_str()))
@@ -85,10 +91,12 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
}
if !task.subtasks.is_empty() {
div {
class: "text-sm text-zinc-400",
i {
class: "fa-solid fa-list-check"
},
class: "flex flex-row items-center gap-1 text-sm text-gray-500",
Icon {
icon: FaListCheck,
height: 14,
width: 14
}
{format!(
" {}/{}",
task.subtasks.iter()

View File

@@ -1,47 +0,0 @@
use crate::components::bottom_panel::BottomPanel;
use crate::components::form_open_button::FormOpenButton;
use crate::components::sticky_bottom::StickyBottom;
use crate::models::project::Project;
use crate::models::task::Task;
use crate::route::Route;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
#[component]
pub(crate) fn Main() -> Element {
let mut display_form = use_signal(|| false);
let project_being_edited =
use_context_provider::<Signal<Option<Project>>>(|| Signal::new(None));
let task_being_edited = use_context_provider::<Signal<Option<Task>>>(|| Signal::new(None));
use_effect(move || {
display_form.set(project_being_edited().is_some() || task_being_edited().is_some());
});
rsx! {
SuspenseBoundary {
fallback: |_| {
rsx! {
div {
class: "grow flex flex-col justify-center items-center",
div {
i {
class: "text-3xl fa-solid fa-cog fa-spin"
}
}
}
}
},
Outlet::<Route> {}
}
StickyBottom {
FormOpenButton {
opened: display_form,
}
BottomPanel {
display_form: display_form,
}
}
}
}

View File

@@ -1,2 +1,2 @@
mod main;
pub(crate) use main::Main;
pub(crate) mod navigation;
pub(crate) mod suspense;

38
src/layouts/navigation.rs Normal file
View File

@@ -0,0 +1,38 @@
use crate::components::bottom_panel::BottomPanel;
use crate::components::create_button::CreateButton;
use crate::components::sticky_bottom::StickyBottom;
use crate::components::task_form::LATEST_VISITED_CATEGORY;
use crate::models::category::Category;
use crate::route::Route;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
#[component]
pub(crate) fn Navigation() -> Element {
let current_route = use_route();
use_effect(use_reactive(&current_route, move |current_route| {
*LATEST_VISITED_CATEGORY.write() = match current_route {
Route::CategorySomedayMaybePage => Category::SomedayMaybe,
Route::CategoryWaitingForPage => Category::WaitingFor(String::new()),
Route::CategoryNextStepsPage => Category::NextSteps,
Route::CategoryCalendarPage | Route::CategoryTodayPage => Category::Calendar {
date: chrono::Local::now().date_naive(),
reoccurrence: None,
time: None,
},
_ => Category::Inbox,
};
}));
rsx! {
div {
class: "grow flex flex-col pb-36",
Outlet::<Route> {}
}
StickyBottom {
CreateButton {},
BottomPanel {}
}
}
}

28
src/layouts/suspense.rs Normal file
View File

@@ -0,0 +1,28 @@
use crate::route::Route;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
use dioxus_free_icons::Icon;
use dioxus_free_icons::icons::fa_solid_icons::FaCog;
#[component]
pub(crate) fn Suspense() -> Element {
rsx! {
SuspenseBoundary {
fallback: |_| {
rsx! {
div {
class: "grow flex flex-col justify-center items-center",
Icon {
class: "text-gray-500 animate-[spin_3000ms_linear_infinite]",
icon: FaCog,
height: 32,
width: 32
}
}
}
},
Outlet::<Route> {}
}
}
}

View File

@@ -2,14 +2,15 @@ use crate::layouts;
use crate::views::category_calendar_page::CategoryCalendarPage;
use crate::views::category_done_page::CategoryDonePage;
use crate::views::category_inbox_page::CategoryInboxPage;
use crate::views::category_long_term_page::CategoryLongTermPage;
use crate::views::category_next_steps_page::CategoryNextStepsPage;
use crate::views::category_someday_maybe_page::CategorySomedayMaybePage;
use crate::views::category_today_page::CategoryTodayPage;
use crate::views::category_trash_page::CategoryTrashPage;
use crate::views::category_waiting_for_page::CategoryWaitingForPage;
use crate::views::not_found_page::NotFoundPage;
use crate::views::project_form_page::ProjectFormPage;
use crate::views::projects_page::ProjectsPage;
use crate::views::task_form_page::TaskFormPage;
use dioxus::prelude::*;
// All variants have the same postfix because they have to match the component names.
@@ -17,28 +18,33 @@ use dioxus::prelude::*;
#[derive(Clone, Routable, Debug, PartialEq)]
#[rustfmt::skip]
pub(crate) enum Route {
#[layout(layouts::Main)]
#[redirect("/", || Route::CategoryTodayPage {})]
#[route("/today")]
CategoryTodayPage,
#[route("/inbox")]
CategoryInboxPage,
#[route("/someday-maybe")]
CategorySomedayMaybePage,
#[route("/waiting-for")]
CategoryWaitingForPage,
#[route("/next-steps")]
CategoryNextStepsPage,
#[route("/calendar")]
CategoryCalendarPage,
#[route("/long-term")]
CategoryLongTermPage,
#[route("/done")]
CategoryDonePage,
#[route("/trash")]
CategoryTrashPage,
#[route("/projects")]
ProjectsPage,
#[layout(layouts::navigation::Navigation)]
#[layout(layouts::suspense::Suspense)]
#[route("/today")]
CategoryTodayPage,
#[route("/inbox")]
CategoryInboxPage,
#[route("/someday-maybe")]
CategorySomedayMaybePage,
#[route("/waiting-for")]
CategoryWaitingForPage,
#[route("/next-steps")]
CategoryNextStepsPage,
#[route("/calendar")]
CategoryCalendarPage,
#[route("/done")]
CategoryDonePage,
#[route("/trash")]
CategoryTrashPage,
#[route("/projects")]
ProjectsPage,
#[end_layout]
#[end_layout]
#[layout(layouts::suspense::Suspense)]
#[route("/task")]
TaskFormPage,
#[route("/project")]
ProjectFormPage,
#[end_layout]
#[redirect("/", || Route::CategoryTodayPage)]
#[route("/:..route")]

View File

@@ -1,17 +0,0 @@
use crate::components::error_boundary_message::ErrorBoundaryMessage;
use crate::models::category::Category;
use crate::views::category_page::CategoryPage;
use dioxus::core_macro::rsx;
use dioxus::dioxus_core::Element;
use dioxus::prelude::*;
#[component]
pub(crate) fn CategoryLongTermPage() -> Element {
rsx! {
ErrorBoundaryMessage {
CategoryPage {
category: Category::LongTerm,
}
}
}
}

View File

@@ -11,8 +11,7 @@ pub(crate) fn CategoryPage(category: Category) -> Element {
rsx! {
TaskList {
tasks: tasks.clone(),
class: "pb-36"
tasks: tasks.clone()
}
}
}

View File

@@ -1,7 +1,6 @@
pub(crate) mod category_calendar_page;
pub(crate) mod category_done_page;
pub(crate) mod category_inbox_page;
pub(crate) mod category_long_term_page;
pub(crate) mod category_next_steps_page;
pub(crate) mod category_page;
pub(crate) mod category_someday_maybe_page;
@@ -9,4 +8,6 @@ pub(crate) mod category_today_page;
pub(crate) mod category_trash_page;
pub(crate) mod category_waiting_for_page;
pub(crate) mod not_found_page;
pub(crate) mod project_form_page;
pub(crate) mod projects_page;
pub(crate) mod task_form_page;

View File

@@ -0,0 +1,12 @@
use crate::components::{error_boundary_message::ErrorBoundaryMessage, project_form::ProjectForm};
use dioxus::prelude::*;
#[component]
pub(crate) fn ProjectFormPage() -> Element {
rsx! {
ErrorBoundaryMessage {
class: "grow py-4 flex flex-col gap-12",
ProjectForm {}
}
}
}

View File

@@ -0,0 +1,12 @@
use crate::components::{error_boundary_message::ErrorBoundaryMessage, task_form::TaskForm};
use dioxus::prelude::*;
#[component]
pub(crate) fn TaskFormPage() -> Element {
rsx! {
ErrorBoundaryMessage {
class: "grow py-4 flex flex-col gap-12",
TaskForm {}
}
}
}

View File

@@ -1,2 +1,27 @@
/* stylelint-disable-next-line import-notation */
@import "tailwindcss";
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("/assets/fonts/inter_variable.woff2") format("woff2");
}
@font-face {
font-family: Inter;
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url("/assets/fonts/inter_variable_italic.woff2") format("woff2");
}
/* stylelint-disable-next-line */
@theme {
--font-sans: "Inter", "sans";
--color-amber-300-muted: #b89a2e;
--color-amber-700-muted: #80390b;
--color-gray-800-muted: #141d2d;
--color-gray-900-muted: #0b111f;
}