diff --git a/package.json b/package.json index fda61a7..96524f5 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "init-db": "node --env-file-if-exists=.env scripts/init-db.js", "check": "node --check server.js && npm test", "test": "node --test && npm run test:python", + "test:runtime-db": "node --env-file-if-exists=.env --test test/runtime-database-integrity.test.js", "test:coverage": "npm run test:coverage:js && npm run test:coverage:python", "test:python": "python3 -m unittest discover -s test/python -p 'test_*.py'", "test:coverage:js": "c8 --exclude='test/**' --exclude='test-support/**' --reporter=text-summary --reporter=lcov --reports-dir=coverage/js node --test", diff --git a/public/css/editor.css b/public/css/editor.css index 4e29cbf..ccbc661 100644 --- a/public/css/editor.css +++ b/public/css/editor.css @@ -1,27 +1,178 @@ -.editor-entry-panel { - align-items: center; - background: rgba(255, 255, 255, .92); - border: 1px solid rgba(49, 93, 58, .22); - border-radius: 1.2rem; - box-shadow: 0 .5rem 1.4rem rgba(20, 40, 24, .08); - display: flex; - gap: 1rem; - justify-content: space-between; - margin-top: 1.4rem; - padding: 1rem; +.route-control { + position: fixed; + top: calc(env(safe-area-inset-top) + .45rem); + right: calc(max(.65rem, env(safe-area-inset-right)) + var(--icon-button-size) + .5rem); + z-index: 10000; } -.editor-entry-panel h2, -.editor-entry-panel p { +.route-control-button, +.route-control-button.ui-button { + display: grid; + width: min(13rem, calc(100vw - var(--icon-button-size) - 5.2rem)); + min-width: 0; + min-height: var(--icon-button-size); + box-sizing: border-box; + gap: .08rem; margin: 0; + padding: .35rem .75rem; + overflow: hidden; + border: 2px solid rgba(255, 255, 255, .9); + border-radius: 999px; + background: #24462b; + color: #fff; + box-shadow: 0 .15rem .55rem rgba(0, 0, 0, .28); + text-align: left; + cursor: pointer; + touch-action: manipulation; } -.editor-entry-panel p { - color: #4e604f; - margin-top: .25rem; +.route-control-button strong, +.route-control-button small { + display: block; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.route-control-button strong { + font-size: .9rem; + line-height: 1.1; +} + +.route-control-button small { + color: rgba(255, 255, 255, .86); + font-size: .68rem; + line-height: 1.1; +} + +.route-control-button:focus-visible, +.route-control-button.ui-button:focus-visible { + outline: .2rem solid #fff; + outline-offset: .15rem; +} + +.route-control-dropdown { + position: absolute; + top: calc(var(--icon-button-size) + .45rem); + right: 0; + width: min(24rem, calc(100vw - 1.3rem)); + max-height: min(70dvh, 36rem); + box-sizing: border-box; + padding: .8rem; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + border: 1px solid rgba(49, 93, 58, .28); + border-radius: 1rem; + background: #f8f5eb; + color: #233326; + box-shadow: 0 .8rem 2rem rgba(20, 32, 22, .28); + touch-action: pan-y; +} + +.route-control-dropdown section + section { + margin-top: .85rem; + padding-top: .75rem; + border-top: 1px solid rgba(49, 93, 58, .18); +} + +.route-control-dropdown h2 { + margin: 0 0 .45rem; + font-size: .95rem; +} + +.route-control-list { + display: grid; + gap: .4rem; + margin: 0; + padding: 0; + list-style: none; +} + +.route-control-list li > span { + display: block; + padding: .6rem .7rem; + border-radius: .65rem; + background: #fff; + color: #607160; + font-size: .85rem; +} + +.route-control-list button, +.route-control-list button.ui-button { + display: grid; + width: 100%; + min-height: 2.8rem; + box-sizing: border-box; + gap: .12rem; + margin: 0; + padding: .55rem .7rem; + border: 1px solid rgba(49, 93, 58, .25); + border-radius: .7rem; + background: #fff; + color: #233326; + text-align: left; + cursor: pointer; +} + +.route-control-list button strong, +.route-control-list button small { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.route-control-list button small { + color: #607160; + font-size: .75rem; +} + +.route-control-list button[aria-current="true"] { + border-color: #315d3a; + background: #e7f1e9; + box-shadow: inset .25rem 0 0 #315d3a; +} + +.route-control-list button:focus-visible, +.route-control-list button.ui-button:focus-visible { + outline: .2rem solid #1e6fba; + outline-offset: .1rem; +} + +.route-control-actions { + display: grid; + grid-template-columns: 1fr; + gap: .5rem; + margin-top: .85rem; + padding-top: .75rem; + border-top: 1px solid rgba(49, 93, 58, .18); } .editor-button, +.editor-primary-button, +.editor-secondary-button, +.editor-icon-button { + border-radius: .75rem; + font: inherit; + min-height: 2.4rem; + padding: .55rem .85rem; +} + +.editor-button, +.editor-primary-button { + background: #315d3a; + border: 1px solid #315d3a; + color: #fff; +} + +.editor-button { + align-items: center; + display: inline-flex; + justify-content: center; + font-weight: 700; +} + .editor-round-button { align-items: center; background: #315d3a; @@ -128,6 +279,18 @@ padding: 0; } +.editor-route-group { + display: grid; + gap: .45rem; + margin: .2rem 0; +} + +.editor-route-group > h4 { + margin: .45rem 0 0; + color: #315d3a; + font-size: .95rem; +} + .editor-poi-list { counter-reset: editor-poi; } @@ -183,21 +346,6 @@ grid-template-columns: minmax(0, 1fr); } -.editor-primary-button, -.editor-secondary-button, -.editor-icon-button { - border-radius: .75rem; - font: inherit; - min-height: 2.4rem; - padding: .55rem .85rem; -} - -.editor-primary-button { - background: #315d3a; - border: 1px solid #315d3a; - color: #fff; -} - .editor-secondary-button, .editor-icon-button { background: #fff; @@ -228,6 +376,33 @@ margin-top: .5rem; } +@media (max-width: 34rem) { + .route-control { + right: calc(max(.5rem, env(safe-area-inset-right)) + var(--icon-button-size) + .35rem); + } + + .route-control-button, + .route-control-button.ui-button { + width: min(10.5rem, calc(100vw - var(--icon-button-size) - 4.7rem)); + padding-right: .55rem; + padding-left: .55rem; + } + + .route-control-dropdown { + position: fixed; + top: calc(env(safe-area-inset-top) + var(--icon-button-size) + .9rem); + right: max(.65rem, env(safe-area-inset-right)); + left: max(.65rem, env(safe-area-inset-left)); + width: auto; + max-height: calc(100dvh - env(safe-area-inset-top) - 5.4rem); + } + + .editor-dialog { + max-width: calc(100vw - 1rem); + width: calc(100vw - 1rem); + } +} + @media (min-width: 42rem) { .editor-coordinate-grid { grid-template-columns: max-content minmax(0, 1fr); diff --git a/public/index.html b/public/index.html index 02b00d7..f028c9a 100644 --- a/public/index.html +++ b/public/index.html @@ -26,7 +26,6 @@

Wegwichtel

Der Wegwichtel bereitet Routen und Medien vor.

-

Cookies: Diese Anwendung setzt derzeit keine Cookies und verwendet weder Werbung noch Tracking.

-
  1. jQuery laden
  2. @@ -64,11 +62,30 @@
    +
    + + +

    Wegwichtel

    -

    Standort wird für nahe Routen lokal auf diesem Gerät ermittelt.

    @@ -81,7 +98,6 @@

    Alle Routen

    -

    Routen werden geladen …

    - -
    -
    -

    Strecken verwalten

    -

    Strecken, Stationen, Bilder und Audiodateien bearbeiten.

    -
    - +
    +

    Lokale Strecken

    +

    Diese Entwürfe bestehen aus GPX- und JSON-Daten und sind ausschließlich in diesem Browser gespeichert.

    +
      +
    • Lokale Strecken werden geladen …

    • +
    @@ -115,7 +128,6 @@

    Route

    -

    @@ -140,7 +152,6 @@
      -