From 723c4b70076bf8eed138d1eff38200090a2acc5e Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Thu, 3 Mar 2022 20:24:38 -0500 Subject: [PATCH] Remove design-system dependency --- package.json | 1 - src/common/utilities.js | 11 +- src/components/controls/Card.js | 232 +++++++++++++++++++ src/components/controls/CardStack.js | 6 +- src/components/controls/atoms/Button.js | 86 +++++++ src/components/controls/atoms/Caret.js | 15 ++ src/components/controls/atoms/CustomField.js | 12 + src/components/controls/atoms/Media.js | 61 +++++ src/components/controls/atoms/Text.js | 46 ++++ src/components/controls/atoms/Time.js | 29 +++ src/components/time/Axis.js | 4 +- src/scss/button.scss | 37 +++ src/scss/card.scss | 108 ++++++--- src/scss/cardstack.scss | 4 +- 14 files changed, 609 insertions(+), 43 deletions(-) create mode 100644 src/components/controls/Card.js create mode 100644 src/components/controls/atoms/Button.js create mode 100644 src/components/controls/atoms/Caret.js create mode 100644 src/components/controls/atoms/CustomField.js create mode 100644 src/components/controls/atoms/Media.js create mode 100644 src/components/controls/atoms/Text.js create mode 100644 src/components/controls/atoms/Time.js create mode 100644 src/scss/button.scss diff --git a/package.json b/package.json index a80445c..8f50d62 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ }, "dependencies": { "@babel/core": "7.12.3", - "@forensic-architecture/design-system": "0.6.2", "@pmmmwh/react-refresh-webpack-plugin": "0.4.2", "@svgr/webpack": "5.4.0", "@testing-library/jest-dom": "^5.11.6", diff --git a/src/common/utilities.js b/src/common/utilities.js index 8315cb9..bfc3035 100644 --- a/src/common/utilities.js +++ b/src/common/utilities.js @@ -484,13 +484,8 @@ export function makeNiceDate(datetime) { month: "long", day: "2-digit", }); - const [ - { value: month }, - , - { value: day }, - , - { value: year }, - ] = dateTimeFormat.formatToParts(datetime); + const [{ value: month }, , { value: day }, , { value: year }] = + dateTimeFormat.formatToParts(datetime); return `${day} ${month}, ${year}`; } @@ -573,3 +568,5 @@ export function getFilterIdx( else if (narrativesExist && categoriesExist) return numCategoryPanels + 1; else return 0; } + +export const isEmptyString = (s) => s.length === 0; diff --git a/src/components/controls/Card.js b/src/components/controls/Card.js new file mode 100644 index 0000000..93f1407 --- /dev/null +++ b/src/components/controls/Card.js @@ -0,0 +1,232 @@ +import React, { useState } from "react"; +import CardText from "./atoms/Text"; +import CardTime from "./atoms/Time"; +import CardButton from "./atoms/Button"; +import CardCaret from "./atoms/Caret"; +import CardCustom from "./atoms/CustomField"; +import CardMedia from "./atoms/Media"; + +import { makeNiceDate, isEmptyString } from "../../common/utilities"; +import hash from "object-hash"; + +export const generateCardLayout = { + basic: ({ event }) => { + return [ + [ + { + kind: "date", + title: "Incident Date", + value: event.datetime || event.date || ``, + }, + { + kind: "text", + title: "Location", + value: event.location || `—`, + }, + ], + [{ kind: "line-break", times: 0.4 }], + [ + { + kind: "text", + title: "Summary", + value: event.description || ``, + scaleFont: 1.1, + }, + ], + ]; + }, + sourced: ({ event }) => { + return [ + [ + { + kind: "date", + title: "Incident Date", + value: event.datetime || event.date || ``, + }, + { + kind: "text", + title: "Location", + value: event.location || `—`, + }, + ], + [ + { + kind: "text", + title: "Summary", + value: event.description || ``, + scaleFont: 1.1, + }, + ], + ...event.sources.flatMap((source, idx) => [ + [ + { + kind: "text", + title: `Source ${idx}`, + value: source.description || ``, + scaleFont: 1.1, + }, + ], + source.paths.map((p) => ({ + kind: "media", + title: "Media", + value: [{ src: p, title: null }], + })), + ]), + ]; + }, +}; + +export const Card = ({ + content = [], + isLoading = true, + onSelect = () => {}, + sources = [], + isSelected = false, + language = "en-US", +}) => { + const [isOpen, setIsOpen] = useState(false); + const toggle = () => setIsOpen(!isOpen); + + // NB: should be internationalized. + const renderTime = (field) => ( + + ); + + const renderCaret = () => + sources.length === 0 && ( + toggle()} isOpen={isOpen} /> + ); + + const renderMedia = ({ media, idx }) => { + return ; + }; + + function renderField(field) { + switch (field.kind) { + case "media": + return ( +
+ {field.value.map((media, idx) => { + return renderMedia({ media, idx }); + })} +
+ ); + case "line": + return ( +
+
+
+ ); + case "line-break": + return ( +
+ ); + case "item": + // this is like a span + return null; + case "markdown": + return ; + case "tag": + return ( +
+
+ {field.value} +
+
+ ); + case "button": + return ( +
+ {field.title &&

{field.title}

} + {/*
*/} + {field.value.map((t, idx) => ( + + ))} + {/*
*/} +
+ ); + case "text": + return !isEmptyString(field.value) && ; + case "date": + return renderTime(field); + case "links": + return ( +
+ {field.title &&

{field.title}

} +
+ {field.value.map(({ text, href }, idx) => ( + + {text} + + ))} +
+
+ ); + case "list": + // Only render if some of the list's strings are non-empty + const shouldFieldRender = + !!field.value.length && + !!field.value.filter((s) => !isEmptyString(s)).length; + return shouldFieldRender ? ( + //
+
+ {field.title &&

{field.title}

} +
+ {field.value.map((t, idx) => ( + + ))} +
+
+ ) : null; + default: + return null; + } + } + + function renderRow(row) { + return ( +
+ {row.map((field) => ( + {renderField(field)} + ))} +
+ ); + } + + // TODO: render afterCaret appropriately from props + sources = []; + + return ( +
  • + {content.map((row) => renderRow(row))} + {isOpen && ( +
    + {sources.map(() => ( +
    + ))} +
    + )} + {sources.length > 0 ? renderCaret() : null} +
  • + ); +}; diff --git a/src/components/controls/CardStack.js b/src/components/controls/CardStack.js index f5faab5..a6d5ad0 100644 --- a/src/components/controls/CardStack.js +++ b/src/components/controls/CardStack.js @@ -1,9 +1,6 @@ import React from "react"; import { connect } from "react-redux"; -import { - generateCardLayout, - Card, -} from "@forensic-architecture/design-system/dist/react"; +import { generateCardLayout, Card } from "./Card"; import * as selectors from "../../selectors"; import { getFilterIdxFromColorSet } from "../../common/utilities"; @@ -70,6 +67,7 @@ class CardStack extends React.Component { return events.map((event, idx) => { const thisRef = React.createRef(); this.refs[idx] = thisRef; + console.log(event); const content = generateTemplate({ event, diff --git a/src/components/controls/atoms/Button.js b/src/components/controls/atoms/Button.js new file mode 100644 index 0000000..12703c1 --- /dev/null +++ b/src/components/controls/atoms/Button.js @@ -0,0 +1,86 @@ +import React from "react"; +import PropTypes from "prop-types"; + +/** + * Primary UI component for user interaction + */ +export const Button = ({ + primary, + backgroundColor, + borderRadius, + size, + label, + normalCursor, + ...props +}) => { + const mode = primary ? "button--primary" : "button--secondary"; + return ( + + ); +}; + +Button.propTypes = { + /** + * Is this the principal call to action on the page? + */ + primary: PropTypes.bool, + /** + * What background color to use + */ + backgroundColor: PropTypes.string, + /** + * How much rounded are they? + */ + borderRadius: PropTypes.string, + /** + * How large should the button be? + */ + size: PropTypes.oneOf(["small", "medium", "large"]), + /** + * Button contents + */ + label: PropTypes.string.isRequired, + /** + * Optional click handler + */ + onClick: PropTypes.func, +}; + +Button.defaultProps = { + backgroundColor: "red", + borderRadius: "0%", + primary: false, + size: "medium", + onClick: undefined, +}; + +const CardButton = ({ + text, + color = "#000", + onClick = () => {}, + normalCursor, +}) => ( +