<
This commit is contained in:
parent
e683323b82
commit
6262019d26
2378
Cargo.lock
generated
Normal file
2378
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
118
Cargo.toml
Normal file
118
Cargo.toml
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
[package]
|
||||||
|
name = "share-web"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = { version = "0.7", optional = true }
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
leptos = { version = "0.6", features = ["nightly"] }
|
||||||
|
leptos_axum = { version = "0.6", optional = true }
|
||||||
|
leptos_meta = { version = "0.6", features = ["nightly"] }
|
||||||
|
leptos_router = { version = "0.6", features = ["nightly"] }
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
|
||||||
|
tower = { version = "0.4", optional = true, features = ["util"] }
|
||||||
|
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||||
|
wasm-bindgen = "=0.2.93"
|
||||||
|
thiserror = "1"
|
||||||
|
tracing = { version = "0.1", optional = true }
|
||||||
|
http = "1"
|
||||||
|
web-sys = { version = "0.3", features = ["MouseEvent"] }
|
||||||
|
rand = "0.8"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||||
|
ssr = [
|
||||||
|
"dep:axum",
|
||||||
|
"dep:tokio",
|
||||||
|
"dep:tower",
|
||||||
|
"dep:tower-http",
|
||||||
|
"dep:leptos_axum",
|
||||||
|
"leptos/ssr",
|
||||||
|
"leptos_meta/ssr",
|
||||||
|
"leptos_router/ssr",
|
||||||
|
"dep:tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Defines a size-optimized profile for the WASM bundle in release mode
|
||||||
|
[profile.wasm-release]
|
||||||
|
inherits = "release"
|
||||||
|
opt-level = 'z'
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[package.metadata.leptos]
|
||||||
|
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
|
||||||
|
output-name = "share-web"
|
||||||
|
|
||||||
|
# IMPORTANT! Remove or comment-out
|
||||||
|
# style-file = "style/main.scss"
|
||||||
|
|
||||||
|
# Activates the tailwind build
|
||||||
|
tailwind-input-file = "style/tailwind.css"
|
||||||
|
tailwind-config-file = "tailwind.config.js"
|
||||||
|
|
||||||
|
|
||||||
|
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
|
||||||
|
site-root = "target/site"
|
||||||
|
|
||||||
|
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
|
||||||
|
# Defaults to pkg
|
||||||
|
site-pkg-dir = "pkg"
|
||||||
|
|
||||||
|
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
|
||||||
|
style-file = "style/main.scss"
|
||||||
|
# Assets source dir. All files found here will be copied and synchronized to site-root.
|
||||||
|
# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.
|
||||||
|
#
|
||||||
|
# Optional. Env: LEPTOS_ASSETS_DIR.
|
||||||
|
assets-dir = "public"
|
||||||
|
|
||||||
|
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
|
||||||
|
site-addr = "127.0.0.1:3000"
|
||||||
|
|
||||||
|
# The port to use for automatic reload monitoring
|
||||||
|
reload-port = 3001
|
||||||
|
|
||||||
|
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
|
||||||
|
# [Windows] for non-WSL use "npx.cmd playwright test"
|
||||||
|
# This binary name can be checked in Powershell with Get-Command npx
|
||||||
|
end2end-cmd = "npx playwright test"
|
||||||
|
end2end-dir = "end2end"
|
||||||
|
|
||||||
|
# The browserlist query used for optimizing the CSS.
|
||||||
|
browserquery = "defaults"
|
||||||
|
|
||||||
|
# The environment Leptos will run in, usually either "DEV" or "PROD"
|
||||||
|
env = "DEV"
|
||||||
|
|
||||||
|
# The features to use when compiling the bin target
|
||||||
|
#
|
||||||
|
# Optional. Can be over-ridden with the command line parameter --bin-features
|
||||||
|
bin-features = ["ssr"]
|
||||||
|
|
||||||
|
# If the --no-default-features flag should be used when compiling the bin target
|
||||||
|
#
|
||||||
|
# Optional. Defaults to false.
|
||||||
|
bin-default-features = false
|
||||||
|
|
||||||
|
# The features to use when compiling the lib target
|
||||||
|
#
|
||||||
|
# Optional. Can be over-ridden with the command line parameter --lib-features
|
||||||
|
lib-features = ["hydrate"]
|
||||||
|
|
||||||
|
# If the --no-default-features flag should be used when compiling the lib target
|
||||||
|
#
|
||||||
|
# Optional. Defaults to false.
|
||||||
|
lib-default-features = false
|
||||||
|
|
||||||
|
# The profile to use for the lib target when compiling for release
|
||||||
|
#
|
||||||
|
# Optional. Defaults to "release".
|
||||||
|
lib-profile-release = "wasm-release"
|
||||||
|
|
||||||
|
|
167
end2end/package-lock.json
generated
Normal file
167
end2end/package-lock.json
generated
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
{
|
||||||
|
"name": "end2end",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "end2end",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.44.1",
|
||||||
|
"@types/node": "^20.12.12",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@playwright/test": {
|
||||||
|
"version": "1.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
|
||||||
|
"integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "1.44.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.12.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
|
||||||
|
"integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
|
||||||
|
"integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.44.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
|
||||||
|
"integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||||
|
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@playwright/test": {
|
||||||
|
"version": "1.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
|
||||||
|
"integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"playwright": "1.44.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "20.12.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
|
||||||
|
"integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"playwright": {
|
||||||
|
"version": "1.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
|
||||||
|
"integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fsevents": "2.3.2",
|
||||||
|
"playwright-core": "1.44.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"playwright-core": {
|
||||||
|
"version": "1.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
|
||||||
|
"integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "5.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||||
|
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
end2end/package.json
Normal file
15
end2end/package.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "end2end",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.44.1",
|
||||||
|
"@types/node": "^20.12.12",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
105
end2end/playwright.config.ts
Normal file
105
end2end/playwright.config.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||||
|
import { devices, defineConfig } from "@playwright/test";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
// require('dotenv').config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./tests",
|
||||||
|
/* Maximum time one test can run for. */
|
||||||
|
timeout: 30 * 1000,
|
||||||
|
expect: {
|
||||||
|
/**
|
||||||
|
* Maximum time expect() should wait for the condition to be met.
|
||||||
|
* For example in `await expect(locator).toHaveText();`
|
||||||
|
*/
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: "html",
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||||
|
actionTimeout: 0,
|
||||||
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
|
// baseURL: 'http://localhost:3000',
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: "on-first-retry",
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Chrome"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "firefox",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Firefox"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "webkit",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Safari"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Test against mobile viewports. */
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Chrome',
|
||||||
|
// use: {
|
||||||
|
// ...devices['Pixel 5'],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Safari',
|
||||||
|
// use: {
|
||||||
|
// ...devices['iPhone 12'],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
// {
|
||||||
|
// name: 'Microsoft Edge',
|
||||||
|
// use: {
|
||||||
|
// channel: 'msedge',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Google Chrome',
|
||||||
|
// use: {
|
||||||
|
// channel: 'chrome',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||||
|
// outputDir: 'test-results/',
|
||||||
|
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
// webServer: {
|
||||||
|
// command: 'npm run start',
|
||||||
|
// port: 3000,
|
||||||
|
// },
|
||||||
|
});
|
9
end2end/tests/example.spec.ts
Normal file
9
end2end/tests/example.spec.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
test("homepage has title and heading text", async ({ page }) => {
|
||||||
|
await page.goto("http://localhost:3000/");
|
||||||
|
|
||||||
|
await expect(page).toHaveTitle("Welcome to Leptos");
|
||||||
|
|
||||||
|
await expect(page.locator("h1")).toHaveText("Welcome to Leptos!");
|
||||||
|
});
|
109
end2end/tsconfig.json
Normal file
109
end2end/tsconfig.json
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
|
/* Projects */
|
||||||
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs", /* Specify what module code is generated. */
|
||||||
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
|
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||||
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
|
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
|
}
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
506
src/Cardstack.rs
Normal file
506
src/Cardstack.rs
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::error_template::{AppError, ErrorTemplate};
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_meta::*;
|
||||||
|
use leptos_router::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn App() -> impl IntoView {
|
||||||
|
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||||
|
provide_meta_context();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
|
||||||
|
// injects a stylesheet into the document <head>
|
||||||
|
// id=leptos means cargo-leptos will hot-reload this stylesheet
|
||||||
|
// Stylesheet name is the CRATE NAME by default!!!
|
||||||
|
<Stylesheet id="leptos" href="/pkg/share-web.css"/>
|
||||||
|
|
||||||
|
// sets the document title
|
||||||
|
<Title text="Welcome to Leptos"/>
|
||||||
|
|
||||||
|
// content for this welcome page
|
||||||
|
<Router fallback=|| {
|
||||||
|
let mut outside_errors = Errors::default();
|
||||||
|
outside_errors.insert_with_default_key(AppError::NotFound);
|
||||||
|
view! {
|
||||||
|
<ErrorTemplate outside_errors/>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}>
|
||||||
|
<main>
|
||||||
|
<Routes>
|
||||||
|
<Route path="" view=HomePage/>
|
||||||
|
<Route path="/new" view=NewPage/>
|
||||||
|
<Route path="/game" view=Game/>
|
||||||
|
</Routes>
|
||||||
|
</main>
|
||||||
|
</Router>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn HomePage() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Title text="isdom"/>
|
||||||
|
<main>
|
||||||
|
<div class="py-8 px-8 mx-auto max-w-screen-xl text-center lg:py-16 lg:px-12">
|
||||||
|
<a href="#" class="inline-flex justify-between items-center py-1 px-1 pr-4 mb-7 text-sm text-gray-700 bg-gray-100 rounded-full dark:bg-gray-800 dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700" role="alert">
|
||||||
|
<span class="text-xs bg-primary-600 rounded-full text-grey px-4 py-1.5 mr-3">New</span> <span class="text-sm font-medium">Wisdom-Version 0.1 </span>
|
||||||
|
</a>
|
||||||
|
<h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-gray-900 md:text-5xl lg:text-6xl dark:text-white">Collecting wisdom, sharing thoughts</h1>
|
||||||
|
<p class="mb-8 text-lg font-normal text-gray-500 lg:text-xl sm:px-16 xl:px-48 dark:text-gray-400">Here you can collect, create and spread wisdom - share inspiration together</p>
|
||||||
|
<div class="flex flex-col mb-8 lg:mb-16 space-y-4 sm:flex-row sm:justify-center sm:space-y-0 sm:space-x-4">
|
||||||
|
<a href="/new" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-grey rounded-lg bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 dark:focus:ring-primary-900">
|
||||||
|
Start now
|
||||||
|
<svg class="ml-2 -mr-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||||
|
</a>
|
||||||
|
<a href="#" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-gray-900 rounded-lg border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 dark:text-white dark:border-gray-700 dark:hover:bg-gray-700 dark:focus:ring-gray-800">
|
||||||
|
<svg class="mr-2 -ml-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2 6a2 2 0 012-2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zM14.553 7.106A1 1 0 0014 8v4a1 1 0 00.553.894l2 1A1 1 0 0018 13V7a1 1 0 00-1.447-.894l-2 1z"></path></svg>
|
||||||
|
Watch video
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<Work/>
|
||||||
|
<Workstart/>
|
||||||
|
<Footer/>
|
||||||
|
</main>
|
||||||
|
<Bar/>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn bar() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
|
||||||
|
<div class="fixed z-50 w-full h-16 max-w-lg -translate-x-1/2 bg-white border border-gray-200 rounded-full left-1/2 dark:bg-gray-700 dark:border-gray-600">
|
||||||
|
<div class="grid h-full max-w-lg grid-cols-5 mx-auto">
|
||||||
|
<button data-tooltip-target="tooltip-home" type="button" class="inline-flex flex-col items-center justify-center px-5 rounded-s-full hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="m19.707 9.293-2-2-7-7a1 1 0 0 0-1.414 0l-7 7-2 2a1 1 0 0 0 1.414 1.414L2 10.414V18a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 0 1 1h3a2 2 0 0 0 2-2v-7.586l.293.293a1 1 0 0 0 1.414-1.414Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Home</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-home" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Home
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<button data-tooltip-target="tooltip-wallet" type="button" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M11.074 4 8.442.408A.95.95 0 0 0 7.014.254L2.926 4h8.148ZM9 13v-1a4 4 0 0 1 4-4h6V6a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h17a1 1 0 0 0 1-1v-2h-6a4 4 0 0 1-4-4Z"/>
|
||||||
|
<path d="M19 10h-6a2 2 0 0 0-2 2v1a2 2 0 0 0 2 2h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1Zm-4.5 3.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2ZM12.62 4h2.78L12.539.41a1.086 1.086 0 1 0-1.7 1.352L12.62 4Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Wallet</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-wallet" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Wallet
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<button data-tooltip-target="tooltip-new" type="button" class="inline-flex items-center justify-center w-10 h-10 font-medium bg-blue-600 rounded-full hover:bg-blue-700 group focus:ring-4 focus:ring-blue-300 focus:outline-none dark:focus:ring-blue-800">
|
||||||
|
<svg class="w-4 h-4 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">New item</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="tooltip-new" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Create new item
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<button data-tooltip-target="tooltip-settings" type="button" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 12.25V1m0 11.25a2.25 2.25 0 0 0 0 4.5m0-4.5a2.25 2.25 0 0 1 0 4.5M4 19v-2.25m6-13.5V1m0 2.25a2.25 2.25 0 0 0 0 4.5m0-4.5a2.25 2.25 0 0 1 0 4.5M10 19V7.75m6 4.5V1m0 11.25a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM16 19v-2"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Settings</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-settings" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Settings
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<button data-tooltip-target="tooltip-profile" type="button" class="inline-flex flex-col items-center justify-center px-5 rounded-e-full hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Profile</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-profile" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Profile
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn work() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<section class="bg-white dark:bg-gray-900">
|
||||||
|
<div class="gap-8 items-center px-16 mx-auto max-w-screen-xl xl:gap-16 md:grid md:grid-cols-2 lg:px-6">
|
||||||
|
<img class="w-0.2 dark:hidden" src="https://cdn-icons-png.flaticon.com/512/10777/10777094.png" alt="dashboard image" />
|
||||||
|
<div class="mt-4 md:mt-0">
|
||||||
|
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">Reflect and grow</h2>
|
||||||
|
<p class="mb-6 font-light text-gray-500 md:text-lg dark:text-gray-400">Connecting with your friends and family as well as discovering new ones Collect and create wisdom to rise in rank. Compare yourself with your friends. Receive ratings from the whole world or just from your circle of friends is easy with features like Groups.</p>
|
||||||
|
<a href="#" class="inline-flex items-center text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-primary-900">
|
||||||
|
Get started
|
||||||
|
<svg class="ml-2 -mr-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn workstart() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<section class="bg-white mt-12">
|
||||||
|
<div class="">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320" class="w-full h-auto">
|
||||||
|
<path fill="rgb(59 130 246)" fill-opacity="1" d="M0,64L48,80C96,96,192,128,288,128C384,128,480,96,576,85.3C672,75,768,85,864,122.7C960,160,1056,224,1152,234.7C1248,245,1344,203,1392,181.3L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative bg-blue-500 z-10 py-12 px-4 mx-auto max-w-screen-xl sm:py-16 lg:px-6">
|
||||||
|
<div class="mx-auto max-w-screen-sm text-center">
|
||||||
|
<h2 class="mb-4 text-4xl tracking-tight font-extrabold leading-tight text-white ">Start collecting now</h2>
|
||||||
|
<p class="mb-6 font-light text-white md:text-lg">Start sharing and collecting wisdom. Become smarter.</p>
|
||||||
|
<a href="/new" class="text-white bg-indigo-600 hover:bg-indigo-700 focus:ring-4 focus:ring-indigo-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 focus:outline-none">Lets Go</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="relative overflow-hidden">
|
||||||
|
<div class="absolute inset-x-0 top-0 h-1/2 bg-blue-500"></div>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320" class="relative z-10 w-full h-auto pb-28">
|
||||||
|
<path fill="white" fill-opacity="1" d="M0,64L48,80C96,96,192,128,288,128C384,128,480,96,576,85.3C672,75,768,85,864,122.7C960,160,1056,224,1152,234.7C1248,245,1344,203,1392,181.3L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn footer() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<footer class=" bg-white dark:bg-gray-800">
|
||||||
|
|
||||||
|
<div class="mx-auto max-w-screen-xl text-center p-12">
|
||||||
|
<a href="#" class="flex justify-center items-center text-2xl font-semibold text-gray-900 dark:text-white">
|
||||||
|
Wisdom
|
||||||
|
</a>
|
||||||
|
<p class="my-6 text-gray-500 dark:text-gray-400"> Opensource - Share and collecting wisdoms</p>
|
||||||
|
<ul class="flex flex-wrap justify-center items-center mb-6 text-gray-900 dark:text-white">
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6 ">About</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6">Blog</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6">FAQs</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6">Github</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<span class="text-sm text-gray-500 sm:text-center dark:text-gray-400"> 2024-2026 <a href="#" class="hover:underline">Marcel Hurban </a>. All Rights Reserved.</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn NewPage() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<section class="bg-white dark:bg-gray-900">
|
||||||
|
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||||
|
<div class="mx-auto max-w-screen-md text-center mb-8 lg:mb-12">
|
||||||
|
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">Choose your gamemode</h2>
|
||||||
|
<p class="mb-5 font-light text-gray-500 sm:text-xl dark:text-gray-400"></p>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-8 lg:grid lg:grid-cols-3 sm:gap-6 xl:gap-10 lg:space-y-0">
|
||||||
|
|
||||||
|
<div class="flex flex-col p-6 mx-auto max-w-lg text-center text-gray-900 bg-white rounded-lg border border-gray-100 shadow dark:border-gray-600 xl:p-8 dark:bg-gray-800 dark:text-white">
|
||||||
|
<h3 class="mb-4 text-2xl font-semibold">Worldwide</h3>
|
||||||
|
<p class="font-light text-gray-500 sm:text-lg dark:text-gray-400">Create or collect wisdoms worldwide.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<a href="/game" class="text-white bg-primary-600 border-solid border-2 bg-indigo-600 hover:bg-indigo-700 hover:bg-primary-700 focus:ring-4 focus:ring-primary-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:text-white dark:focus:ring-primary-900 my-12">Play</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col p-6 mx-auto max-w-lg text-center text-gray-900 bg-white rounded-lg border border-gray-100 shadow dark:border-gray-600 xl:p-8 dark:bg-gray-800 dark:text-white">
|
||||||
|
<h3 class="mb-4 text-2xl font-semibold">Private</h3>
|
||||||
|
<p class="font-light text-gray-500 sm:text-lg dark:text-gray-400">Enter the invitation code or group code and share your wisdom or collect some from the group</p>
|
||||||
|
|
||||||
|
<form class="max-w-sm mx-auto">
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
|
||||||
|
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 16">
|
||||||
|
<path d="m10.036 8.278 9.258-7.79A1.979 1.979 0 0 0 18 0H2A1.987 1.987 0 0 0 .641.541l9.395 7.737Z"/>
|
||||||
|
<path d="M11.241 9.817c-.36.275-.801.425-1.255.427-.428 0-.845-.138-1.187-.395L0 2.6V14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2.5l-8.759 7.317Z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="email-address-icon" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 my-8" placeholder="invitation code"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<a href="#" class="text-white border-solid border-2 bg-primary-600 bg-indigo-600 hover:bg-indigo-700 hover:bg-primary-700 focus:ring-4 focus:ring-primary-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:text-white dark:focus:ring-primary-900 my-2 ">Play</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn game() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<CardStack/>
|
||||||
|
<Footer/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Renders the Gamecard
|
||||||
|
#[component]
|
||||||
|
fn card() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<div class="flex items-center justify-center min-h-screen bg-gray-100">
|
||||||
|
<div class="relative w-64 h-96 perspective-1000 group">
|
||||||
|
<div class="w-full h-full transition-transform duration-700 transform-style-preserve-3d group-hover:rotate-y-180">
|
||||||
|
<div class="absolute w-full h-full backface-hidden">
|
||||||
|
<div class="w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 overflow-hidden">
|
||||||
|
<div class="w-full h-full border-8 border-blue-400 rounded-xl p-4 flex flex-col justify-between">
|
||||||
|
<div class="text-2xl font-bold text-red-600">1</div>
|
||||||
|
<div class="text-center font-serif italic text-lg px-2">
|
||||||
|
"Weisheit beginnt mit Verwunderung."
|
||||||
|
<div class="mt-2 text-sm font-sans">- Sokrates</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-bold text-red-600 self-end transform rotate-180">1</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="absolute w-full h-full backface-hidden rotate-y-180 bg-blue-500 rounded-xl flex items-center justify-center">
|
||||||
|
<div class="text-white text-3xl font-bold">Rückseite</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CardStack() -> impl IntoView {
|
||||||
|
let cards = vec![
|
||||||
|
("1", "Weisheit beginnt mit Verwunderung.", "- Sokrates"),
|
||||||
|
("2", "Der Weg ist das Ziel.", "- Konfuzius"),
|
||||||
|
("3", "Ich weiß, dass ich nichts weiß.", "- Sokrates"),
|
||||||
|
// Fügen Sie hier weitere Karten hinzu
|
||||||
|
];
|
||||||
|
|
||||||
|
let cards_state = Rc::new(RefCell::new(cards));
|
||||||
|
let current_card = create_rw_signal(None);
|
||||||
|
let drag_start = create_rw_signal(0.0);
|
||||||
|
let is_dragging = create_rw_signal(false);
|
||||||
|
|
||||||
|
let draw_card = move |_| {
|
||||||
|
let mut cards = cards_state.borrow_mut();
|
||||||
|
if let Some(card) = cards.pop() {
|
||||||
|
current_card.set(Some(card));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drag_start = move |e: web_sys::MouseEvent| {
|
||||||
|
drag_start.set(e.client_y() as f64);
|
||||||
|
is_dragging.set(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drag_end = move |e: web_sys::MouseEvent| {
|
||||||
|
if is_dragging.get() {
|
||||||
|
let drag_end = e.client_y() as f64;
|
||||||
|
let drag_distance = drag_start.get() - drag_end;
|
||||||
|
if drag_distance > 50.0 {
|
||||||
|
draw_card(());
|
||||||
|
}
|
||||||
|
is_dragging.set(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-100">
|
||||||
|
<div
|
||||||
|
class="relative w-64 h-96 perspective-1000 cursor-grab"
|
||||||
|
on:mousedown=on_drag_start
|
||||||
|
on:mouseup=on_drag_end
|
||||||
|
on:mouseleave=move |_| is_dragging.set(false)
|
||||||
|
>
|
||||||
|
{move || {
|
||||||
|
current_card.get().map(|card| view! {
|
||||||
|
<FlipCard number=card.0 quote=card.1 author=card.2 />
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
<div class="absolute top-0 left-0 w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 -z-10">
|
||||||
|
<div class="w-full h-full border-8 border-blue-400 rounded-xl p-4 flex flex-col justify-between">
|
||||||
|
<div class="text-center font-serif italic text-lg px-2">
|
||||||
|
"Weisheit beginnt mit Verwunderung."
|
||||||
|
<div class="mt-2 text-sm font-sans">- Sokrates</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="absolute top-1 left-1 w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 -z-20"></div>
|
||||||
|
<div class="absolute top-2 left-2 w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 -z-30"></div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-4 text-gray-600">"Ziehen Sie eine Karte nach oben, um sie zu ziehen"</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn FlipCard(
|
||||||
|
number: &'static str,
|
||||||
|
quote: &'static str,
|
||||||
|
author: &'static str,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let (is_flipped, set_is_flipped) = create_signal(false);
|
||||||
|
|
||||||
|
let toggle_flip = move |_| set_is_flipped.update(|flipped| *flipped = !*flipped);
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div
|
||||||
|
class="absolute w-64 h-96 perspective-1000 cursor-pointer"
|
||||||
|
on:click=toggle_flip
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class=move || format!(
|
||||||
|
"w-full h-full transition-transform duration-700 transform-style-preserve-3d {}",
|
||||||
|
if is_flipped.get() { "rotate-y-180" } else { "" }
|
||||||
|
)
|
||||||
|
>
|
||||||
|
<div class="absolute w-full h-full backface-hidden">
|
||||||
|
<div class="w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 overflow-hidden">
|
||||||
|
<div class="w-full h-full border-8 border-blue-400 rounded-xl p-4 flex flex-col justify-between">
|
||||||
|
<div class="text-2xl font-bold text-red-600">{number}</div>
|
||||||
|
<div class="text-center font-serif italic text-lg px-2">
|
||||||
|
{quote}
|
||||||
|
<div class="mt-2 text-sm font-sans">{author}</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-bold text-red-600 self-end transform rotate-180">{number}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="absolute w-full h-full backface-hidden rotate-y-180 bg-blue-500 rounded-xl flex items-center justify-center">
|
||||||
|
<div class="text-white text-3xl font-bold">"Rückseite"</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn GamePage() -> impl IntoView {
|
||||||
|
let invite_code = create_rw_signal(String::new());
|
||||||
|
let player_quotes = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
let current_quote = create_rw_signal(String::new());
|
||||||
|
let ratings = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
|
||||||
|
let join_game = move |_| {
|
||||||
|
// Logic to handle joining the game with the invite code
|
||||||
|
// For now, just print the invite code
|
||||||
|
log!("Joined game with invite code: {}", invite_code.get());
|
||||||
|
};
|
||||||
|
|
||||||
|
let submit_quote = move |_| {
|
||||||
|
let quote = current_quote.get();
|
||||||
|
if !quote.is_empty() {
|
||||||
|
player_quotes.borrow_mut().push(quote.clone());
|
||||||
|
current_quote.set(String::new());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let rate_quote = move |index: usize, rating: u8| {
|
||||||
|
ratings.borrow_mut().push((index, rating));
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-200">
|
||||||
|
<h1 class="text-3xl font-bold mb-4">"Quote Game"</h1>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Invite Code"
|
||||||
|
value=invite_code.get()
|
||||||
|
on:input=move |e| invite_code.set(e.target_unchecked_into::<HtmlInputElement>().value())
|
||||||
|
class="border p-2 mb-4"
|
||||||
|
/>
|
||||||
|
<button on:click=join_game class="bg-blue-500 text-white p-2 rounded">"Join Game"</button>
|
||||||
|
|
||||||
|
<h2 class="mt-6 text-xl">"Submit Your Quote"</h2>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Your Quote"
|
||||||
|
value=current_quote.get()
|
||||||
|
on:input=move |e| current_quote.set(e.target_unchecked_into::<HtmlInputElement>().value())
|
||||||
|
class="border p-2 mb-4"
|
||||||
|
/>
|
||||||
|
<button on:click=submit_quote class="bg-green-500 text-white p-2 rounded">"Submit Quote"</button>
|
||||||
|
|
||||||
|
<h2 class="mt-6 text-xl">"Player Quotes"</h2>
|
||||||
|
<ul>
|
||||||
|
{move || {
|
||||||
|
player_quotes.borrow().iter().enumerate().map(|(index, quote)| {
|
||||||
|
view! {
|
||||||
|
<li class="flex justify-between items-center border-b p-2">
|
||||||
|
<span>{quote}</span>
|
||||||
|
<div>
|
||||||
|
{ (1..=5).map(|rating| {
|
||||||
|
view! {
|
||||||
|
<button on:click=move |_| rate_quote(index, rating) class="mx-1">{rating}</button>
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>() }
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
565
src/app.rs
Normal file
565
src/app.rs
Normal file
|
@ -0,0 +1,565 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::error_template::{AppError, ErrorTemplate};
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_meta::*;
|
||||||
|
use leptos_router::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn App() -> impl IntoView {
|
||||||
|
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||||
|
provide_meta_context();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
|
||||||
|
// injects a stylesheet into the document <head>
|
||||||
|
// id=leptos means cargo-leptos will hot-reload this stylesheet
|
||||||
|
// Stylesheet name is the CRATE NAME by default!!!
|
||||||
|
<Stylesheet id="leptos" href="/pkg/share-web.css"/>
|
||||||
|
|
||||||
|
// sets the document title
|
||||||
|
<Title text="Welcome to Leptos"/>
|
||||||
|
|
||||||
|
// content for this welcome page
|
||||||
|
<Router fallback=|| {
|
||||||
|
let mut outside_errors = Errors::default();
|
||||||
|
outside_errors.insert_with_default_key(AppError::NotFound);
|
||||||
|
view! {
|
||||||
|
<ErrorTemplate outside_errors/>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}>
|
||||||
|
<main>
|
||||||
|
<Routes>
|
||||||
|
<Route path="" view=HomePage/>
|
||||||
|
<Route path="/new" view=NewPage/>
|
||||||
|
<Route path="/game" view=Game/>
|
||||||
|
<Route path="/lobby" view=Lobby/>
|
||||||
|
</Routes>
|
||||||
|
</main>
|
||||||
|
</Router>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn HomePage() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Title text="isdom"/>
|
||||||
|
<main>
|
||||||
|
<div class="py-8 px-8 mx-auto max-w-screen-xl text-center lg:py-16 lg:px-12">
|
||||||
|
<a href="#" class="inline-flex justify-between items-center py-1 px-1 pr-4 mb-7 text-sm text-gray-700 bg-gray-100 rounded-full dark:bg-gray-800 dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700" role="alert">
|
||||||
|
<span class="text-xs bg-primary-600 rounded-full text-grey px-4 py-1.5 mr-3">New</span> <span class="text-sm font-medium">Wisdom-Version 0.1 </span>
|
||||||
|
</a>
|
||||||
|
<h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-gray-900 md:text-5xl lg:text-6xl dark:text-white">Collecting wisdom, sharing thoughts</h1>
|
||||||
|
<p class="mb-8 text-lg font-normal text-gray-500 lg:text-xl sm:px-16 xl:px-48 dark:text-gray-400">Here you can collect, create and spread wisdom - share inspiration together</p>
|
||||||
|
<div class="flex flex-col mb-8 lg:mb-16 space-y-4 sm:flex-row sm:justify-center sm:space-y-0 sm:space-x-4">
|
||||||
|
<a href="/new" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-grey rounded-lg bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 dark:focus:ring-primary-900">
|
||||||
|
Start now
|
||||||
|
<svg class="ml-2 -mr-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||||
|
</a>
|
||||||
|
<a href="#" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-gray-900 rounded-lg border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 dark:text-white dark:border-gray-700 dark:hover:bg-gray-700 dark:focus:ring-gray-800">
|
||||||
|
<svg class="mr-2 -ml-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2 6a2 2 0 012-2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zM14.553 7.106A1 1 0 0014 8v4a1 1 0 00.553.894l2 1A1 1 0 0018 13V7a1 1 0 00-1.447-.894l-2 1z"></path></svg>
|
||||||
|
Watch video
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<Work/>
|
||||||
|
<Workstart/>
|
||||||
|
<Footer/>
|
||||||
|
</main>
|
||||||
|
<Bar/>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn bar() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
|
||||||
|
<div class="fixed z-50 w-full h-16 max-w-lg -translate-x-1/2 bg-white border border-gray-200 rounded-full left-1/2 dark:bg-gray-700 dark:border-gray-600">
|
||||||
|
<div class="grid h-full max-w-lg grid-cols-5 mx-auto">
|
||||||
|
<button data-tooltip-target="tooltip-home" type="button" class="inline-flex flex-col items-center justify-center px-5 rounded-s-full hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="m19.707 9.293-2-2-7-7a1 1 0 0 0-1.414 0l-7 7-2 2a1 1 0 0 0 1.414 1.414L2 10.414V18a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 0 1 1h3a2 2 0 0 0 2-2v-7.586l.293.293a1 1 0 0 0 1.414-1.414Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Home</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-home" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Home
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<button data-tooltip-target="tooltip-wallet" type="button" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M11.074 4 8.442.408A.95.95 0 0 0 7.014.254L2.926 4h8.148ZM9 13v-1a4 4 0 0 1 4-4h6V6a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h17a1 1 0 0 0 1-1v-2h-6a4 4 0 0 1-4-4Z"/>
|
||||||
|
<path d="M19 10h-6a2 2 0 0 0-2 2v1a2 2 0 0 0 2 2h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1Zm-4.5 3.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2ZM12.62 4h2.78L12.539.41a1.086 1.086 0 1 0-1.7 1.352L12.62 4Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Wallet</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-wallet" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Wallet
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<button data-tooltip-target="tooltip-new" type="button" class="inline-flex items-center justify-center w-10 h-10 font-medium bg-blue-600 rounded-full hover:bg-blue-700 group focus:ring-4 focus:ring-blue-300 focus:outline-none dark:focus:ring-blue-800">
|
||||||
|
<svg class="w-4 h-4 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">New item</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="tooltip-new" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Create new item
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<button data-tooltip-target="tooltip-settings" type="button" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 12.25V1m0 11.25a2.25 2.25 0 0 0 0 4.5m0-4.5a2.25 2.25 0 0 1 0 4.5M4 19v-2.25m6-13.5V1m0 2.25a2.25 2.25 0 0 0 0 4.5m0-4.5a2.25 2.25 0 0 1 0 4.5M10 19V7.75m6 4.5V1m0 11.25a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM16 19v-2"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Settings</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-settings" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Settings
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<button data-tooltip-target="tooltip-profile" type="button" class="inline-flex flex-col items-center justify-center px-5 rounded-e-full hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Profile</span>
|
||||||
|
</button>
|
||||||
|
<div id="tooltip-profile" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
|
Profile
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn work() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<section class="bg-white dark:bg-gray-900">
|
||||||
|
<div class="gap-8 items-center px-16 mx-auto max-w-screen-xl xl:gap-16 md:grid md:grid-cols-2 lg:px-6">
|
||||||
|
<img class="w-0.2 dark:hidden" src="https://cdn-icons-png.flaticon.com/512/10777/10777094.png" alt="dashboard image" />
|
||||||
|
<div class="mt-4 md:mt-0">
|
||||||
|
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">Reflect and grow</h2>
|
||||||
|
<p class="mb-6 font-light text-gray-500 md:text-lg dark:text-gray-400">Connecting with your friends and family as well as discovering new ones Collect and create wisdom to rise in rank. Compare yourself with your friends. Receive ratings from the whole world or just from your circle of friends is easy with features like Groups.</p>
|
||||||
|
<a href="#" class="inline-flex items-center text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-primary-900">
|
||||||
|
Get started
|
||||||
|
<svg class="ml-2 -mr-1 w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn workstart() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<section class="bg-white mt-12">
|
||||||
|
<div class="">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320" class="w-full h-auto">
|
||||||
|
<path fill="rgb(59 130 246)" fill-opacity="1" d="M0,64L48,80C96,96,192,128,288,128C384,128,480,96,576,85.3C672,75,768,85,864,122.7C960,160,1056,224,1152,234.7C1248,245,1344,203,1392,181.3L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative bg-blue-500 z-10 py-12 px-4 mx-auto max-w-screen-xl sm:py-16 lg:px-6">
|
||||||
|
<div class="mx-auto max-w-screen-sm text-center">
|
||||||
|
<h2 class="mb-4 text-4xl tracking-tight font-extrabold leading-tight text-white ">Start collecting now</h2>
|
||||||
|
<p class="mb-6 font-light text-white md:text-lg">Start sharing and collecting wisdom. Become smarter.</p>
|
||||||
|
<a href="/new" class="text-white bg-indigo-600 hover:bg-indigo-700 focus:ring-4 focus:ring-indigo-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 focus:outline-none">Lets Go</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="relative overflow-hidden">
|
||||||
|
<div class="absolute inset-x-0 top-0 h-1/2 bg-blue-500"></div>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320" class="relative z-10 w-full h-auto pb-28">
|
||||||
|
<path fill="white" fill-opacity="1" d="M0,64L48,80C96,96,192,128,288,128C384,128,480,96,576,85.3C672,75,768,85,864,122.7C960,160,1056,224,1152,234.7C1248,245,1344,203,1392,181.3L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn footer() -> impl IntoView {
|
||||||
|
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<footer class=" bg-white dark:bg-gray-800">
|
||||||
|
|
||||||
|
<div class="mx-auto max-w-screen-xl text-center p-12">
|
||||||
|
<a href="#" class="flex justify-center items-center text-2xl font-semibold text-gray-900 dark:text-white">
|
||||||
|
Wisdom
|
||||||
|
</a>
|
||||||
|
<p class="my-6 text-gray-500 dark:text-gray-400"> Opensource - Share and collecting wisdoms</p>
|
||||||
|
<ul class="flex flex-wrap justify-center items-center mb-6 text-gray-900 dark:text-white">
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6 ">About</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6">Blog</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6">FAQs</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="mr-4 hover:underline md:mr-6">Github</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<span class="text-sm text-gray-500 sm:text-center dark:text-gray-400"> 2024-2026 <a href="#" class="hover:underline">Marcel Hurban </a>. All Rights Reserved.</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn NewPage() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<section class="bg-white dark:bg-gray-900">
|
||||||
|
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||||
|
<div class="mx-auto max-w-screen-md text-center mb-8 lg:mb-12">
|
||||||
|
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">Choose your gamemode</h2>
|
||||||
|
<p class="mb-5 font-light text-gray-500 sm:text-xl dark:text-gray-400"></p>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-8 lg:grid lg:grid-cols-3 sm:gap-6 xl:gap-10 lg:space-y-0">
|
||||||
|
|
||||||
|
<div class="flex flex-col p-6 mx-auto max-w-lg text-center text-gray-900 bg-white rounded-lg border border-gray-100 shadow dark:border-gray-600 xl:p-8 dark:bg-gray-800 dark:text-white">
|
||||||
|
<h3 class="mb-4 text-2xl font-semibold">Worldwide</h3>
|
||||||
|
<p class="font-light text-gray-500 sm:text-lg dark:text-gray-400">Create or collect wisdoms worldwide.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<a href="/game" class="text-white bg-primary-600 border-solid border-2 bg-indigo-600 hover:bg-indigo-700 hover:bg-primary-700 focus:ring-4 focus:ring-primary-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:text-white dark:focus:ring-primary-900 my-12">Play</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col p-6 mx-auto max-w-lg text-center text-gray-900 bg-white rounded-lg border border-gray-100 shadow dark:border-gray-600 xl:p-8 dark:bg-gray-800 dark:text-white">
|
||||||
|
<h3 class="mb-4 text-2xl font-semibold">Private</h3>
|
||||||
|
<p class="font-light text-gray-500 sm:text-lg dark:text-gray-400">Enter the invitation code or group code and share your wisdom or collect some from the group</p>
|
||||||
|
|
||||||
|
<form class="max-w-sm mx-auto">
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
|
||||||
|
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 16">
|
||||||
|
<path d="m10.036 8.278 9.258-7.79A1.979 1.979 0 0 0 18 0H2A1.987 1.987 0 0 0 .641.541l9.395 7.737Z"/>
|
||||||
|
<path d="M11.241 9.817c-.36.275-.801.425-1.255.427-.428 0-.845-.138-1.187-.395L0 2.6V14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2.5l-8.759 7.317Z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="email-address-icon" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 my-8" placeholder="invitation code"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<a href="#" class="text-white border-solid border-2 bg-primary-600 bg-indigo-600 hover:bg-indigo-700 hover:bg-primary-700 focus:ring-4 focus:ring-primary-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:text-white dark:focus:ring-primary-900 my-2 ">Play</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn game() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<CardStack/>
|
||||||
|
<Gamenavigation />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Renders the Gamecard
|
||||||
|
#[component]
|
||||||
|
fn gamenavigation() -> impl IntoView {
|
||||||
|
// You'll need to implement these signals in your actual code
|
||||||
|
let (countdown, set_countdown) = create_signal(60); // Beispiel: 60 Sekunden Countdown
|
||||||
|
let (participants, set_participants) = create_signal(5); // Beispiel: 5 Teilnehmer
|
||||||
|
let (current_round, set_current_round) = create_signal(2); // Beispiel: Runde 2
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="fixed z-50 w-full h-16 max-w-lg -translate-x-1/2 bg-white border border-gray-200 rounded-full bottom-4 left-1/2 dark:bg-gray-700 dark:border-gray-600">
|
||||||
|
<div class="flex h-full max-w-lg mx-auto items-center justify-center">
|
||||||
|
<button type="button" class="inline-flex flex-col items-center justify-center px-5 rounded-s-full hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 0a9 9 0 1 0 0 18A9 9 0 0 0 9 0zm0 16a7 7 0 1 1 0-14 7 7 0 0 1 0 14zm1-8h3v2h-3v3H8v-3H5V8h3V5h2v3z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs mt-1">{move || format!("{} Players", participants.get())}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="inline-flex items-center justify-center w-10 h-10 font-medium bg-blue-600 rounded-full hover:bg-blue-700 group focus:ring-4 focus:ring-blue-300 focus:outline-none dark:focus:ring-blue-800 mx-4">
|
||||||
|
<span class="text-white text-sm font-bold">{move || countdown.get()}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm3.982 13.982a6.938 6.938 0 0 1-7.964 0A3.997 3.997 0 0 1 10 11a3.997 3.997 0 0 1 3.982 2.982ZM13.5 5.5a3.5 3.5 0 1 1-7 0 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs mt-1">{move || format!("Round {}", current_round.get())}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CardStack() -> impl IntoView {
|
||||||
|
let cards = vec![
|
||||||
|
("1", "Weisheit beginnt mit Verwunderung.", "- Sokrates"),
|
||||||
|
("2", "Der Weg ist das Ziel.", "- Konfuzius"),
|
||||||
|
("3", "Ich weiß, dass ich nichts weiß.", "- Sokrates"),
|
||||||
|
// Fügen Sie hier weitere Karten hinzu
|
||||||
|
];
|
||||||
|
|
||||||
|
let cards_state = Rc::new(RefCell::new(cards));
|
||||||
|
let current_card = create_rw_signal(None);
|
||||||
|
let drag_start = create_rw_signal(0.0);
|
||||||
|
let is_dragging = create_rw_signal(false);
|
||||||
|
|
||||||
|
let draw_card = move |_| {
|
||||||
|
let mut cards = cards_state.borrow_mut();
|
||||||
|
if let Some(card) = cards.pop() {
|
||||||
|
current_card.set(Some(card));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drag_start = move |e: web_sys::MouseEvent| {
|
||||||
|
drag_start.set(e.client_y() as f64);
|
||||||
|
is_dragging.set(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_drag_end = move |e: web_sys::MouseEvent| {
|
||||||
|
if is_dragging.get() {
|
||||||
|
let drag_end = e.client_y() as f64;
|
||||||
|
let drag_distance = drag_start.get() - drag_end;
|
||||||
|
if drag_distance > 50.0 {
|
||||||
|
draw_card(());
|
||||||
|
}
|
||||||
|
is_dragging.set(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-100">
|
||||||
|
<div
|
||||||
|
class="relative w-64 h-96 perspective-1000 cursor-grab"
|
||||||
|
on:mousedown=on_drag_start
|
||||||
|
on:mouseup=on_drag_end
|
||||||
|
on:mouseleave=move |_| is_dragging.set(false)
|
||||||
|
>
|
||||||
|
{move || {
|
||||||
|
current_card.get().map(|card| view! {
|
||||||
|
<FlipCard number=card.0 quote=card.1 author=card.2 />
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
<div class="absolute top-0 left-0 w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 -z-10">
|
||||||
|
<div class="w-full h-full border-8 border-blue-400 rounded-xl p-4 flex flex-col justify-between">
|
||||||
|
<div class="text-center font-serif italic text-lg px-2">
|
||||||
|
"draw a card from the deck."
|
||||||
|
<div class="mt-2 text-sm font-sans">Guide</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="absolute top-1 left-1 w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 -z-20"></div>
|
||||||
|
<div class="absolute top-2 left-2 w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 -z-30"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
use leptos::*;
|
||||||
|
use web_sys::KeyboardEvent;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn FlipCard(
|
||||||
|
number: &'static str,
|
||||||
|
quote: &'static str,
|
||||||
|
author: &'static str,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let words: Vec<&str> = quote.split_whitespace().collect();
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let removed_index = rng.gen_range(0..words.len());
|
||||||
|
let removed_word = words[removed_index];
|
||||||
|
|
||||||
|
let (user_input, set_user_input) = create_signal(String::new());
|
||||||
|
let (is_correct, set_is_correct) = create_signal(None::<bool>);
|
||||||
|
|
||||||
|
let check_answer = move || {
|
||||||
|
let correct = user_input.get().trim().to_lowercase() == removed_word.to_lowercase();
|
||||||
|
set_is_correct.set(Some(correct));
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle_keydown = move |ev: KeyboardEvent| {
|
||||||
|
if ev.key() == "Enter" {
|
||||||
|
check_answer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="w-64 h-96">
|
||||||
|
<div class="w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 overflow-hidden">
|
||||||
|
<div class="w-full h-full border-8 border-blue-400 rounded-xl p-4 flex flex-col justify-between">
|
||||||
|
<div class="text-2xl font-bold text-red-600">{number}</div>
|
||||||
|
<div class="text-center font-serif italic text-lg px-2">
|
||||||
|
{words.iter().enumerate().map(|(i, &word)| {
|
||||||
|
let element = if i == removed_index {
|
||||||
|
view! {
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="____"
|
||||||
|
class=move || {
|
||||||
|
let base_class = "w-20 text-center border-b border-gray-300 focus:outline-none focus:border-blue-500";
|
||||||
|
match is_correct.get() {
|
||||||
|
Some(true) => format!("{} text-green-500", base_class),
|
||||||
|
Some(false) => format!("{} text-red-500", base_class),
|
||||||
|
None => base_class.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on:input=move |ev| {
|
||||||
|
set_user_input.set(event_target_value(&ev));
|
||||||
|
set_is_correct.set(None);
|
||||||
|
}
|
||||||
|
on:keydown=handle_keydown
|
||||||
|
/>
|
||||||
|
}.into_view()
|
||||||
|
} else {
|
||||||
|
view! { <span>{word}</span> }.into_view()
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<>
|
||||||
|
{element}
|
||||||
|
{if i < words.len() - 1 { " " } else { "" }}
|
||||||
|
</>
|
||||||
|
}.into_view()
|
||||||
|
}).collect::<Vec<_>>()}
|
||||||
|
<div class="mt-2 text-sm font-sans">{author}</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-bold text-red-600 self-end transform rotate-180">{number}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn lobby() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
|
||||||
|
<Lobbyinfo />
|
||||||
|
<LobbyGame/>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn lobby_game() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-100">
|
||||||
|
<div class="w-64 h-96">
|
||||||
|
<div class="w-64 h-96 bg-white rounded-xl shadow-2xl transform rotate-3 transition-transform duration-300 overflow-hidden">
|
||||||
|
<div class="w-full h-full border-8 border-blue-400 rounded-xl p-4 flex flex-col justify-between">
|
||||||
|
<table class="px-64 text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||||
|
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="px-6 py-3">
|
||||||
|
Player
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-6 py-3">
|
||||||
|
<span class="sr-only">Edit</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-600">
|
||||||
|
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||||
|
Player_1
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<td class="px-6 py-4 text-right">
|
||||||
|
<a href="#" class="font-medium text-blue-600 dark:text-blue-500 hover:underline">Edit</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="text-center font-serif italic text-lg px-2">
|
||||||
|
<div class="mt-2 text-sm font-sans"></div>
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-bold text-red-600 self-end transform rotate-180"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Renders the Gamecard
|
||||||
|
#[component]
|
||||||
|
fn lobbyinfo() -> impl IntoView {
|
||||||
|
|
||||||
|
let (participants, set_participants) = create_signal(5); // Beispiel: 5 Teilnehmer
|
||||||
|
let (current_round, set_current_round) = create_signal(2); // Beispiel: Runde 2
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="fixed z-50 w-full h-16 max-w-lg -translate-x-1/2 bg-white border border-gray-200 rounded-full bottom-4 left-1/2 dark:bg-gray-700 dark:border-gray-600">
|
||||||
|
<div class="flex h-full max-w-lg mx-auto items-center justify-center">
|
||||||
|
<button type="button" class="inline-flex flex-col items-center justify-center px-5 rounded-s-full hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 0a9 9 0 1 0 0 18A9 9 0 0 0 9 0zm0 16a7 7 0 1 1 0-14 7 7 0 0 1 0 14zm1-8h3v2h-3v3H8v-3H5V8h3V5h2v3z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs mt-1">{move || format!("{} Players", participants.get())}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="inline-flex items-center justify-center w-10 h-10 font-medium bg-blue-600 rounded-full hover:bg-blue-700 group focus:ring-4 focus:ring-blue-300 focus:outline-none dark:focus:ring-blue-800 mx-4">
|
||||||
|
<span class="text-white text-sm font-bold">GO</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
|
||||||
|
<svg class="w-5 h-5 mb-1 text-gray-500 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm3.982 13.982a6.938 6.938 0 0 1-7.964 0A3.997 3.997 0 0 1 10 11a3.997 3.997 0 0 1 3.982 2.982ZM13.5 5.5a3.5 3.5 0 1 1-7 0 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5 a3.5"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs mt-1">{move || format!("Round {}", current_round.get())}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
72
src/error_template.rs
Normal file
72
src/error_template.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use http::status::StatusCode;
|
||||||
|
use leptos::*;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Error)]
|
||||||
|
pub enum AppError {
|
||||||
|
#[error("Not Found")]
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppError {
|
||||||
|
pub fn status_code(&self) -> StatusCode {
|
||||||
|
match self {
|
||||||
|
AppError::NotFound => StatusCode::NOT_FOUND,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A basic function to display errors served by the error boundaries.
|
||||||
|
// Feel free to do more complicated things here than just displaying the error.
|
||||||
|
#[component]
|
||||||
|
pub fn ErrorTemplate(
|
||||||
|
#[prop(optional)] outside_errors: Option<Errors>,
|
||||||
|
#[prop(optional)] errors: Option<RwSignal<Errors>>,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let errors = match outside_errors {
|
||||||
|
Some(e) => create_rw_signal(e),
|
||||||
|
None => match errors {
|
||||||
|
Some(e) => e,
|
||||||
|
None => panic!("No Errors found and we expected errors!"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// Get Errors from Signal
|
||||||
|
let errors = errors.get_untracked();
|
||||||
|
|
||||||
|
// Downcast lets us take a type that implements `std::error::Error`
|
||||||
|
let errors: Vec<AppError> = errors
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())
|
||||||
|
.collect();
|
||||||
|
println!("Errors: {errors:#?}");
|
||||||
|
|
||||||
|
// Only the response code for the first error is actually sent from the server
|
||||||
|
// this may be customized by the specific application
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
{
|
||||||
|
use leptos_axum::ResponseOptions;
|
||||||
|
let response = use_context::<ResponseOptions>();
|
||||||
|
if let Some(response) = response {
|
||||||
|
response.set_status(errors[0].status_code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<h1>{if errors.len() > 1 {"Errors"} else {"Error"}}</h1>
|
||||||
|
<For
|
||||||
|
// a function that returns the items we're iterating over; a signal is fine
|
||||||
|
each= move || {errors.clone().into_iter().enumerate()}
|
||||||
|
// a unique key for each item as a reference
|
||||||
|
key=|(index, _error)| *index
|
||||||
|
// renders each item to a view
|
||||||
|
children=move |error| {
|
||||||
|
let error_string = error.1.to_string();
|
||||||
|
let error_code= error.1.status_code();
|
||||||
|
view! {
|
||||||
|
<h2>{error_code.to_string()}</h2>
|
||||||
|
<p>"Error: " {error_string}</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
60
src/fileserv.rs
Normal file
60
src/fileserv.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use crate::app::App;
|
||||||
|
use axum::response::Response as AxumResponse;
|
||||||
|
use axum::{
|
||||||
|
body::Body,
|
||||||
|
extract::State,
|
||||||
|
http::{Request, Response, StatusCode},
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use leptos::*;
|
||||||
|
use tower::ServiceExt;
|
||||||
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
|
pub async fn file_and_error_handler(
|
||||||
|
State(options): State<LeptosOptions>,
|
||||||
|
req: Request<Body>,
|
||||||
|
) -> AxumResponse {
|
||||||
|
let root = options.site_root.clone();
|
||||||
|
let (parts, body) = req.into_parts();
|
||||||
|
|
||||||
|
let mut static_parts = parts.clone();
|
||||||
|
static_parts.headers.clear();
|
||||||
|
if let Some(encodings) = parts.headers.get("accept-encoding") {
|
||||||
|
static_parts
|
||||||
|
.headers
|
||||||
|
.insert("accept-encoding", encodings.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = get_static_file(Request::from_parts(static_parts, Body::empty()), &root)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if res.status() == StatusCode::OK {
|
||||||
|
res.into_response()
|
||||||
|
} else {
|
||||||
|
let handler = leptos_axum::render_app_to_stream(options.to_owned(), App);
|
||||||
|
handler(Request::from_parts(parts, body))
|
||||||
|
.await
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_static_file(
|
||||||
|
request: Request<Body>,
|
||||||
|
root: &str,
|
||||||
|
) -> Result<Response<Body>, (StatusCode, String)> {
|
||||||
|
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
|
||||||
|
// This path is relative to the cargo root
|
||||||
|
match ServeDir::new(root)
|
||||||
|
.precompressed_gzip()
|
||||||
|
.precompressed_br()
|
||||||
|
.oneshot(request)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => Ok(res.into_response()),
|
||||||
|
Err(err) => Err((
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Error serving files: {err}"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
12
src/lib.rs
Normal file
12
src/lib.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
pub mod app;
|
||||||
|
pub mod error_template;
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
pub mod fileserv;
|
||||||
|
|
||||||
|
#[cfg(feature = "hydrate")]
|
||||||
|
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||||
|
pub fn hydrate() {
|
||||||
|
use crate::app::*;
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
leptos::mount_to_body(App);
|
||||||
|
}
|
38
src/main.rs
Normal file
38
src/main.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
use axum::Router;
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||||
|
use share_web::app::*;
|
||||||
|
use share_web::fileserv::file_and_error_handler;
|
||||||
|
|
||||||
|
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
|
||||||
|
// For deployment these variables are:
|
||||||
|
// <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>
|
||||||
|
// Alternately a file can be specified such as Some("Cargo.toml")
|
||||||
|
// The file would need to be included with the executable when moved to deployment
|
||||||
|
let conf = get_configuration(None).await.unwrap();
|
||||||
|
let leptos_options = conf.leptos_options;
|
||||||
|
let addr = leptos_options.site_addr;
|
||||||
|
let routes = generate_route_list(App);
|
||||||
|
|
||||||
|
// build our application with a route
|
||||||
|
let app = Router::new()
|
||||||
|
.leptos_routes(&leptos_options, routes, App)
|
||||||
|
.fallback(file_and_error_handler)
|
||||||
|
.with_state(leptos_options);
|
||||||
|
|
||||||
|
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
||||||
|
logging::log!("listening on http://{}", &addr);
|
||||||
|
axum::serve(listener, app.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
pub fn main() {
|
||||||
|
// no client-side main function
|
||||||
|
// unless we want this to work with e.g., Trunk for a purely client-side app
|
||||||
|
// see lib.rs for hydration function instead
|
||||||
|
}
|
4
style/main.scss
Normal file
4
style/main.scss
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
20
style/tailwind.css
Normal file
20
style/tailwind.css
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.perspective-1000 {
|
||||||
|
perspective: 1000px;
|
||||||
|
}
|
||||||
|
.backface-hidden {
|
||||||
|
backface-visibility: hidden;
|
||||||
|
}
|
||||||
|
.transform-style-preserve-3d {
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
}
|
||||||
|
.rotate-y-180 {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
tailwind.config.js
Normal file
13
tailwind.config.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["*.html", "./src/**/*.rs",],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('flowbite/plugin')
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user