generated from hjess/PythonTemplateProject
Compare commits
18 Commits
mvc
...
dea59f3d23
| Author | SHA1 | Date | |
|---|---|---|---|
| dea59f3d23 | |||
|
|
7072e7e099 | ||
|
|
1cb9e066ab | ||
| 4e43c10b54 | |||
|
|
4009d49ee6 | ||
|
|
f13aa9ec7e | ||
| ce74aa5113 | |||
| 11e66000c9 | |||
| afd60d3d58 | |||
| 8b57e2af8b | |||
| 2029db143f | |||
| 93c8a066cb | |||
| a2c6b94da1 | |||
| 63372e8210 | |||
| e2e8c8bf66 | |||
| adfa478eca | |||
| 7f7dd5139e | |||
| ffa1ae346f |
12
app/main.py
12
app/main.py
@@ -27,9 +27,6 @@ class Application:
|
|||||||
# Process Markdown files into HTML
|
# Process Markdown files into HTML
|
||||||
processor = MarkdownProcessor(input_dir="./data", templates_dir="./templates")
|
processor = MarkdownProcessor(input_dir="./data", templates_dir="./templates")
|
||||||
processor.run()
|
processor.run()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
yield
|
yield
|
||||||
print("App shutdown: Cleanup complete.")
|
print("App shutdown: Cleanup complete.")
|
||||||
|
|
||||||
@@ -41,10 +38,13 @@ class Application:
|
|||||||
def _include_routers(self):
|
def _include_routers(self):
|
||||||
"""Include all route controllers."""
|
"""Include all route controllers."""
|
||||||
category_controller = CategoryController()
|
category_controller = CategoryController()
|
||||||
dynamic_controller = DynamicController("./data")
|
dynamic_controller = DynamicController( "./data" )
|
||||||
|
|
||||||
|
|
||||||
|
self.app.include_router( category_controller.router )
|
||||||
|
self.app.include_router( dynamic_controller.router )
|
||||||
|
|
||||||
|
|
||||||
self.app.include_router(category_controller.router)
|
|
||||||
self.app.include_router(dynamic_controller.router)
|
|
||||||
|
|
||||||
def get_app(self):
|
def get_app(self):
|
||||||
"""Return the FastAPI app instance."""
|
"""Return the FastAPI app instance."""
|
||||||
|
|||||||
@@ -41,6 +41,25 @@ def warning(content):
|
|||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def slider(options, images):
|
||||||
|
"""Render a slider using the provided HTML structure."""
|
||||||
|
width = options.get("width", 500)
|
||||||
|
height = options.get("height", 375)
|
||||||
|
|
||||||
|
html_content = []
|
||||||
|
for i, val in enumerate(images):
|
||||||
|
if i % 2 == 0:
|
||||||
|
html_content.append(f"""<button onclick="openModal('modal{i}')" class="open"> <img src="{val}" width={width} height={height}> Open Modal {i} </button>""".strip())
|
||||||
|
else:
|
||||||
|
html_content.append(f"""<button onclick="openModal('modal{i}')" class="open_inv"> <img src="{val}" width={width} height={height}> Open Modal {i} </button>""".strip())
|
||||||
|
|
||||||
|
html_content.append(f"""<div class="modal" id="modal{i}"> <div class="modal-content"> <h2>Modal {i}</h2> <p>This is modal {i}.</p> <img src="{val}" width="80%" height="80%"> <button onclick="closeModal('modal{i}')">Close</button> </div> </div>""")
|
||||||
|
html = '\n'.join( html_content )
|
||||||
|
|
||||||
|
return html
|
||||||
|
|
||||||
|
|
||||||
def create_jinja_environment():
|
def create_jinja_environment():
|
||||||
"""Create and configure the Jinja2 environment."""
|
"""Create and configure the Jinja2 environment."""
|
||||||
env = Environment(loader=DictLoader({"base_template": "{{ content | safe }}"}))
|
env = Environment(loader=DictLoader({"base_template": "{{ content | safe }}"}))
|
||||||
@@ -50,6 +69,7 @@ def create_jinja_environment():
|
|||||||
"note": note,
|
"note": note,
|
||||||
"warning": warning,
|
"warning": warning,
|
||||||
"link_to": link_to,
|
"link_to": link_to,
|
||||||
|
"slider": slider,
|
||||||
})
|
})
|
||||||
return env
|
return env
|
||||||
|
|
||||||
@@ -70,10 +90,15 @@ def render_markdown_with_jinja(markdown_content: str):
|
|||||||
|
|
||||||
# Step 2: Pass the resulting HTML with Jinja2 custom tags through Jinja2
|
# Step 2: Pass the resulting HTML with Jinja2 custom tags through Jinja2
|
||||||
env = create_jinja_environment()
|
env = create_jinja_environment()
|
||||||
|
|
||||||
template = env.get_template("base_template")
|
template = env.get_template("base_template")
|
||||||
final_html = template.render(content=intermediate_html)
|
final_html = template.render(content=intermediate_html)
|
||||||
|
|
||||||
# Step 3: Re-render final_html in Jinja2 for embedded tags like {{ box(...) }}
|
#Step 3: Re-render final_html in Jinja2 for embedded tags like {{ box(...) }}
|
||||||
|
|
||||||
|
try:
|
||||||
final_output = env.from_string(final_html).render()
|
final_output = env.from_string(final_html).render()
|
||||||
|
except:
|
||||||
|
print(final_html)
|
||||||
|
|
||||||
return final_output, metadata
|
return final_output, metadata
|
||||||
|
|||||||
@@ -32,3 +32,10 @@ Det portugisiske køkken er en oplevelse i sig selv:
|
|||||||
Kunst og kultur går hånd i hånd i Portugal. Byer som **Porto** og **Lissabon** er fyldt med street art, museer og gallerier, der udtrykker både moderne og traditionel portugisisk kunst.
|
Kunst og kultur går hånd i hånd i Portugal. Byer som **Porto** og **Lissabon** er fyldt med street art, museer og gallerier, der udtrykker både moderne og traditionel portugisisk kunst.
|
||||||
|
|
||||||
Portugal formår at kombinere historie, musik og mad med en afslappet livsstil. Det er en kultur, der er lige så rig på oplevelser, som den er på sjæl.
|
Portugal formår at kombinere historie, musik og mad med en afslappet livsstil. Det er en kultur, der er lige så rig på oplevelser, som den er på sjæl.
|
||||||
|
|
||||||
|
<h1 style="text-align: center;">FastAPI + Sass Image Carousel</h1>
|
||||||
|
|
||||||
|
|
||||||
|
### Slider test
|
||||||
|
{{ slider(options={"width": 500, "height": 500}, images=["https://picsum.photos/id/34/500/375","https://picsum.photos/id/42/500/375","https://picsum.photos/id/72/500/375","https://picsum.photos/id/94/500/375"]) }}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,17 @@
|
|||||||
<h3>En levende kunstscene</h3>
|
<h3>En levende kunstscene</h3>
|
||||||
<p>Kunst og kultur går hånd i hånd i Portugal. Byer som <strong>Porto</strong> og <strong>Lissabon</strong> er fyldt med street art, museer og gallerier, der udtrykker både moderne og traditionel portugisisk kunst.</p>
|
<p>Kunst og kultur går hånd i hånd i Portugal. Byer som <strong>Porto</strong> og <strong>Lissabon</strong> er fyldt med street art, museer og gallerier, der udtrykker både moderne og traditionel portugisisk kunst.</p>
|
||||||
<p>Portugal formår at kombinere historie, musik og mad med en afslappet livsstil. Det er en kultur, der er lige så rig på oplevelser, som den er på sjæl. </p>
|
<p>Portugal formår at kombinere historie, musik og mad med en afslappet livsstil. Det er en kultur, der er lige så rig på oplevelser, som den er på sjæl. </p>
|
||||||
|
<h1 style="text-align: center;">FastAPI + Sass Image Carousel</h1>
|
||||||
|
|
||||||
|
<h3>Slider test</h3>
|
||||||
|
<p><button onclick="openModal('modal0')" class="open"> <img src="https://picsum.photos/id/34/500/375" width=500 height=500> Open Modal 0 </button>
|
||||||
|
<div class="modal" id="modal0"> <div class="modal-content"> <h2>Modal 0</h2> <p>This is modal 0.</p> <img src="https://picsum.photos/id/34/500/375" width="80%" height="80%"> <button onclick="closeModal('modal0')">Close</button> </div> </div>
|
||||||
|
<button onclick="openModal('modal1')" class="open_inv"> <img src="https://picsum.photos/id/42/500/375" width=500 height=500> Open Modal 1 </button>
|
||||||
|
<div class="modal" id="modal1"> <div class="modal-content"> <h2>Modal 1</h2> <p>This is modal 1.</p> <img src="https://picsum.photos/id/42/500/375" width="80%" height="80%"> <button onclick="closeModal('modal1')">Close</button> </div> </div>
|
||||||
|
<button onclick="openModal('modal2')" class="open"> <img src="https://picsum.photos/id/72/500/375" width=500 height=500> Open Modal 2 </button>
|
||||||
|
<div class="modal" id="modal2"> <div class="modal-content"> <h2>Modal 2</h2> <p>This is modal 2.</p> <img src="https://picsum.photos/id/72/500/375" width="80%" height="80%"> <button onclick="closeModal('modal2')">Close</button> </div> </div>
|
||||||
|
<button onclick="openModal('modal3')" class="open_inv"> <img src="https://picsum.photos/id/94/500/375" width=500 height=500> Open Modal 3 </button>
|
||||||
|
<div class="modal" id="modal3"> <div class="modal-content"> <h2>Modal 3</h2> <p>This is modal 3.</p> <img src="https://picsum.photos/id/94/500/375" width="80%" height="80%"> <button onclick="closeModal('modal3')">Close</button> </div> </div></p>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
14
inspiration/image_slider.html
Normal file
14
inspiration/image_slider.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<main>
|
||||||
|
<div class="scroll-slider hide-scrollbar">
|
||||||
|
<figure class="relative my-0 slider-bg">
|
||||||
|
<img src="https://picsum.photos/id/19/500/375" width="500" height="375">
|
||||||
|
<figcaption class="absolute inset-0 flex flex-col justify-end p-6">
|
||||||
|
<h1 class="my-0">Fylgja CSS Slider</h1>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
<img src="https://picsum.photos/id/34/500/375" width="500" height="375">
|
||||||
|
<img src="https://picsum.photos/id/42/500/375" width="500" height="375">
|
||||||
|
<img src="https://picsum.photos/id/72/500/375" width="500" height="375">
|
||||||
|
<img src="https://picsum.photos/id/94/500/375" width="500" height="375">
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
39
static/css/carousel.css
Normal file
39
static/css/carousel.css
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
.carousel-container {
|
||||||
|
position: relative;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-slide img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.capa {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
color: white;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-left { left: 10px; }
|
||||||
|
.nav-right { right: 10px; }
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
@import 'fontawesome-all.min.css';
|
@import 'fontawesome-all.min.css';
|
||||||
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,600,400italic,600italic|Roboto+Slab:400,700");
|
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,600,400italic,600italic|Roboto+Slab:400,700");
|
||||||
/*
|
/*
|
||||||
@@ -2472,6 +2473,45 @@ button:disabled,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
width: 90%; /* Sæt bredden til 80% af skærmens bredde */
|
||||||
|
height: 90%;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 50000; /* Content stays above the blurred overlay */
|
||||||
|
margin: 10% auto; /* Centrer modalen horisontalt og tilføj vertikal afstand */
|
||||||
|
}
|
||||||
|
|
||||||
|
.open {
|
||||||
|
padding: -20px;
|
||||||
|
margin: -10% auto; /* Centrer modalen horisontalt og tilføj vertikal afstand */
|
||||||
|
transform: rotate(15deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.open_inv {
|
||||||
|
padding: -20px;
|
||||||
|
margin: -10% auto; /* Centrer modalen horisontalt og tilføj vertikal afstand */
|
||||||
|
transform: rotate(-15deg);
|
||||||
|
}
|
||||||
|
|
||||||
/* Wrapper */
|
/* Wrapper */
|
||||||
#wrapper {
|
#wrapper {
|
||||||
display: -moz-flex;
|
display: -moz-flex;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
27
static/js/modal_handler.js
Normal file
27
static/js/modal_handler.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Open a specific modal
|
||||||
|
function openModal(modalId) {
|
||||||
|
const modal = document.getElementById(modalId);
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
console.error(`Modal with ID "${modalId}" not found.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close a specific modal
|
||||||
|
function closeModal(modalId) {
|
||||||
|
const modal = document.getElementById(modalId);
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
console.error(`Modal with ID "${modalId}" not found.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all modals are hidden on page load
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const modals = document.querySelectorAll('.modal'); // Select all modals
|
||||||
|
modals.forEach(modal => {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
});
|
||||||
|
});
|
||||||
52
static/sass/components/_modals.scss
Normal file
52
static/sass/components/_modals.scss
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Variables
|
||||||
|
$modal-bg: rgba(0, 0, 0, 0.3);
|
||||||
|
$modal-blur: 8px;
|
||||||
|
$modal-z-index: 999999;
|
||||||
|
$modal-content-z-index: 50000;
|
||||||
|
$modal-content-bg: #fff;
|
||||||
|
$modal-content-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
// Modal background with blur effect
|
||||||
|
.modal {
|
||||||
|
display: none; // Hidden by default
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: $modal-bg; // Semi-transparent dark background
|
||||||
|
backdrop-filter: blur($modal-blur); // Blurs everything behind the modal
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: $modal-z-index; // Ensure it stays on top
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal content
|
||||||
|
.modal-content {
|
||||||
|
background-color: $modal-content-bg;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: $modal-content-shadow;
|
||||||
|
width: 90%; /* Sæt bredden til 80% af skærmens bredde */
|
||||||
|
height: 90%;
|
||||||
|
// max-width: 1200px; /* Tilføj en maks-bredde for store skærme */
|
||||||
|
text-align: center;
|
||||||
|
z-index: $modal-content-z-index; /* Content stays above the blurred overlay */
|
||||||
|
margin: 10% auto; /* Centrer modalen horisontalt og tilføj vertikal afstand */
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotating buttons
|
||||||
|
.open {
|
||||||
|
padding: -20px;
|
||||||
|
margin: -10% auto; /* Centrer modalen horisontalt og tilføj vertikal afstand */
|
||||||
|
|
||||||
|
transform: rotate(15deg); // Rotates button 15 degrees clockwise
|
||||||
|
}
|
||||||
|
|
||||||
|
.open_inv {
|
||||||
|
padding: -20px;
|
||||||
|
margin: -10% auto; /* Centrer modalen horisontalt og tilføj vertikal afstand */
|
||||||
|
|
||||||
|
transform: rotate(-15deg); // Rotates button 15 degrees counterclockwise
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
@import 'components/mini-posts';
|
@import 'components/mini-posts';
|
||||||
@import 'components/features';
|
@import 'components/features';
|
||||||
@import 'components/posts';
|
@import 'components/posts';
|
||||||
|
@import 'components/modals';
|
||||||
|
|
||||||
// Layout.
|
// Layout.
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>{% block title %}PortugalFAQ{% endblock %}</title>
|
<title>{% block title %}PortugalFAQ{% endblock %}</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
|
||||||
<link rel="stylesheet" href="/static/css/main.css">
|
<link rel="stylesheet" href="/static/css/main.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="is-preload">
|
<body class="is-preload">
|
||||||
@@ -38,11 +38,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="/static/js/jquery.min.js"></script>
|
<script src="/static/js/jquery.min.js"></script>
|
||||||
<script src="/static/js/browser.min.js"></script>
|
<script src="/static/js/browser.min.js"></script>
|
||||||
<script src="/static/js/breakpoints.min.js"></script>
|
<script src="/static/js/breakpoints.min.js"></script>
|
||||||
<script src="/static/js/util.js"></script>
|
<script src="/static/js/util.js"></script>
|
||||||
<script src="/static/js/main.js"></script>
|
<script src="/static/js/main.js"></script>
|
||||||
|
<script src="/static/js/modal_handler.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
14
templates/image_slider.html
Normal file
14
templates/image_slider.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<main>
|
||||||
|
<div class="scroll-slider hide-scrollbar">
|
||||||
|
<figure class="relative my-0 slider-bg">
|
||||||
|
<img src="https://picsum.photos/id/19/500/375" width="500" height="375">
|
||||||
|
<figcaption class="absolute inset-0 flex flex-col justify-end p-6">
|
||||||
|
<h1 class="my-0">Fylgja CSS Slider</h1>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
<img src="https://picsum.photos/id/34/500/375" width="500" height="375">
|
||||||
|
<img src="https://picsum.photos/id/42/500/375" width="500" height="375">
|
||||||
|
<img src="https://picsum.photos/id/72/500/375" width="500" height="375">
|
||||||
|
<img src="https://picsum.photos/id/94/500/375" width="500" height="375">
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
Reference in New Issue
Block a user