From aea1d4f48c01f4c152d2f98bd0dbee4da4812879 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 11 Mar 2024 10:37:36 +0100 Subject: [PATCH 1/6] Update ruff in pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3efdd32..12b7ac6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.2.0' + rev: 'v0.3.1' hooks: - id: ruff From 44e4d81a50054c9ad66b076322db4d363b0268f3 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 11 Mar 2024 10:39:08 +0100 Subject: [PATCH 2/6] Integrate app.scss and SCSS components from empowerplan --- .../static/{css/project.css => scss/app.scss} | 16 +- slapp/static/scss/base/_fonts.scss | 102 ++++ slapp/static/scss/base/_mixins.scss | 106 ++++ .../static/scss/base/_variable_overrides.scss | 525 ++++++++++++++++++ slapp/static/scss/base/_variables.scss | 96 ++++ slapp/static/scss/components/_controls.scss | 80 +++ slapp/static/scss/components/_map.scss | 215 +++++++ slapp/templates/base.html | 3 +- 8 files changed, 1135 insertions(+), 8 deletions(-) rename slapp/static/{css/project.css => scss/app.scss} (73%) create mode 100644 slapp/static/scss/base/_fonts.scss create mode 100644 slapp/static/scss/base/_mixins.scss create mode 100644 slapp/static/scss/base/_variable_overrides.scss create mode 100644 slapp/static/scss/base/_variables.scss create mode 100644 slapp/static/scss/components/_controls.scss create mode 100644 slapp/static/scss/components/_map.scss diff --git a/slapp/static/css/project.css b/slapp/static/scss/app.scss similarity index 73% rename from slapp/static/css/project.css rename to slapp/static/scss/app.scss index 979d16e..6a51ffd 100644 --- a/slapp/static/css/project.css +++ b/slapp/static/scss/app.scss @@ -1,4 +1,13 @@ -/* These styles are generated from project.scss. */ +@charset "utf-8"; + +@import "base/variable_overrides"; + +@import "../vendors/bootstrap/scss/bootstrap"; + +@import 'base/variables'; + +@import 'components/map'; +@import 'components/controls'; .alert-debug { color: black; @@ -35,8 +44,3 @@ border: 1px solid #ddd; padding: 10px; } - -#map { - width: 100%; - height: 100% -} diff --git a/slapp/static/scss/base/_fonts.scss b/slapp/static/scss/base/_fonts.scss new file mode 100644 index 0000000..4401325 --- /dev/null +++ b/slapp/static/scss/base/_fonts.scss @@ -0,0 +1,102 @@ +//Apache License +//Version 2.0, January 2004 +//http://www.apache.org/licenses/ + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-Bold.woff2') format('woff2'), + url('../fonts/Roboto-Bold.woff') format('woff'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-BoldItalic.woff2') format('woff2'), + url('../fonts/Roboto-BoldItalic.woff') format('woff'); + font-weight: 700; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-Thin.woff2') format('woff2'), + url('../fonts/Roboto-Thin.woff') format('woff'); + font-weight: 100; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-ThinItalic.woff2') format('woff2'), + url('../fonts/Roboto-ThinItalic.woff') format('woff'); + font-weight: 100; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-Light.woff2') format('woff2'), + url('../fonts/Roboto-Light.woff') format('woff'); + font-weight: 300; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-Italic.woff2') format('woff2'), + url('../fonts/Roboto-Italic.woff') format('woff'); + font-weight: 400; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-LightItalic.woff2') format('woff2'), + url('../fonts/Roboto-LightItalic.woff') format('woff'); + font-weight: 300; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Roboto'; + src: url('../fonts/Roboto-Regular.woff2') format('woff2'), + url('../fonts/Roboto-Regular.woff') format('woff'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Roboto Mono'; + src: url('../fonts/RobotoMono-Bold.woff2') format('woff2'), + url('../fonts/RobotoMono-Bold.woff') format('woff'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Roboto Mono'; + src: url('../fonts/RobotoMono-Light.woff2') format('woff2'), + url('../fonts/RobotoMono-Light.woff') format('woff'); + font-weight: 300; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Roboto Mono'; + src: url('../fonts/RobotoMono-Regular.woff2') format('woff2'), + url('../fonts/RobotoMono-Regular.woff') format('woff'); + font-weight: 400; + font-style: normal; + font-display: swap; +} diff --git a/slapp/static/scss/base/_mixins.scss b/slapp/static/scss/base/_mixins.scss new file mode 100644 index 0000000..e7aad3b --- /dev/null +++ b/slapp/static/scss/base/_mixins.scss @@ -0,0 +1,106 @@ +// Flex +@mixin flex-row { + display: flex; + flex-direction: row; +} + +@mixin flex-row-justify-center { + display: flex; + flex-direction: row; + justify-content: center; +} + +@mixin flex-row-space-between { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +@mixin flex-row-flex-start { + display: flex; + flex-direction: row; + justify-content: flex-start; +} + +@mixin flex-column { + display: flex; + flex-direction: column; +} + +@mixin flex-column-align-center { + display: flex; + flex-direction: column; + align-items: center; +} + +@mixin flex-column-justify-center { + display: flex; + flex-direction: column; + justify-content: center; +} + +@mixin flex-column-align-justify-center { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +// Translate +@mixin translateY($translateY-value) { + transform: translateY($translateY-value); + -webkit-transform: translateY($translateY-value); + -moz-transform: translateY($translateY-value); + -ms-transform: translateY($translateY-value); + -o-transform: translateY($translateY-value); +} + +@mixin translateX($translateX-value) { + transform: translateX($translateX-value); + -webkit-transform: translateX($translateX-value); + -moz-transform: translateX($translateX-value); + -ms-transform: translateX($translateX-value); + -o-transform: translateX($translateX-value); +} + +// User select +@mixin user-select-none { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; +} + +// UL +@mixin ul-horizontal { + @extend .list-unstyled; + @extend .mb-0; + @extend .ps-0; +} + +// Info icon +@mixin info-icon { + padding-left: 0.35rem; + cursor: pointer; + vertical-align: middle; +} + +// User select +@mixin user-select-none { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +// Map controls and legends +@mixin map-control-legend { + position: $map-control-position; + padding: $padding-small; + border-radius: $button-border-radius; + background-color: $map-control-bg-color; + box-shadow: $box-shadow; + z-index: $map-control-z-index; + @include user-select-none; +} diff --git a/slapp/static/scss/base/_variable_overrides.scss b/slapp/static/scss/base/_variable_overrides.scss new file mode 100644 index 0000000..5c9ed8b --- /dev/null +++ b/slapp/static/scss/base/_variable_overrides.scss @@ -0,0 +1,525 @@ +// Include any default variable overrides here (though functions won't be available). + +// Variables +// +// Variables should follow the `$component-state-property-size` formula for +// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs. + +// Color system + +// scss-docs-start gray-color-variables +$gray-100: #F4F6F7; +$gray-200: #E3E8EB; +$gray-300: #BECAD0; +$gray-400: #99ACB5; +$gray-500: #758E9A; +$gray-600: #576D78; +$gray-700: #3C4B53; +$gray-800: #222A2E; +$gray-900: #070809; +// scss-docs-end gray-color-variables + +// fusv-disable +$purple-100: #FEF8FF; +$purple-200: #FBEBFF; +$purple-300: #EEC0FA; +$purple-400: #DD92EF; +$purple-500: #CF6BE8; +$purple-600: #B83FD7; +$purple-700: #9920B7; +$purple-800: #700E88; +$purple-900: #49065A; + +$red-100: #FFF6F8; +$red-200: #FFEBED; +$red-300: #FAC7CD; +$red-400: #F09EA9; +$red-500: #E5707F; +$red-600: #CD4759; +$red-700: #AB2134; +$red-800: #860E1E; +$red-900: #640B17; + +$yellow-100: #FFFDF5; +$yellow-200: #FFFBEA; +$yellow-300: #FBEEAE; +$yellow-400: #F9E171; +$yellow-500: #F8D637; +$yellow-600: #F0C808; +$yellow-700: #D0AE07; +$yellow-800: #947B02; +$yellow-900: #5F4F03; + +$green-100: #F6FFFC; +$green-200: #EBFFF9; +$green-300: #BDF8E5; +$green-400: #73DDBC; +$green-500: #4AC29D; +$green-600: #21A179; +$green-700: #13825F; +$green-800: #09684A; +$green-900: #06543B; + +$cyan-100: #F6FCFF; +$cyan-200: #E9F6FE; +$cyan-300: #BAE1F6; +$cyan-400: #8BC8EA; +$cyan-500: #67B7E3; +$cyan-600: #3FA0D7; +$cyan-700: #2080B6; +$cyan-800: #0D547C; +$cyan-900: #074061; +// scss-docs-start gray-colors-map +// scss-docs-end gray-colors-map +// fusv-enable + +// scss-docs-start color-variables +$blue: #002E50; +$black: #292929; +$cyan: #2348C3; +// scss-docs-end color-variables + +// scss-docs-start colors-map +// scss-docs-end colors-map + +// scss-docs-start theme-color-variables +// scss-docs-end theme-color-variables + +// scss-docs-start theme-colors-map +// scss-docs-end theme-colors-map + +// scss-docs-start theme-colors-rgb +// scss-docs-end theme-colors-rgb + +// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.0 are 3, 4.5 and 7. +// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast + +// Customize the light and dark text colors for use in our color contrast function. + +// fusv-disable +// fusv-enable + +// Characters which are escaped by the escape-svg function + +// Options +// +// Quickly modify global styling by enabling or disabling optional features. + +// Prefix for :root CSS variables + +// Gradient +// +// The gradient which is added to components if `$enable-gradients` is `true` +// This gradient is also added to elements with `.bg-gradient` +// scss-docs-start variable-gradient +// scss-docs-end variable-gradient + +// Spacing +// +// Control the default styling of most Bootstrap elements by modifying these +// variables. Mostly focused on spacing. +// You can add more entries to the $spacers map, should you need more variation. + +// scss-docs-start spacer-variables-maps +$spacer: 1rem; +$spacers: ( + 0: 0, + 1: $spacer * .5, // 0.5rem + 2: $spacer, // 1rem + 3: $spacer * 1.5, // 1.5rem + 4: $spacer * 2, // 2rem + 5: $spacer * 4, // 4rem +); +// scss-docs-end spacer-variables-maps + +// Position +// +// Define the edge positioning anchors of the position utilities. + +// scss-docs-start position-map +// scss-docs-end position-map + +// Body +// +// Settings for the `` element. +$body-color: $blue; +// Utilities maps +// +// Extends the default `$theme-colors` maps to help create our utilities. + +// Come v6, we'll de-dupe these variables. Until then, for backward compatibility, we keep them to reassign. +// scss-docs-start utilities-colors +// scss-docs-end utilities-colors + +// scss-docs-start utilities-text-colors + +// scss-docs-end utilities-text-colors + +// scss-docs-start utilities-bg-colors +// scss-docs-end utilities-bg-colors + +// Links +// +// Style anchor elements. +$link-color: #0060AF; +$link-decoration: none; + +// Paragraphs +// +// Style p element. + + +// Grid breakpoints +// +// Define the minimum dimensions at which your layout will change, +// adapting to different screen sizes, for use in media queries. + +// scss-docs-start grid-breakpoints +// scss-docs-end grid-breakpoints + + +// Grid containers +// +// Define the maximum width of `.container` for different screen sizes. + +// scss-docs-start container-max-widths +// scss-docs-end container-max-widths + + +// Grid columns +// +// Set the number of columns and specify the width of the gutters. + +// Container padding +$container-padding-x: 2rem; + +// Components +// +// Define common padding and border radius sizes and more. + +// scss-docs-start border-variables +$border-widths: ( + 1: 1px, + 2: 2px, + 3: 3px, + 4: 4px, + 5: 5px +); +$custom-border-widths: ( + 6: 8px, + 7: 12px, + 8: 16px +); +$border-widths: map-merge($border-widths, $custom-border-widths); + +$border-color: $gray-200; +// scss-docs-end border-variables + +// scss-docs-start border-radius-variables +// scss-docs-end border-radius-variables + +// scss-docs-start box-shadow-variables +$box-shadow: 0 .5rem 1rem rgba($blue, .15); +$box-shadow-sm: 0 .125rem .25rem rgba($blue, .075); +$box-shadow-lg: 0 1rem 3rem rgba($blue, .175); +$box-shadow-inset: inset 0 1px 2px rgba($blue, .075); +// scss-docs-end box-shadow-variables + +// scss-docs-start caret-variables +// scss-docs-end caret-variables + +// scss-docs-start collapse-transition +// scss-docs-end collapse-transition + +// stylelint-disable function-disallowed-list +// scss-docs-start aspect-ratios +// scss-docs-end aspect-ratios +// stylelint-enable function-disallowed-list + +// Typography +// +// Font, line-height, and color for body text, headings, and more. + +// scss-docs-start font-variables +// stylelint-disable value-keyword-case +$font-family-sans-serif: Roboto, sans-serif; +$font-family-monospace: "Roboto Mono", monospace; +// stylelint-enable value-keyword-case + +// $font-size-root affects the value of `rem`, which is used for as well font sizes, paddings, and margins +// $font-size-base affects the font size of the body text +$font-size-base: 1rem; + +$h1-font-size: $font-size-base * 2; +$h2-font-size: $font-size-base * 1.5; +$h3-font-size: $font-size-base * 1.25; +$h4-font-size: $font-size-base * 1.25; +$h5-font-size: $font-size-base; +$h6-font-size: $font-size-base; +// scss-docs-end font-variables + +// scss-docs-start font-sizes +$font-sizes: ( + 1: $h1-font-size, + 2: $h2-font-size, + 3: $h3-font-size, + 4: $h4-font-size, + 5: $h5-font-size, + 6: $h6-font-size +); + +$custom-fs: ( + "7": 0.875rem, + "8": 0.75rem +); + +$font-sizes: map-merge($font-sizes, $custom-fs); +// scss-docs-end font-sizes + +// scss-docs-start headings-variables +// scss-docs-end headings-variables + +// scss-docs-start display-headings +// scss-docs-end display-headings + +// scss-docs-start type-variables +$text-muted: rgba(0, 46, 80, 0.5) !default; +// scss-docs-end type-variables + + +// Tables +// +// Customizes the `.table` component with basic values, each used across all table variations. + +// scss-docs-start table-variables +// scss-docs-end table-variables + +// scss-docs-start table-loop +// scss-docs-end table-loop + + +// Buttons + Forms +// +// Shared variables that are reassigned to `$input-` and `$btn-` specific variables. + +// scss-docs-start input-btn-variables +// scss-docs-end input-btn-variables + + +// Buttons +// +// For each of Bootstrap's buttons, define text, background, and border color. + +// scss-docs-start btn-variables +$btn-font-weight: 400; + +// Allows for customizing button radius independently from global border radius +$btn-border-radius: 100px; +$btn-border-radius-sm: 100px; +$btn-border-radius-lg: 100px; +// scss-docs-end btn-variables + + +// Forms + +// scss-docs-start form-text-variables +// scss-docs-end form-text-variables + +// scss-docs-start form-label-variables +// scss-docs-end form-label-variables + +// scss-docs-start form-input-variables +// scss-docs-end form-input-variables + +// scss-docs-start form-check-variables +// scss-docs-end form-check-variables + +// scss-docs-start form-switch-variables +// scss-docs-end form-switch-variables + +// scss-docs-start input-group-variables +// scss-docs-end input-group-variables + +// scss-docs-start form-select-variables +// scss-docs-end form-select-variables + +// scss-docs-start form-range-variables +// scss-docs-end form-range-variables + +// scss-docs-start form-file-variables +// scss-docs-end form-file-variables + +// scss-docs-start form-floating-variables +// scss-docs-end form-floating-variables + +// Form validation + +// scss-docs-start form-feedback-variables +// scss-docs-end form-feedback-variables + +// scss-docs-start form-validation-states +// scss-docs-end form-validation-states + +// Z-index master list +// +// Warning: Avoid customizing these values. They're used for a bird's eye view +// of components dependent on the z-axis and are designed to all work together. + +// scss-docs-start zindex-stack +$zindex-modal: 1210; +$zindex-modal-backdrop: 1190; +// scss-docs-end zindex-stack + + +// Navs + +// scss-docs-start nav-variables +// scss-docs-end nav-variables + + +// Navbar + +// scss-docs-start navbar-variables +$navbar-nav-link-padding-x: 1rem; +// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link +// scss-docs-end navbar-variables + +// scss-docs-start navbar-theme-variables +$navbar-light-color: $blue; +$navbar-light-hover-color: rgba($blue, .7); +$navbar-light-active-color: rgba($blue, .9); +$navbar-light-disabled-color: rgba($blue, .3); +$navbar-light-toggler-icon-bg: url("data:image/svg+xml,"); +$navbar-light-toggler-border-color: none; +// scss-docs-end navbar-theme-variables + + +// Dropdowns +// +// Dropdown menu container and contents. + +// scss-docs-start dropdown-variables +// scss-docs-end dropdown-variables + +// scss-docs-start dropdown-dark-variables +// scss-docs-end dropdown-dark-variables + + +// Pagination + +// scss-docs-start pagination-variables +// scss-docs-end pagination-variables + + +// Placeholders + +// scss-docs-start placeholders +// scss-docs-end placeholders + +// Cards + +// scss-docs-start card-variables +// scss-docs-end card-variables + +// Accordion + +// scss-docs-start accordion-variables +// scss-docs-end accordion-variables + +// Tooltips + +// scss-docs-start tooltip-variables +// scss-docs-end tooltip-variables + +// Form tooltips must come after regular tooltips +// scss-docs-start tooltip-feedback-variables +// scss-docs-end tooltip-feedback-variables + + +// Popovers + +// scss-docs-start popover-variables +// scss-docs-end popover-variables + + +// Toasts + +// scss-docs-start toast-variables +// scss-docs-end toast-variables + + +// Badges + +// scss-docs-start badge-variables +// scss-docs-end badge-variables + + +// Modals + +// scss-docs-start modal-variables +// scss-docs-end modal-variables + + +// Alerts +// +// Define alert colors, border radius, and padding. + +// scss-docs-start alert-variables +// scss-docs-end alert-variables + + +// Progress bars + +// scss-docs-start progress-variables +// scss-docs-end progress-variables + + +// List group + +// scss-docs-start list-group-variables +// scss-docs-end list-group-variables + + +// Image thumbnails + +// scss-docs-start thumbnail-variables +// scss-docs-end thumbnail-variables + + +// Figures + +// scss-docs-start figure-variables +// scss-docs-end figure-variables + + +// Breadcrumbs + +// scss-docs-start breadcrumb-variables +// scss-docs-end breadcrumb-variables + +// Carousel + +// scss-docs-start carousel-variables +// scss-docs-end carousel-variables + + +// Spinners + +// scss-docs-start spinner-variables +// scss-docs-end spinner-variables + + +// Close + +// scss-docs-start close-variables +// scss-docs-end close-variables + + +// Offcanvas + +// scss-docs-start offcanvas-variables +$offcanvas-border-width: 0; +$offcanvas-transition-duration: 0; +// scss-docs-end offcanvas-variables + +// Code diff --git a/slapp/static/scss/base/_variables.scss b/slapp/static/scss/base/_variables.scss new file mode 100644 index 0000000..5719751 --- /dev/null +++ b/slapp/static/scss/base/_variables.scss @@ -0,0 +1,96 @@ +// Padding +$padding-large: 1.5rem; +$padding-normal: 1rem; +$padding-small: 0.5rem; + +// Buttons +$button-border-radius: 8px; + +// COLORS +$c-color-gray: #CAD3DB; +$c-color-yellow: rgb(250, 174, 29); +$gray-3: rgb(98, 98, 98); + +$c-color-biomass: #99D8C9; +$c-color-conventional: #A6BDDB; +$c-color-pvground: #FA9FB5; +$c-color-pvroof: #FEC44F; + +$windlight: #67B7E3; +$winddark: #273f73; +$pvlight: #F8D637; +$pvdark: #c19800; + +// TYPOGRAPHY +$c-letter-spacing: 0.03rem; +$font-size-xlarge: 1.953rem; // 31.25px +$font-size-large: 1.563rem; // 25px +$font-size-medium: 1.25rem; // 20px +$font-size-normal: 1rem; // 16px +$font-size-small: .875rem; // 14px +$font-size-xsmall: .75rem; // 12px + +$letter-spacing-small: 0.01rem; +$letter-spacing-normal: 0.03rem; +$letter-spacing-large: 0.06rem; + +// LEFT PANEL +$panel-width-sm: 22rem; +$panel-width: 26rem; +$panel-side-padding: 1.5rem; +$panel-results-nav-button-padding: 0.5rem; + +// Media-breaks +$small-screen: 576px; +$medium-screen: 768px; +$large-screen: 992px; +$xlarge-screen: 1200px; +$xxlarge-screen: 1400px; + +// TOP-NAV +$top-nav-height: 3.375rem; + +// WIZARD +$wizard-height: 3.75rem; + +// STEPS +$steps-height: 3rem; +$steps-numbers-size: 1.5rem; + +// FOOTER +$footer-height: 1.75rem; + +// MAP +$map-height: calc(100vh - #{$top-nav-height} - #{$wizard-height} - #{$footer-height}); +$map-padding-x-small: 2rem; +$map-layers-btn-size: 3rem; +$map-items-padding: 1rem; +$map-layers-box-legend-color-size: 0.875rem; +$map-layers-box-height: 62vh; +$map-layers-box-width: 18rem; +$map-layers-box-z-index: 200; +$map-legend-box-height: 8.5rem; +$map-legend-box-padding-bottom: 2rem; + +// Map controls +$map-control-position: absolute; +$map-control-img-size: 3rem; +$map-control-text-height: 1rem; +$map-control-total-height: calc((#{$padding-small} * 2) + #{$map-control-img-size} + #{$map-control-text-height}); +$map-control-position-top: $map-items-padding; +$map-control-position-right: 1rem; +$map-control-position-left: 1rem; +$map-control-z-index: 9; +$map-control-bg-color: rgba(255, 255, 255, 1); + +// POPUP +$popup-width: 28rem; + +// ICONS +$info-icon-size: 1rem; +$info-icon-margin-left: 0.25rem; +$icon-margin-right: 0.25rem; + +// ANIMATIONS +$animation-transition: transform 0.3s ease; +$animation-transform: translateX(4px); diff --git a/slapp/static/scss/components/_controls.scss b/slapp/static/scss/components/_controls.scss new file mode 100644 index 0000000..17d797a --- /dev/null +++ b/slapp/static/scss/components/_controls.scss @@ -0,0 +1,80 @@ +/////////////////////////////////////// SWITCHES /////////////////////////////////////// +.form-check.form-switch .form-check-input { + margin-left: 0; + + &:checked { + background-color: $blue; + border-color: $blue; + } + + &:focus { + border-color: $blue; + } + + &:not(:checked):focus { + background-image: url("data:image/svg+xml,"); + } +} + +.c-switch { + .form-check-input { + clear: left; + } + + .form-switch.form-switch { + @extend .mb-1; + @extend .ps-0; + } + + .form-switch.form-switch .form-check-input { + @extend .rounded-pill; + height: 1rem; + width: calc(1rem + 0.75rem); + } +} + +/////////////////////////////////////// SLIDERS /////////////////////////////////////// +.irs { + font-family: 'Roboto', sans-serif !important; +} + +.irs.irs--flat { + .irs-line { + background-color: $gray-200; + } + + .irs-from, + .irs-to, + .irs-bar, + .irs-single { + background-color: $blue; + } + + .irs-bar, + .irs-line { + height: 8px; + } + + .irs-from:before, + .irs-to:before, + .irs-single:before { + border-top-color: $blue; + } + + .irs-handle { + height: 14px; + + &>i:first-child, + &.state_hover>i:first-child, + &:hover>i:first-child { + background-color: $blue; + } + } +} + +/////////////////////////////////////// INFO ICON /////////////////////////////////////// +img.i-icon { + margin-left: $info-icon-margin-left; + width: $info-icon-size; + height: $info-icon-size; +} diff --git a/slapp/static/scss/components/_map.scss b/slapp/static/scss/components/_map.scss new file mode 100644 index 0000000..8aaf85a --- /dev/null +++ b/slapp/static/scss/components/_map.scss @@ -0,0 +1,215 @@ +.map-content { + @extend .d-flex; + @extend .flex-column; + @extend .flex-md-row; + height: calc(100vh - #{$top-nav-height} - #{$wizard-height}); + overflow-x: hidden; +} + +.map-wrap { + //@include flex-column; + position: relative; + overflow-y: auto; + width: 100%; + + @include media-breakpoint-down(md) { + overflow-y: visible; + padding: 0 $map-padding-x-small $map-padding-x-small; + } +} + +#mainTabContent { + @extend .bg-light; + height: $map-height; + overflow-y: auto; +} + +#map { + height: $map-height; +} + +.map { + &__layers-btn { + @extend .d-flex; + @extend .flex-row; + @extend .justify-content-center; + @extend .align-items-center; + @extend .position-absolute; + @extend .rounded-circle; + @extend .bg-white; + @extend .border; + cursor: pointer; + width: $map-layers-btn-size; + height: $map-layers-btn-size; + top: $map-control-position-top; + right: $map-items-padding; + z-index: 9; + + svg { + @extend .d-block; + } + } + + &__layers-box { + @extend .position-absolute; + @extend .bg-white; + @extend .border; + @extend .p-2; + @extend .overflow-auto; + width: $map-layers-box-width; + height: calc(100vh - #{$top-nav-height} - #{$steps-height} - #{$map-legend-box-height} - #{$map-legend-box-padding-bottom} - (2 * #{$map-items-padding}) - #{$footer-height}); + top: $map-items-padding; + right: $map-items-padding; + z-index: $map-layers-box-z-index; + } + + &__layers-close { + @extend .d-flex; + @extend .flex-row; + @extend .justify-content-end; + } + + &__layers-heading { + @extend .fs-7; + @extend .text-uppercase; + @extend .fw-bold; + @extend .mb-1; + + &:not(:first-of-type) { + @extend .pt-2; + } + } +} + +.map-layer { + @extend .d-flex; + @extend .flex-row; + @extend .justify-content-between; + + &__legend { + @extend .d-flex; + @extend .flex-row; + @extend .flex-nowrap; + + &-color { + @extend .d-inline-block; + //@include translateY(2px); + flex: 0 0 1rem; + width: 1rem; + height: 1rem; + + &--rounded { + @extend .rounded-circle; + } + } + + &-text { + @extend .d-inline-block; + @extend .fs-7; + @extend .fw-light; + @extend .ps-1; + } + } + &__control { + @extend .d-flex; + @extend .flex-row; + + &-toggle { + height: 2rem; + } + + img { + @extend .me-1; + } + } +} + +.legend { + @extend .position-absolute; + @extend .border; + @extend .p-2; + @extend .fs-8; + background-color: rgba(255, 255, 255, 0.8); + width: calc(100% - 7rem); + right: $map-padding-x-small; + bottom: 5rem; + z-index: $map-layers-box-z-index; + + @include media-breakpoint-up(sm) { + font-size: $font-size-small !important; + width: $map-layers-box-width; + right: $map-items-padding; + bottom: 2rem; + } + + &__heading { + @extend .d-flex; + @extend .flex-row; + @extend .fw-bold; + } + + &__title { + @extend .text-uppercase; + @extend .pb-1; + } + + &__unit { + @extend .fw-light; + @extend .ps-1; + } + + &__wrap { + @extend .d-flex; + @extend .flex-row; + } + + &__column { + @extend .col-6; + @extend .fs-8; + } + + &__item { + @extend .position-relative; + @extend .ps-4; + + &:before { + content: ''; + @extend .position-absolute; + height: 1.25rem; + width: 1.25rem; + left: 0; + right: 0; + } + } +} + +#legend.legend { + height: $map-legend-box-height; + + .legend { + &__title { + @extend .d-none; + } + + &__detail { + @extend .pb-1; + } + } +} + +#satellite-layer { + position: absolute; + bottom: 2rem; + right: 12rem; + width: 3rem; + height: 3rem; + @extend .rounded; + background-color: rgba(255, 255, 255, 0.8); + border-width: 1px; + border-color: #bdbdbd; + + img { + width: 1.5rem; + height: 1.5rem; + } +} diff --git a/slapp/templates/base.html b/slapp/templates/base.html index 01fbccc..f8038b4 100644 --- a/slapp/templates/base.html +++ b/slapp/templates/base.html @@ -21,8 +21,7 @@ {% compress css %} - - {% endcompress %} From 1c1a04089e65f4192299c24bf9b5343a3febea0a Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 11 Mar 2024 10:39:22 +0100 Subject: [PATCH 3/6] Update mapengine to v1.0.0 --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index f51bda0..06d22fc 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -24,7 +24,7 @@ djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework django-cors-headers==4.3.0 # https://github.com/adamchainz/django-cors-headers # DRF-spectacular for api documentation drf-spectacular==0.27.1 # https://github.com/tfranzel/drf-spectacular -git+https://github.com/rl-institut/django-mapengine.git@v0.18.0 # django-mapengine +django-mapengine==1.0.0 # django-mapengine django-distill>=2.6,<4.0 geojson>=2.5.0,<4.0 django-geojson>=3.1.0,<4.0 From ebb2d26d902ddd3b04dde0a174d025437827faab Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 11 Mar 2024 10:40:16 +0100 Subject: [PATCH 4/6] Implement layer controls --- slapp/explorer/forms.py | 39 +++ slapp/explorer/map_config.py | 247 ++++++++++++++++++ slapp/explorer/views.py | 8 +- slapp/explorer/widgets.py | 9 + slapp/static/images/icons/i_info.svg | 4 + slapp/templates/forms/layer.html | 25 ++ slapp/templates/pages/map.html | 1 + slapp/templates/pages/partials/layer_box.html | 22 ++ slapp/templates/widgets/info_button.html | 7 + slapp/templates/widgets/switch.html | 27 ++ 10 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 slapp/explorer/forms.py create mode 100644 slapp/explorer/map_config.py create mode 100644 slapp/explorer/widgets.py create mode 100644 slapp/static/images/icons/i_info.svg create mode 100644 slapp/templates/forms/layer.html create mode 100644 slapp/templates/pages/partials/layer_box.html create mode 100644 slapp/templates/widgets/info_button.html create mode 100644 slapp/templates/widgets/switch.html diff --git a/slapp/explorer/forms.py b/slapp/explorer/forms.py new file mode 100644 index 0000000..3a9e5f8 --- /dev/null +++ b/slapp/explorer/forms.py @@ -0,0 +1,39 @@ +"""Form definitions for the explorer app.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +from django.forms import BooleanField, Form, renderers +from django.utils.safestring import mark_safe + +if TYPE_CHECKING: + from django_mapengine.legend import LegendLayer + +from .widgets import SwitchWidget + + +class TemplateForm(Form): # noqa: D101 + template_name = None + + def __str__(self) -> str: # noqa: D105 + if self.template_name: + renderer = renderers.get_default_renderer() + return mark_safe(renderer.render(self.template_name, {"form": self})) # noqa: S308 + return super().__str__() + + +class StaticLayerForm(TemplateForm): # noqa: D101 + template_name = "forms/layer.html" + switch = BooleanField( + label=False, + widget=SwitchWidget( + attrs={ + "switch_class": "form-check form-switch", + "switch_input_class": "form-check-input", + }, + ), + ) + + def __init__(self, layer: LegendLayer, *args, **kwargs) -> None: # noqa: ANN002, D107 + super().__init__(*args, **kwargs) + self.layer = layer diff --git a/slapp/explorer/map_config.py b/slapp/explorer/map_config.py new file mode 100644 index 0000000..36a230c --- /dev/null +++ b/slapp/explorer/map_config.py @@ -0,0 +1,247 @@ +"""Actual map setup is done here.""" +import dataclasses + +from django.utils.translation import gettext_lazy as _ +from django_mapengine import legend + + +@dataclasses.dataclass +class SymbolLegendLayer(legend.LegendLayer): + """Adds symbol field.""" + + symbol: str = "rectangle" + + +LEGEND = { + _("Renewables"): [ + SymbolLegendLayer( + _("Wind turbine"), + _("Windenergieanlagen"), + layer_id="wind", + color="#6A89CC", + symbol="circle", + ), + SymbolLegendLayer( + _("Roof-mounted PV"), + _("PV-Aufdachanlagen"), + layer_id="pvroof", + color="#FFD660", + symbol="circle", + ), + SymbolLegendLayer( + _("Ground-mounted PV"), + _("PV-Freiflächenanlagen"), + layer_id="pvground", + color="#EFAD25", + symbol="circle", + ), + SymbolLegendLayer( + _("Hydro"), + _("Wasserkraftanlagen"), + layer_id="hydro", + color="#A9BDE8", + symbol="circle", + ), + SymbolLegendLayer( + _("Biomass"), + _("Biomasseanlagen"), + layer_id="biomass", + color="#52C41A", + symbol="circle", + ), + SymbolLegendLayer( + _("Combustion"), + _("Verbrennungskraftwerke"), + layer_id="combustion", + color="#E6772E", + symbol="circle", + ), + SymbolLegendLayer( + _("GSGK"), + _("Geo- oder Solarthermie-, Grubengas- und Klärschlamm-Anlagen"), + layer_id="gsgk", + color="#C27BA0", + symbol="circle", + ), + SymbolLegendLayer( + _("Batteriespeicher"), + _("Batteriespeicher"), + layer_id="storage", + color="#8D2D5F", + symbol="circle", + ), + ], + _("Settlements Infrastructure"): [ + legend.LegendLayer( + _("Settlement 0m"), + _( + "Eine Siedlung ist ein Gebiet, welches die menschliche Niederlassung in beliebiger Form der " + "gruppierten Behausung beschreibt. Sie beinhaltet überwiegend Wohngebiete.", + ), + layer_id="settlement-0m", + ), + legend.LegendLayer( + _("Industry"), + _( + "Industrie- und Gewerbegebiete werden ausgewiesen, um störende Einwirkungen von Betrieben wie Lärm, " + "Geruch oder Gefahren auf Wohnbebauung zu vermeiden.", + ), + layer_id="industry", + ), + legend.LegendLayer( + _("Road Railway 500m"), + _( + "Die Flächen längs von Autobahnen oder Schienenwegen werden durch Erstellen einer 500 m breiten " + "Pufferzone abzüglich einer 15 m breiten Pufferzone gebildet.", + ), + layer_id="road_railway-500m_region", + ), + legend.LegendLayer( + _("Road"), + _("Zu den Straßen gehören unter anderem Bundesautobahnen, Bundesfern-, Landes- und Kreisstraßen."), + layer_id="road_default", + ), + legend.LegendLayer( + _("Railway"), + _( + "Der Bahnverkehr ist ein wichtiger Bestandteil der Verkehrsinfrastruktur. Berücksichtigt " + "werden Fernverkehrsbahnen, Regionalverkehrsbahnen und S-Bahnen.", + ), + layer_id="railway", + ), + legend.LegendLayer( + _("Aviation"), + _( + "Zur Infrastruktur des Luftverkehrs gehören neben Start- und Landebahnen die " + "Flughafengebäude und Hangars.", + ), + layer_id="aviation", + ), + legend.LegendLayer( + _("Air Traffic"), + _("Ein Drehfunkfeuer ist ein Funkfeuer für die Luftfahrtnavigation."), + layer_id="air_traffic", + ), + legend.LegendLayer( + _("Military"), + _("Zu den militärisch genutzten Flächen gehören militärische Sperrgebiete und Liegenschaften."), + layer_id="military", + ), + legend.LegendLayer( + _("Grid"), + _( + "Zum Übertragungsnetz zählen die elektrischen Leitungen sowie die dazugehörigen Einrichtungen " + "wie Schalt- und Umspannwerke der Höchst- und Hochspannungsebenen.", + ), + layer_id="grid", + ), + ], + _("Nature Landscape"): [ + legend.LegendLayer( + _("Nature Conservation Area"), + _( + "Naturschutzgebiete dienen dem Schutz der Natur und Landschaft. Sie tragen zur Erhaltung, Entwicklung " + "und Wiederherstellung der Lebensstätte für bestimmte wild lebende Tier- und Pflanzenarten bei. Aber " + "auch aus wissenschaftlichen, naturgeschichtlichen und ästhetischen Gründen werden Teile oder die " + "Gesamtheit der Natur in Schutz genommen.", + ), + layer_id="nature_conservation_area", + ), + legend.LegendLayer( + _("Fauna Flora Habitat"), + _( + "Die Fauna-Flora-Habitat-Richtlinie ist eine Naturschutz-Richtlinie der Europäischen Union (EU), die " + "seltene oder bedrohte Arten und Lebensräume schützt. Sie gehört zum Schutzgebietsnetz Natura 2000.", + ), + layer_id="fauna_flora_habitat", + ), + legend.LegendLayer( + _("Special Protection Area"), + _( + "Die Vogelschutzrichtlinie der Europäischen Union (EU) dient der Erhaltung der wild lebenden, " + "heimischen Vogelarten. Sie regelt den Schutz dieser Vögel, ihrer Eier und Lebensräume wie Brut-, " + "Rast- und Überwinterungsgebiete. Die Vogelschutzgebiete gehören zum Schutzgebietsnetz Natura 2000.", + ), + layer_id="special_protection_area", + ), + legend.LegendLayer( + _("Biosphere Reserve"), + _( + "Biosphärenreservate sind großräumige und für bestimmte Landschaftstypen charakteristische Gebiete " + "mit interdisziplinärem Ansatz. In diesen von der UNESCO initiierten Modellregionen soll nachhaltige " + "Entwicklung in ökologischer, ökonomischer und sozialer Hinsicht exemplarisch verwirklicht werden. " + "Die Biosphärenreservate sind in drei Zonen eingeteilt: Eine naturschutzorientierte Kernzone " + "(Schutzfunktion), eine am Landschaftsschutz orientierte Pflegezone (Forschungs- und Bildungsfunktion)" + " und eine sozioökonomisch orientierte Entwicklungszone (Entwicklungsfunktion).", + ), + layer_id="biosphere_reserve", + ), + legend.LegendLayer( + _("Landscape Protection Area"), + _( + "Landschaftsschutzgebiete sind oft großflächiger angelegt und zielen auf den Erhalt des " + "Landschaftscharakters, das allgemeine Erscheinungsbild der Landschaft und dessen Schönheit ab. " + "Sie haben einen geringeren Schutzstatus als etwa Naturschutzgebiete oder Nationalparke und " + "unterliegen daher weniger strengen Nutzungsbeschränkungen.", + ), + layer_id="landscape_protection_area", + ), + legend.LegendLayer( + _("Forest"), + _( + "Wald umfasst eine Vielzahl an mit Bäumen und anderer Vegetation bedeckten Fläche " + "mit unterschiedlicher forstwirtschaftlicher Nutzung und ökologischer Bedeutung. Wälder können in " + "Nadel-, Laub- und Mischwald sowie anhand der Waldfunktionen (z. B. Schutzwald, Erholungswald) " + "unterschieden werden.", + ), + layer_id="forest", + ), + legend.LegendLayer( + _("Drinking Water Protection Area"), + _( + "Wasserschutzgebiete stellen die öffentliche Wasserversorgung durch die Vermeidung " + "schädlicher Eintragungen in die Gewässer " + "(Grundwasser, oberirdische Gewässer, Küstengewässer) sicher.", + ), + layer_id="drinking_water_protection_area", + ), + legend.LegendLayer( + _("Water"), + _( + "Ein Gewässer ist in der Natur fließendes oder stehendes Wasser. " + "Dazu gehören der Wasserkörper, das Gewässerbett und der Grundwasserleiter.", + ), + layer_id="water", + ), + legend.LegendLayer( + _("Floodplain"), + _( + "Bei Überschwemmungsgebieten handelt es sich um die Flächen, " + "die statistisch gesehen mindestens einmal in hundert Jahren überflutet sein können.", + ), + layer_id="floodplain", + ), + legend.LegendLayer( + _("Soil Quality High"), + _( + "Acker- und Grünlandflächen mit hoher Bodenqualität (Soil Quality Rating (SQR) >= 40). Um die " + "Flächenkonkurrenz zwischen landwirtschaftlicher Nutzung und Energiegewinnung zu minimieren, wird bei " + "den links einstellbaren PV-Freiflächenpotenzialen als Grenzwert ein SQR von 40 angenommen, es werden " + "also lediglich Flächen mit sehr geringer und geringer Ertragsfähigkeit als potenzielle " + "Standorte berücksichtigt.", + ), + layer_id="soil_quality_high", + ), + legend.LegendLayer( + _("Soil Quality Low"), + _( + "Acker- und Grünlandflächen inner- und außerhalb benachteiligter Gebiete mit geringer Bodenqualität " + "(Soil Quality Rating (SQR) < 40). Um die Flächenkonkurrenz zwischen landwirtschaftlicher Nutzung und " + "Energiegewinnung zu minimieren, wird bei den links einstellbaren PV-Freiflächenpotenzialen als " + "Grenzwert ein SQR von 40 angenommen, es werden also lediglich Flächen mit sehr geringer und geringer " + "Ertragsfähigkeit als potenzielle Standorte berücksichtigt.", + ), + layer_id="soil_quality_low", + ), + ], +} diff --git a/slapp/explorer/views.py b/slapp/explorer/views.py index 8a22a0b..a464bed 100644 --- a/slapp/explorer/views.py +++ b/slapp/explorer/views.py @@ -18,6 +18,7 @@ from django.views.generic import TemplateView from django_mapengine import views +from . import forms, map_config from .models import Municipality MAX_MUNICIPALITY_COUNT = 3 @@ -27,7 +28,12 @@ class MapGLView(TemplateView, views.MapEngineMixin): """Single view for the map.""" template_name = "pages/map.html" - extra_context = {} + extra_context = { + "area_switches": { + category: [forms.StaticLayerForm(layer) for layer in layers] + for category, layers in map_config.LEGEND.items() + }, + } def municipalities_details(ids: list[int]) -> list[Municipality]: diff --git a/slapp/explorer/widgets.py b/slapp/explorer/widgets.py new file mode 100644 index 0000000..85860e9 --- /dev/null +++ b/slapp/explorer/widgets.py @@ -0,0 +1,9 @@ +"""Module holds widgets for digiplan.""" + +from django.forms.widgets import Widget + + +class SwitchWidget(Widget): + """Widget to render switches.""" + + template_name = "widgets/switch.html" diff --git a/slapp/static/images/icons/i_info.svg b/slapp/static/images/icons/i_info.svg new file mode 100644 index 0000000..122e8fa --- /dev/null +++ b/slapp/static/images/icons/i_info.svg @@ -0,0 +1,4 @@ + + + + diff --git a/slapp/templates/forms/layer.html b/slapp/templates/forms/layer.html new file mode 100644 index 0000000..7149337 --- /dev/null +++ b/slapp/templates/forms/layer.html @@ -0,0 +1,25 @@ +{% load static i18n %} + +
+
+
+
+
+ +
+
+
+
+ {% include 'widgets/info_button.html' with description=form.layer.description %} +
+
+
+ +
+
+
+
+
diff --git a/slapp/templates/pages/map.html b/slapp/templates/pages/map.html index b153739..c50ba8d 100644 --- a/slapp/templates/pages/map.html +++ b/slapp/templates/pages/map.html @@ -33,6 +33,7 @@

