diff --git a/src/App.js b/src/App.js
new file mode 100644
index 0000000..20440c1
--- /dev/null
+++ b/src/App.js
@@ -0,0 +1,105 @@
+import React, { Component } from 'react';
+import queryString from 'query-string';
+import axios from 'axios';
+import './styles/App.css';
+import TopMenuBar from './components/TopMenuBar';
+import Map from './Map';
+
+class App extends Component {
+ constructor(props) {
+ super(props);
+ const query = queryString.parse(window.location.search);
+ this.state = {
+ searchTerm: null,
+ mappingData: null,
+ loading: false,
+ inputURL: query.url ? query.url : '',
+ };
+ }
+
+ componentDidMount() {
+ if (this.state.inputURL) {
+ this.handleInputURL(this.state.inputURL);
+ }
+ }
+
+ handleInputURL = (url) => {
+ const testURL = url.replace('end=0', 'end=1').concat('&format=json');
+ let query = null;
+ this.setState({ loading: true });
+ axios
+ .get(testURL)
+ .then((response) => response.data)
+ .then((data) => {
+ query = data.query;
+ query.end = data.results_length;
+ this.fetchData(query, testURL);
+ })
+ .catch((error) => {
+ console.error(error);
+ this.setState({ loading: false, searchTerm: null });
+ });
+ };
+
+ fetchData = (query, testURL) => {
+ const i = testURL.indexOf('/query');
+ const baseURL = testURL.substring(0, i + 6);
+ axios
+ .get(baseURL, { params: query })
+ .then((response) => response.data)
+ .then((data) => {
+ //console.log(data);
+ this.setState({ searchTerm: query.q });
+ this.processData(data);
+ })
+ .catch((error) => {
+ console.error(error);
+ });
+ };
+
+ processData = ({ results }) => {
+ const dioceseMap = {};
+ const mappingData = {};
+ mappingData.searchTerm = this.state.searchTerm;
+ results.forEach((result) => {
+ const { context, metadata_fields: metadata } = result;
+ const { record_id, diocese_id } = metadata;
+ if (diocese_id) {
+ if (dioceseMap.hasOwnProperty(diocese_id)) {
+ if (!dioceseMap[diocese_id].has(record_id)) {
+ dioceseMap[diocese_id].add(record_id);
+ mappingData[diocese_id].push({ metadata, context });
+ }
+ } else {
+ dioceseMap[diocese_id] = new Set([record_id]);
+ mappingData[diocese_id] = [{ metadata, context }];
+ }
+ }
+ });
+ //console.log(mappingData);
+ this.setState({
+ mappingData,
+ loading: false,
+ });
+ };
+
+ render() {
+ return (
+
+
+
+ {this.state.loading && (
+
+ )}
+
+ );
+ }
+}
+
+export default App;
diff --git a/src/Map.js b/src/Map.js
new file mode 100644
index 0000000..8733385
--- /dev/null
+++ b/src/Map.js
@@ -0,0 +1,104 @@
+import React, { Component } from 'react';
+import { Map as LeafletMap, ScaleControl } from 'react-leaflet';
+import mapConfig from './assets/map_config';
+import './styles/Map.css';
+import MapBoxLayer from './components/MapBoxLayer';
+import ControlPanel from './components/ControlPanel';
+import InfoPanel from './components/InfoPanel';
+import ColorLegend from './components/ColorLegend';
+import RecordsModal from './components/RecordsModal';
+import GeoJSONLayer from './components/GeoJSONLayer';
+
+class LocalLegislationMap extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ config: mapConfig,
+ currentColorScheme: 'color1',
+ info: null,
+ recordsModalOpen: false,
+ searchResults: null,
+ };
+ this.mapRef = React.createRef();
+ }
+
+ showRecordsModal = (searchResults) => {
+ this.setState({
+ searchResults: searchResults,
+ recordsModalOpen: true,
+ });
+ };
+
+ closeRecordsModal = () => {
+ this.setState({ recordsModalOpen: false });
+ };
+
+ changeColorScheme = (colorScheme) => {
+ this.setState({
+ currentColorScheme: colorScheme,
+ });
+ };
+
+ updateInfo = (info) => {
+ this.setState({
+ info: info,
+ });
+ };
+
+ getMaxNumEntries = () => {
+ const { mappingData } = this.props;
+ let maxNumEntries = 0;
+ for (const prop in mappingData) {
+ if (mappingData.hasOwnProperty(prop)) {
+ const numEntries = mappingData[prop].length;
+ if (numEntries > maxNumEntries) {
+ maxNumEntries = numEntries;
+ }
+ }
+ }
+ return maxNumEntries;
+ };
+
+ render() {
+ const config = this.state.config;
+ const maxNumEntries = this.getMaxNumEntries();
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default LocalLegislationMap;
diff --git a/src/assets/map_config.js b/src/assets/map_config.js
index c03223d..5e0004e 100644
--- a/src/assets/map_config.js
+++ b/src/assets/map_config.js
@@ -5,7 +5,7 @@ config.params = {
zoomControl: false,
zoom: 5,
maxZoom: 9,
- minZoom: 5,
+ minZoom: 4,
scrollwheel: false,
legends: true,
infoControl: false,
diff --git a/src/components/App.js b/src/components/App.js
deleted file mode 100644
index 9697fdc..0000000
--- a/src/components/App.js
+++ /dev/null
@@ -1,234 +0,0 @@
-import React, { Component } from 'react';
-import './App.css';
-import Map from './Map';
-import queryString from 'query-string';
-import axios from 'axios';
-import { Button, Menu, Modal, Header, Icon, Form } from 'semantic-ui-react';
-
-//==============
-// Top Menu Bar
-//==============
-
-class TopMenuBar extends Component {
- render() {
- return (
-
- );
- }
-}
-
-//==========================
-// Modal to paste query url
-//==========================
-
-class ModalQuery extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- modalOpen: false,
- url: this.props.inputURL,
- };
- }
-
- handleOpen = () => this.setState({ modalOpen: true });
-
- handleClose = () => this.setState({ modalOpen: false });
-
- handleChange = (event) => this.setState({ url: event.target.value });
-
- handleSubmit = (event) => {
- this.props.handleInputURL(this.state.url);
- this.setState({ modalOpen: false });
- this.setState({ url: '' });
- };
-
- render() {
- return (
-
- Map Search Results
-
- }
- open={this.state.modalOpen}
- onClose={this.handleClose}
- size="small"
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-const ModalDescription = () => (
-
- To map your search results,
-
- -
- Go to the PhiloLogic database:{' '}
-
- /corpus
- {' '}
- or{' '}
-
- /corpusnorm
-
-
- - Make a search.
- -
- Click "Map All Results" button on the top-right corner. (recommended)
-
- - Alternatively, you can paste in the search URL below.
-
-
-);
-
-//====================
-// Main App Component
-//====================
-
-class App extends Component {
- constructor(props) {
- super(props);
- const query = queryString.parse(window.location.search);
- this.state = {
- searchTerm: null,
- mappingData: null,
- loading: false,
- inputURL: query.url ? query.url : '',
- };
- }
-
- componentDidMount() {
- if (this.state.inputURL) {
- this.handleInputURL(this.state.inputURL);
- }
- }
-
- handleInputURL = (url) => {
- const testURL = url.replace('end=0', 'end=1').concat('&format=json');
- let query = null;
- this.setState({ loading: true });
- axios
- .get(testURL)
- .then((response) => response.data)
- .then((data) => {
- query = data.query;
- query.end = data.results_length;
- this.fetchData(query, testURL);
- })
- .catch((error) => {
- console.error(error);
- this.setState({ loading: false, searchTerm: null });
- });
- };
-
- fetchData = (query, testURL) => {
- const i = testURL.indexOf('/query');
- const baseURL = testURL.substring(0, i + 6);
- axios
- .get(baseURL, { params: query })
- .then((response) => response.data)
- .then((data) => {
- //console.log(data);
- this.setState({ searchTerm: query.q });
- this.processData(data);
- })
- .catch((error) => {
- console.error(error);
- });
- };
-
- processData = ({ results }) => {
- const dioceseMap = {};
- const mappingData = {};
- mappingData.searchTerm = this.state.searchTerm;
- results.forEach((result) => {
- const { context, metadata_fields: metadata } = result;
- const { record_id, diocese_id } = metadata;
- if (diocese_id) {
- if (dioceseMap.hasOwnProperty(diocese_id)) {
- if (!dioceseMap[diocese_id].has(record_id)) {
- dioceseMap[diocese_id].add(record_id);
- mappingData[diocese_id].push({ metadata, context });
- }
- } else {
- dioceseMap[diocese_id] = new Set([record_id]);
- mappingData[diocese_id] = [{ metadata, context }];
- }
- }
- });
- //console.log(mappingData);
- this.setState({
- mappingData,
- loading: false,
- });
- };
-
- render() {
- return (
-
-
-
- {this.state.loading && (
-
- )}
-
- );
- }
-}
-
-export default App;
diff --git a/src/components/ColorLegend.js b/src/components/ColorLegend.js
new file mode 100644
index 0000000..005cf43
--- /dev/null
+++ b/src/components/ColorLegend.js
@@ -0,0 +1,52 @@
+import React, { Component } from 'react';
+import { Card } from 'semantic-ui-react';
+
+class ColorLegend extends Component {
+ render() {
+ if (!this.props.mappingData) {
+ return null;
+ }
+ const { maxNumEntries, currentColorScheme } = this.props;
+ const { colorSchemes } = this.props.config;
+ const colors = colorSchemes[currentColorScheme];
+ const numPerBucket = Math.ceil(maxNumEntries / colors.length);
+
+ const getStyle = (colorHex) => ({
+ background: colorHex,
+ });
+
+ const colorBlocks = [];
+
+ colorBlocks.push(
+
+
+ 0
+
+
+ );
+
+ for (let i = 0; i < colors.length; i++) {
+ const start = i * numPerBucket + 1;
+ const end = (i + 1) * numPerBucket;
+ let range = `${start} - ${end}`;
+ if (i === colors.length - 1) {
+ range = `> ${start}`;
+ }
+ colorBlocks.push(
+
+
+ {range}
+
+
+ );
+ }
+
+ return (
+
+ {colorBlocks}
+
+ );
+ }
+}
+
+export default ColorLegend;
diff --git a/src/components/ControlPanel.js b/src/components/ControlPanel.js
new file mode 100644
index 0000000..11427ea
--- /dev/null
+++ b/src/components/ControlPanel.js
@@ -0,0 +1,47 @@
+import React, { Component } from 'react';
+import { Card, Dropdown, Icon } from 'semantic-ui-react';
+
+class ControlPanel extends Component {
+ render() {
+ const colorSchemeOptions = [
+ {
+ text: 'Black & White',
+ value: 'bw',
+ },
+ {
+ text: 'Colors #1',
+ value: 'color1',
+ },
+ {
+ text: 'Colors #2',
+ value: 'color2',
+ },
+ {
+ text: 'Colors #3',
+ value: 'color3',
+ },
+ {
+ text: 'Colors #4',
+ value: 'color4',
+ },
+ ];
+ return (
+
+
+
+ Control Panel
+
+ this.props.changeColorScheme(data.value)}
+ selection
+ fluid
+ />
+
+
+ );
+ }
+}
+
+export default ControlPanel;
diff --git a/src/components/GeoJSONLayer.js b/src/components/GeoJSONLayer.js
new file mode 100644
index 0000000..bdf96f0
--- /dev/null
+++ b/src/components/GeoJSONLayer.js
@@ -0,0 +1,143 @@
+import React, { Component } from 'react';
+import { GeoJSON } from 'react-leaflet';
+import s2d from '../assets/s2d.json';
+import dioceseInfo from '../assets/diocese_info.json';
+
+class GeoJSONLayer extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ geojson: null,
+ isLoading: true,
+ };
+ this.geojsonRef = React.createRef();
+ this.shapeToDiocese = s2d.map;
+ }
+
+ componentDidMount() {
+ fetch(process.env.REACT_APP_GEOJSON_URL)
+ .then((response) => response.json())
+ .then((data) => {
+ this.setState({
+ geojson: data,
+ isLoading: false,
+ });
+ });
+ }
+
+ getColor(shpfid) {
+ const { colorSchemes } = this.props.config;
+ const { currentColorScheme } = this.props;
+ const colors = colorSchemes[currentColorScheme];
+ const { mappingData } = this.props;
+
+ if (mappingData) {
+ const dioceseID = this.shapeToDiocese[shpfid];
+ const maxNumEntries = this.props.maxNumEntries;
+ const numPerBucket = Math.ceil(maxNumEntries / colors.length);
+ if (mappingData.hasOwnProperty(dioceseID)) {
+ const numEntries = mappingData[dioceseID].length;
+ let index = Math.floor((numEntries - 1) / numPerBucket);
+ if (index > colors.length - 1) {
+ index = colors.length - 1;
+ }
+ return colors[index];
+ }
+ }
+ return '#fff';
+ }
+
+ style = (feature) => {
+ return {
+ fillColor: this.getColor(feature.properties.SHPFID),
+ weight: 1,
+ opacity: 3,
+ color: 'grey',
+ dashArray: '3',
+ fillOpacity: 1.0,
+ };
+ };
+
+ onEachFeature = (feature, layer) => {
+ layer.on({
+ mouseover: this.highlightFeature,
+ mouseout: this.resetHighlight,
+ click: this.showRecordsModal,
+ });
+ };
+
+ getDioceseData = (layer) => {
+ const { SHPFID: shpfid } = layer.feature.properties;
+ const { mappingData } = this.props;
+ const info = {};
+ if (this.shapeToDiocese.hasOwnProperty(shpfid)) {
+ const dioceseID = this.shapeToDiocese[shpfid];
+ if (dioceseInfo.hasOwnProperty(dioceseID)) {
+ const {
+ diocese_name,
+ diocese_alt,
+ province,
+ country_modern,
+ } = dioceseInfo[dioceseID];
+ const diocese = diocese_alt
+ ? `${diocese_name} (${diocese_alt})`
+ : diocese_name;
+ info.diocese = diocese;
+ info.province = province;
+ info.country = country_modern;
+ }
+
+ if (mappingData && mappingData.hasOwnProperty(dioceseID)) {
+ info.hasMappingData = true;
+ info.searchData = mappingData[dioceseID];
+ info.query = mappingData.searchTerm;
+ }
+ }
+ return info;
+ };
+
+ highlightFeature = (e) => {
+ var layer = e.target;
+ const info = this.getDioceseData(layer);
+ this.props.updateInfo(info);
+
+ layer.setStyle({
+ weight: 2,
+ color: 'black',
+ dashArray: '',
+ fillOpacity: 1.0,
+ });
+
+ layer.bringToFront();
+ };
+
+ resetHighlight = (e) => {
+ const { leafletElement } = this.geojsonRef.current;
+ leafletElement.resetStyle(e.target);
+ this.props.updateInfo(null);
+ };
+
+ showRecordsModal = (e) => {
+ var layer = e.target;
+ const info = this.getDioceseData(layer);
+ if (info.hasMappingData) {
+ this.props.showRecordsModal(info);
+ }
+ };
+
+ render() {
+ if (this.state.isLoading) {
+ return ;
+ }
+ return (
+
+ );
+ }
+}
+
+export default GeoJSONLayer;
diff --git a/src/components/InfoPanel.js b/src/components/InfoPanel.js
new file mode 100644
index 0000000..80586d1
--- /dev/null
+++ b/src/components/InfoPanel.js
@@ -0,0 +1,40 @@
+import React, { Component } from 'react';
+import { Card } from 'semantic-ui-react';
+
+class InfoPanel extends Component {
+ render() {
+ const { info } = this.props;
+ const diocese = info ? info.diocese : 'Hover over a region';
+
+ // Province and Modern country
+ const attributes = ['province', 'country'];
+ const title = (str) => str.charAt(0).toUpperCase() + str.slice(1);
+ const provinceCountry = [];
+ if (info) {
+ for (let i = 0; i < attributes.length; i++) {
+ const attr = attributes[i];
+ if (info.hasOwnProperty(attr)) {
+ provinceCountry.push(
+
+ {title(attr)}: {info[attr]}
+
+ );
+ }
+ }
+ }
+
+ return (
+
+
+ {diocese}
+ {info && }
+ {info && info.searchData && (
+ Total hits: ({info.searchData.length})
+ )}
+
+
+ );
+ }
+}
+
+export default InfoPanel;
diff --git a/src/components/Map.js b/src/components/Map.js
deleted file mode 100644
index b711725..0000000
--- a/src/components/Map.js
+++ /dev/null
@@ -1,571 +0,0 @@
-import React, { Component } from 'react';
-import {
- Map as LeafletMap,
- TileLayer,
- GeoJSON,
- ScaleControl,
-} from 'react-leaflet';
-import {
- Button,
- Card,
- Dropdown,
- Header,
- Icon,
- Label,
- Modal,
- Table,
-} from 'semantic-ui-react';
-import mapConfig from '../assets/map_config';
-import s2d from '../assets/s2d.json';
-import metadataFields from '../assets/metadata_fields.json';
-import dioceseInfo from '../assets/diocese_info.json';
-import './Map.css';
-
-//=================
-// Mapbox base map
-//=================
-
-class BaseMap extends React.Component {
- render() {
- const { tileLayer } = this.props.config;
- return (
-
- );
- }
-}
-
-//================
-// Geo JSON layer
-//================
-
-class GeoJSONLayer extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- geojson: null,
- isLoading: true,
- };
- this.geojsonRef = React.createRef();
- this.shapeToDiocese = s2d.map;
- this.onEachFeature = this.onEachFeature.bind(this);
- this.highlightFeature = this.highlightFeature.bind(this);
- this.resetHighlight = this.resetHighlight.bind(this);
- this.showSearchResultsModal = this.showSearchResultsModal.bind(this);
- this.style = this.style.bind(this);
- this.getDioceseData = this.getDioceseData.bind(this);
- }
-
- componentDidMount() {
- fetch(process.env.REACT_APP_GEOJSON_URL)
- .then((response) => response.json())
- .then((data) => {
- this.setState({
- geojson: data,
- isLoading: false,
- });
- });
- }
-
- getColor(shpfid) {
- const { colorSchemes } = this.props.config;
- const { currentColorScheme } = this.props;
- const colors = colorSchemes[currentColorScheme];
- const { mappingData } = this.props;
-
- if (mappingData) {
- const dioceseID = this.shapeToDiocese[shpfid];
- const maxNumEntries = this.props.maxNumEntries;
- const numPerBucket = Math.ceil(maxNumEntries / colors.length);
- if (mappingData.hasOwnProperty(dioceseID)) {
- const numEntries = mappingData[dioceseID].length;
- let index = Math.floor((numEntries - 1) / numPerBucket);
- if (index > colors.length - 1) {
- index = colors.length - 1;
- }
- return colors[index];
- }
- }
- return '#fff';
- }
-
- style(feature) {
- return {
- fillColor: this.getColor(feature.properties.SHPFID),
- weight: 1,
- opacity: 3,
- color: 'grey',
- dashArray: '3',
- fillOpacity: 1.0,
- };
- }
-
- onEachFeature(feature, layer) {
- layer.on({
- mouseover: this.highlightFeature,
- mouseout: this.resetHighlight,
- click: this.showSearchResultsModal,
- });
- }
-
- getDioceseData(layer) {
- const { SHPFID: shpfid } = layer.feature.properties;
- const { mappingData } = this.props;
- const info = {};
- if (this.shapeToDiocese.hasOwnProperty(shpfid)) {
- const dioceseID = this.shapeToDiocese[shpfid];
- if (dioceseInfo.hasOwnProperty(dioceseID)) {
- const {
- diocese_name,
- diocese_alt,
- province,
- country_modern,
- } = dioceseInfo[dioceseID];
- const diocese = diocese_alt
- ? `${diocese_name} (${diocese_alt})`
- : diocese_name;
- info.diocese = diocese;
- info.province = province;
- info.country = country_modern;
- }
-
- if (mappingData && mappingData.hasOwnProperty(dioceseID)) {
- info.hasMappingData = true;
- info.searchData = mappingData[dioceseID];
- info.query = mappingData.searchTerm;
- }
- }
- return info;
- }
-
- highlightFeature(e) {
- var layer = e.target;
- const info = this.getDioceseData(layer);
- this.props.updateInfo(info);
-
- layer.setStyle({
- weight: 2,
- color: 'black',
- dashArray: '',
- fillOpacity: 1.0,
- });
-
- layer.bringToFront();
- }
-
- resetHighlight(e) {
- const { leafletElement } = this.geojsonRef.current;
- leafletElement.resetStyle(e.target);
- this.props.updateInfo(null);
- }
-
- showSearchResultsModal(e) {
- var layer = e.target;
- const info = this.getDioceseData(layer);
- if (info.hasMappingData) {
- this.props.showModal(info);
- }
- }
-
- render() {
- if (this.state.isLoading) {
- return ;
- }
- return (
-
- );
- }
-}
-
-//======================
-// Search Results Modal
-//======================
-
-class SearchResultsModal extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- show: new Array(1000).fill(false),
- };
- this.toggleMetadataTable = this.toggleMetadataTable.bind(this);
- this.handleClose = this.handleClose.bind(this);
- }
-
- toggleMetadataTable(index) {
- const newState = [...this.state.show];
- newState[index] = !newState[index];
- this.setState({
- show: newState,
- });
- }
-
- handleClose() {
- this.setState({
- show: new Array(1000).fill(false),
- });
- this.props.closeModal();
- }
-
- render() {
- const { searchResults } = this.props;
- return (
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-const SearchResultsModalTitle = ({ searchResults }) => {
- return searchResults ? (
-
-
- {searchResults.diocese}
-
-
- ) : (
- ''
- );
-};
-
-const MetadataTable = ({ metadata }) =>
- metadataFields.map(({ id, label }, i) => {
- if (metadata[id]) {
- return (
-
- {label}
- {metadata[id]}
-
- );
- }
- });
-
-// prettier-ignore
-const ResultCards = ({ searchResults, toggleMetadataTable, showTable }) => {
- if (!searchResults || !searchResults.searchData) {
- return ;
- }
- const baseURL = 'https://corpus-synodalium.com/philologic/corpus/query?report=concordance&method=proxy&start=0&end=0';
- return searchResults.searchData.map(({ context, metadata }, index) => {
- const url = `${baseURL}&q=${searchResults.query}&record_id=%22${metadata.record_id}%22`;
- const { origPlace, year, head } = metadata;
- const label = `${index + 1}. ${origPlace} (${year}) - ${head}`;
- return
- toggleMetadataTable={toggleMetadataTable}
- />;
- });
-};
-
-const SingleResultCard = (props) => (
-
-
-
-
- ... ${props.context} ...
`,
- }}
- />
-
-
- {props.showTable[props.index] && (
-
- {props.metadataTable}
-
- )}
-
-
-
-);
-
-//===============
-// Control Panel
-//===============
-
-class ControlPanel extends React.Component {
- render() {
- const colorSchemeOptions = [
- {
- text: 'Black & White',
- value: 'bw',
- },
- {
- text: 'Colors #1',
- value: 'color1',
- },
- {
- text: 'Colors #2',
- value: 'color2',
- },
- {
- text: 'Colors #3',
- value: 'color3',
- },
- {
- text: 'Colors #4',
- value: 'color4',
- },
- ];
- return (
-
-
-
- Control Panel
-
- this.props.changeColorScheme(data.value)}
- selection
- fluid
- />
-
-
- );
- }
-}
-
-//============
-// Info Panel
-//============
-
-class InfoPanel extends React.Component {
- render() {
- const { info } = this.props;
- const diocese = info ? info.diocese : 'Hover over a region';
-
- // Province and Modern country
- const attributes = ['province', 'country'];
- const title = (str) => str.charAt(0).toUpperCase() + str.slice(1);
- const provinceCountry = [];
- if (info) {
- for (let i = 0; i < attributes.length; i++) {
- const attr = attributes[i];
- if (info.hasOwnProperty(attr)) {
- provinceCountry.push(
-
- {title(attr)}: {info[attr]}
-
- );
- }
- }
- }
-
- return (
-
-
- {diocese}
- {info && }
- {info && info.searchData && (
- Total hits: ({info.searchData.length})
- )}
-
-
- );
- }
-}
-
-//===============
-// Color Legend
-//===============
-
-class ColorLegend extends React.Component {
- render() {
- if (!this.props.mappingData) {
- return null;
- }
- const { maxNumEntries, currentColorScheme } = this.props;
- const { colorSchemes } = this.props.config;
- const colors = colorSchemes[currentColorScheme];
- const numPerBucket = Math.ceil(maxNumEntries / colors.length);
-
- const getStyle = (colorHex) => ({
- background: colorHex,
- });
-
- const colorBlocks = [];
-
- colorBlocks.push(
-
-
- 0
-
-
- );
-
- for (let i = 0; i < colors.length; i++) {
- const start = i * numPerBucket + 1;
- const end = (i + 1) * numPerBucket;
- let range = `${start} - ${end}`;
- if (i === colors.length - 1) {
- range = `> ${start}`;
- }
- colorBlocks.push(
-
-
- {range}
-
-
- );
- }
-
- return (
-
- {colorBlocks}
-
- );
- }
-}
-
-//====================
-// Main map component
-//====================
-
-class LocalLegislationMap extends Component {
- constructor(props) {
- super(props);
- this.state = {
- config: mapConfig,
- currentColorScheme: 'color1',
- info: null,
- modalOpen: false,
- searchResults: null,
- };
- this.mapRef = React.createRef();
- this.changeColorScheme = this.changeColorScheme.bind(this);
- this.updateInfo = this.updateInfo.bind(this);
- this.showModal = this.showModal.bind(this);
- this.closeModal = this.closeModal.bind(this);
- }
-
- showModal(searchResults) {
- this.setState({
- searchResults: searchResults,
- modalOpen: true,
- });
- }
-
- closeModal() {
- this.setState({ modalOpen: false });
- }
-
- changeColorScheme(colorScheme) {
- this.setState({
- currentColorScheme: colorScheme,
- });
- }
-
- updateInfo(info) {
- this.setState({
- info: info,
- });
- }
-
- getMaxNumEntries = () => {
- const { mappingData } = this.props;
- let maxNumEntries = 0;
- for (const prop in mappingData) {
- if (mappingData.hasOwnProperty(prop)) {
- const numEntries = mappingData[prop].length;
- if (numEntries > maxNumEntries) {
- maxNumEntries = numEntries;
- }
- }
- }
- return maxNumEntries;
- };
-
- render() {
- const config = this.state.config;
- const maxNumEntries = this.getMaxNumEntries();
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-export default LocalLegislationMap;
diff --git a/src/components/MapBoxLayer.js b/src/components/MapBoxLayer.js
new file mode 100644
index 0000000..33f5e59
--- /dev/null
+++ b/src/components/MapBoxLayer.js
@@ -0,0 +1,19 @@
+import React, { Component } from 'react';
+import { TileLayer } from 'react-leaflet';
+
+class MapBoxLayer extends Component {
+ render() {
+ const { tileLayer } = this.props.config;
+ return (
+
+ );
+ }
+}
+
+export default MapBoxLayer;
diff --git a/src/components/PasteURLModal.js b/src/components/PasteURLModal.js
new file mode 100644
index 0000000..7ab0f28
--- /dev/null
+++ b/src/components/PasteURLModal.js
@@ -0,0 +1,99 @@
+import React, { Component } from 'react';
+import { Button, Modal, Header, Icon, Form } from 'semantic-ui-react';
+
+class PasteURLModal extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ modalOpen: false,
+ url: this.props.inputURL,
+ };
+ }
+
+ handleOpen = () => this.setState({ modalOpen: true });
+
+ handleClose = () => this.setState({ modalOpen: false });
+
+ handleChange = (event) => this.setState({ url: event.target.value });
+
+ handleSubmit = (event) => {
+ this.props.handleInputURL(this.state.url);
+ this.setState({ modalOpen: false });
+ this.setState({ url: '' });
+ };
+
+ render() {
+ return (
+ }
+ open={this.state.modalOpen}
+ onClose={this.handleClose}
+ size="small"
+ >
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+const ModalButton = ({ handleOpen }) => (
+
+);
+
+const URLInput = ({ url, handleChange }) => (
+
+
+
+
+
+);
+
+const Description = () => (
+
+ To map your search results,
+
+ -
+ Go to the PhiloLogic database: or
+
+ - Make a search.
+ -
+ Click "Map All Results" button on the top-right corner. (recommended)
+
+ - Alternatively, you can paste in the search URL below.
+
+
+);
+
+const Corpus = () => (
+
+ /corpus
+
+);
+
+const CorpusNorm = () => (
+
+ /corpusnorm
+
+);
+
+export default PasteURLModal;
diff --git a/src/components/RecordsModal.js b/src/components/RecordsModal.js
new file mode 100644
index 0000000..a2d4c36
--- /dev/null
+++ b/src/components/RecordsModal.js
@@ -0,0 +1,151 @@
+import React, { Component } from 'react';
+import {
+ Button,
+ Card,
+ Header,
+ Icon,
+ Label,
+ Modal,
+ Table,
+} from 'semantic-ui-react';
+import metadataFields from '../assets/metadata_fields.json';
+
+class RecordsModal extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ show: new Array(1000).fill(false),
+ };
+ }
+
+ toggleMetadataTable = (index) => {
+ const newState = [...this.state.show];
+ newState[index] = !newState[index];
+ this.setState({
+ show: newState,
+ });
+ };
+
+ handleClose = () => {
+ this.setState({
+ show: new Array(1000).fill(false),
+ });
+ this.props.closeRecordsModal();
+ };
+
+ render() {
+ const { searchResults } = this.props;
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+const Title = ({ searchResults }) => {
+ return searchResults ? (
+
+
+ {searchResults.diocese}
+
+
+ ) : (
+ ''
+ );
+};
+
+const MetadataTable = ({ metadata }) => {
+ return metadataFields.map(({ id, label }, i) => {
+ // skip empty fields
+ if (!metadata[id]) {
+ return ;
+ }
+ return (
+
+ {label}
+ {metadata[id]}
+
+ );
+ });
+};
+
+// prettier-ignore
+const CardList = ({ searchResults, toggleMetadataTable, showTable }) => {
+ if (!searchResults || !searchResults.searchData) {
+ return ;
+ }
+ const baseURL = 'https://corpus-synodalium.com/philologic/corpus/query?report=concordance&method=proxy&start=0&end=0';
+ return searchResults.searchData.map(({ context, metadata }, index) => {
+ const url = `${baseURL}&q=${searchResults.query}&record_id=%22${metadata.record_id}%22`;
+ const { origPlace, year, head } = metadata;
+ const label = `${index + 1}. ${origPlace} (${year}) - ${head}`;
+ return
+ toggleMetadataTable={toggleMetadataTable}
+ />;
+ });
+};
+
+const SingleCard = (props) => (
+
+
+
+
+ ... ${props.context} ...
`,
+ }}
+ />
+
+
+ {props.showTable[props.index] && (
+
+ {props.metadataTable}
+
+ )}
+
+
+
+);
+
+export default RecordsModal;
diff --git a/src/components/TopMenuBar.js b/src/components/TopMenuBar.js
new file mode 100644
index 0000000..f13296b
--- /dev/null
+++ b/src/components/TopMenuBar.js
@@ -0,0 +1,34 @@
+import React, { Component } from 'react';
+import PasteURLModal from './PasteURLModal';
+import { Menu } from 'semantic-ui-react';
+
+class TopMenuBar extends Component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default TopMenuBar;
diff --git a/src/index.js b/src/index.js
index dcd7420..a3a5723 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import './index.css';
-import App from './components/App';
+import './styles/index.css';
+import App from './App';
import 'semantic-ui-css/semantic.min.css';
import registerServiceWorker from './registerServiceWorker';
diff --git a/src/components/App.css b/src/styles/App.css
similarity index 95%
rename from src/components/App.css
rename to src/styles/App.css
index 55e7778..032c841 100644
--- a/src/components/App.css
+++ b/src/styles/App.css
@@ -6,10 +6,6 @@
padding-bottom: 20px;
}
-code.example {
- color: grey;
-}
-
.center {
position: absolute;
z-index: 800;
diff --git a/src/components/Map.css b/src/styles/Map.css
similarity index 100%
rename from src/components/Map.css
rename to src/styles/Map.css
diff --git a/src/index.css b/src/styles/index.css
similarity index 100%
rename from src/index.css
rename to src/styles/index.css