]> git.localhorst.tv Git - alttp.git/commitdiff
list items shown on map
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 6 Feb 2023 12:04:17 +0000 (13:04 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 6 Feb 2023 12:04:17 +0000 (13:04 +0100)
resources/js/components/common/Icon.js
resources/js/components/map/Item.js [new file with mode: 0644]
resources/js/components/map/List.js [new file with mode: 0644]
resources/js/components/pages/Map.js
resources/js/components/techniques/List.js
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/map.scss

index c9d604abd453fb3ecf41691d3147f28f450f2bd0..3e094ccab30643a4e2cbbe19c275176123c884d5 100644 (file)
@@ -63,6 +63,7 @@ Icon.ALLOWED = makePreset('AllowedIcon', 'square-check');
 Icon.APPLY = makePreset('ApplyIcon', 'right-to-bracket');
 Icon.APPLICATIONS = makePreset('ApplicationsIcon', 'person-running');
 Icon.CHART = makePreset('ChartIcon', 'chart-line');
+Icon.CROSSHAIRS = makePreset('CrosshairsIcon', 'crosshairs');
 Icon.DISCORD = makePreset('DiscordIcon', ['fab', 'discord']);
 Icon.EDIT = makePreset('EditIcon', 'edit');
 Icon.FINISHED = makePreset('FinishedIcon', 'square-check');
diff --git a/resources/js/components/map/Item.js b/resources/js/components/map/Item.js
new file mode 100644 (file)
index 0000000..4bd209a
--- /dev/null
@@ -0,0 +1,94 @@
+import OpenSeadragon from 'openseadragon';
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Button } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import { Link } from 'react-router-dom';
+
+import { useOpenSeadragon } from './OpenSeadragon';
+import Icon from '../common/Icon';
+import Rulesets from '../techniques/Rulesets';
+import {
+       getLink,
+       getRelations,
+       getTranslation,
+       hasRelations,
+       sorted,
+} from '../../helpers/Technique';
+import i18n from '../../i18n';
+
+const Item = ({ pin }) => {
+       const { viewer } = useOpenSeadragon();
+       const { t } = useTranslation();
+
+       const goToLocation = React.useCallback(pin => {
+               if (viewer && viewer.viewport) {
+                       viewer.viewport.panTo(new OpenSeadragon.Point(pin.x, pin.y));
+                       viewer.viewport.zoomTo(4);
+                       viewer.element.scrollIntoView();
+               }
+       }, [viewer]);
+
+       return <li className="d-flex align-items-start justify-content-between">
+               <div className="flex-grow-1">
+                               {pin.technique.type === 'location' ? <>
+                                       <h2>{getTranslation(pin.technique, 'title', i18n.language)}</h2>
+                                       <p>{getTranslation(pin.technique, 'short', i18n.language)}</p>
+                                       {hasRelations(pin.technique, 'related') ?
+                                               sorted(getRelations(pin.technique, 'related')).map(r =>
+                                                       <div
+                                                               className="d-flex align-items-start justify-content-between"
+                                                               key={r.id}
+                                                       >
+                                                               <div className="me-auto">
+                                                                       <h3>
+                                                                               <Link to={getLink(r)}>
+                                                                                       {getTranslation(r, 'title', i18n.language)}
+                                                                               </Link>
+                                                                       </h3>
+                                                                       <p>{getTranslation(r, 'short', i18n.language)}</p>
+                                                               </div>
+                                                               {r.rulesets ?
+                                                                       <Rulesets technique={r} />
+                                                               : null}
+                                                       </div>
+                                               )
+                                       : null}
+                               </> : <div className="d-flex align-items-start justify-content-between">
+                                       <div className="flex-grow-1">
+                                               <h2>
+                                                       <Link to={getLink(pin.technique)}>
+                                                               {getTranslation(pin.technique, 'title', i18n.language)}
+                                                       </Link>
+                                               </h2>
+                                               <p>{getTranslation(pin.technique, 'short', i18n.language)}</p>
+                                       </div>
+                                       {pin.technique.rulesets ?
+                                               <Rulesets technique={pin.technique} />
+                                       : null}
+                               </div>}
+                       </div>
+               <Button
+                       className="m-2"
+                       onClick={() => goToLocation(pin)}
+                       title={t('map.goToLocation')}
+                       variant="outline-secondary"
+               >
+                       <Icon.CROSSHAIRS title="" />
+               </Button>
+       </li>;
+};
+
+Item.propTypes = {
+       pin: PropTypes.shape({
+               technique: PropTypes.shape({
+                       rulesets: PropTypes.shape({
+                       }),
+                       type: PropTypes.string,
+               }),
+               x: PropTypes.number,
+               y: PropTypes.number,
+       }),
+};
+
+export default Item;
diff --git a/resources/js/components/map/List.js b/resources/js/components/map/List.js
new file mode 100644 (file)
index 0000000..7b66630
--- /dev/null
@@ -0,0 +1,31 @@
+import React from 'react';
+import { Container } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+
+import Item from './Item';
+import { useOpenSeadragon } from './OpenSeadragon';
+import { compareTranslation } from '../../helpers/Technique';
+import i18n from '../../i18n';
+
+const List = () => {
+       const { pins } = useOpenSeadragon();
+       const { t } = useTranslation();
+
+       const sortedPins = React.useMemo(() => {
+               const compare = compareTranslation('title', i18n.language);
+               return pins.sort((a, b) => compare(a.technique, b.technique));
+       }, [pins, i18n.language]);
+
+       if (!pins || !pins.length) return null;
+
+       return <Container className="mt-3">
+               <h2>{t('map.onThisMap')}</h2>
+               <ul className="pin-list">
+                       {sortedPins.map(pin =>
+                               <Item key={pin.id} pin={pin} />
+                       )}
+               </ul>
+       </Container>;
+};
+
+export default List;
index 44ac0a05f5a441e8ecd368ec1cb2a81b1a4b3590..841227cd4ac45a8c5ac0433359748b4bf0c3ca2d 100644 (file)
@@ -3,6 +3,7 @@ import { Container } from 'react-bootstrap';
 import { useTranslation } from 'react-i18next';
 
 import Buttons from '../map/Buttons';