Karte

+ {% include 'pages/partials/layer_box.html' %}
diff --git a/slapp/templates/pages/partials/layer_box.html b/slapp/templates/pages/partials/layer_box.html new file mode 100644 index 0000000..73f0eda --- /dev/null +++ b/slapp/templates/pages/partials/layer_box.html @@ -0,0 +1,22 @@ +
+ + + +
+
+
+ +
+ {% for category, layers in area_switches.items %} +

{{ category }}

+ {% for layer in layers %}{{ layer }}{% endfor %} + {% endfor %} +
diff --git a/slapp/templates/widgets/info_button.html b/slapp/templates/widgets/info_button.html new file mode 100644 index 0000000..6776335 --- /dev/null +++ b/slapp/templates/widgets/info_button.html @@ -0,0 +1,7 @@ +{% load static %} + +Info Icon diff --git a/slapp/templates/widgets/switch.html b/slapp/templates/widgets/switch.html new file mode 100644 index 0000000..12e9d3a --- /dev/null +++ b/slapp/templates/widgets/switch.html @@ -0,0 +1,27 @@ +{% load static %} + +
+
+ {% if widget.attrs.icon %} + + + + {% endif %} +
+ {{ widget.attrs.label }} + {% if widget.attrs.tooltip %} + {% include 'widgets/info_button.html' with description=widget.attrs.tooltip %} + {% endif %} +
+
+
+ + +
+
From 1515ad66b06ddfc87a76eebe09c1021b788da95d Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Wed, 3 Jul 2024 09:19:25 +0200 Subject: [PATCH 5/6] Update mapengine to v2.0.1 --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 06d22fc..6a765b9 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -24,7 +24,7 @@ djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework django-cors-headers==4.3.0 # https://github.com/adamchainz/django-cors-headers # DRF-spectacular for api documentation drf-spectacular==0.27.1 # https://github.com/tfranzel/drf-spectacular -django-mapengine==1.0.0 # django-mapengine +django-mapengine==2.0.1 # django-mapengine django-distill>=2.6,<4.0 geojson>=2.5.0,<4.0 django-geojson>=3.1.0,<4.0 From b792edefc8456406bbec1b3a97dab3975f183679 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Wed, 3 Jul 2024 09:19:51 +0200 Subject: [PATCH 6/6] Fix layer activation via map controls --- slapp/templates/forms/layer.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slapp/templates/forms/layer.html b/slapp/templates/forms/layer.html index 7149337..acc203b 100644 --- a/slapp/templates/forms/layer.html +++ b/slapp/templates/forms/layer.html @@ -6,7 +6,7 @@
- +
@@ -17,7 +17,7 @@
+ id="{{ form.layer.layer_id }}" />