+import List from '../map/List';
 import OpenSeadragon from '../map/OpenSeadragon';
 import Pins from '../map/Pins';
 
@@ -22,6 +23,7 @@ const Map = () => {
                        </div>
                        <div ref={container} style={{ height: '80vh' }} />
                        <Pins />
+                       <List />
                </OpenSeadragon>
        </Container>;
 };
index e18937e9d21ff5660fa44d9e53e09912c1106dec..aa80836c562098cefc3e5303ed8466b2bafcb941 100644 (file)
@@ -20,9 +20,9 @@ const List = ({ techniques }) => <ul className="tech-list">
                                </h2>
                                <p>{getTranslation(tech, 'short', i18n.language)}</p>
                        </div>
-               {tech.rulesets ?
-                       <Rulesets technique={tech} />
-               : null}
+                       {tech.rulesets ?
+                               <Rulesets technique={tech} />
+                       : null}
                </li>
        )}
 </ul>;
index 0f7c8ff5c0ddccac8735b4cecafe3149cba9fc5c..02adc9da8ea8b35937532b1f03f3a17c18003114 100644 (file)
@@ -379,9 +379,11 @@ export default {
                map: {
                        dwLong: 'Dark World',
                        dwShort: 'DW',
+                       goToLocation: 'Zur Stelle springen',
                        heading: 'Karte',
                        lwLong: 'Light World',
                        lwShort: 'LW',
+                       onThisMap: 'Auf dieser Karte',
                        spLong: 'Spezielle Gebiete',
                        spShort: 'SP',
                        uwLong: 'Underworld',
index 7414796e70157c6107eb8a7b3583a32319e0ce00..efb99b385fc6487f4e59ab126ec8869f2e351021 100644 (file)
@@ -379,9 +379,11 @@ export default {
                map: {
                        dwLong: 'Dark World',
                        dwShort: 'DW',
+                       goToLocation: 'Go to location',
                        heading: 'Map',
                        lwLong: 'Light World',
                        lwShort: 'LW',
+                       onThisMap: 'On this map',
                        spLong: 'Special Areas',
                        spShort: 'SP',
                        uwLong: 'Underworld',
index 95d645ad178a01868d4d85d04fa156a128825be5..599cd15111a0d81184b591232d4a9dd368891ec5 100644 (file)
                display: none;
        }
 }
+
+.pin-list {
+       margin: 1em 0;
+       padding: 0;
+       list-style: none;
+
+       li {
+               margin: 1ex 0;
+               padding: 1ex;
+               border-top: thin solid silver;
+       }
+
+       h2 > a, h3 > a {
+               text-decoration: none;
+       }
+}