From c04d4e11644cb33805b19cf0a418dddfbceaedf9 Mon Sep 17 00:00:00 2001 From: francesco Date: Thu, 24 Sep 2020 19:25:14 +0200 Subject: [PATCH] Changed default font. New entity preview as drawer --- README.md | 4 +- .../custom_preview/CustomBooleanPreview.tsx | 2 +- example/src/index.css | 13 -- example/src/index.tsx | 14 +- example/src/index_simple.tsx | 2 +- example/yarn.lock | 31 ++-- package.json | 1 + src/CMSApp.tsx | 158 +++++++++--------- src/collection/CollectionTable.tsx | 26 ++- src/contexts.tsx | 5 + src/form/fields/SwitchField.tsx | 6 +- src/form/index.tsx | 1 + src/models.ts | 5 +- src/preview/EntityDetailDialog.tsx | 93 ++++++----- src/preview/EntityPreview.tsx | 92 ++++++---- src/preview/PreviewComponent.tsx | 69 +++++--- src/routes/CollectionRoute.tsx | 4 +- src/styles.module.css | 1 + src/styles.ts | 14 +- yarn.lock | 5 + 20 files changed, 318 insertions(+), 228 deletions(-) delete mode 100644 example/src/index.css create mode 100644 src/contexts.tsx diff --git a/README.md b/README.md index 53aef21..9d98201 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ firebaseConfig specification, since it gets picked up automatically. import React from "react"; import ReactDOM from "react-dom"; -import "typeface-roboto"; +import "typeface-rubik"; import { Authenticator, @@ -272,7 +272,7 @@ const myAuthenticator: Authenticator = (user?: User) => { ReactDOM.render( : + value ? : ); } diff --git a/example/src/index.css b/example/src/index.css deleted file mode 100644 index 7d30ace..0000000 --- a/example/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/example/src/index.tsx b/example/src/index.tsx index 4cffe13..f0734b3 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -1,10 +1,9 @@ import React from "react"; import ReactDOM from "react-dom"; -import "./index.css"; import logo from "./images/test_shop_logo.png"; import algoliasearch, { SearchClient } from "algoliasearch"; -import "typeface-roboto"; +import "typeface-rubik"; import * as serviceWorker from "./serviceWorker"; @@ -376,6 +375,15 @@ export const testEntitySchema = buildSchema({ } } }, + pdf: { + title: "Pdf", + dataType: "string", + config: { + storageMeta: { + storagePath: "test" + } + } + }, image_urls: { title: "Image URLs", dataType: "array", @@ -550,7 +558,7 @@ const myAuthenticator: Authenticator = (user?: User) => { ReactDOM.render( { ReactDOM.render( =3", + "typeface-rubik": "^0.0.72", "typeface-roboto": "^0.0.75", "yup": "^0.29.3" }, diff --git a/src/CMSApp.tsx b/src/CMSApp.tsx index a49d6b6..058a881 100644 --- a/src/CMSApp.tsx +++ b/src/CMSApp.tsx @@ -8,7 +8,6 @@ import { Divider, Drawer, Grid, - Hidden, IconButton, List, ListItem, @@ -53,6 +52,7 @@ import { import { useStyles } from "./styles"; import { Authenticator } from "./authenticator"; import { blue, pink, red } from "@material-ui/core/colors"; +import { AuthContext, FirebaseConfigContext } from "./contexts"; /** * Main entry point that defines the CMS configuration @@ -115,6 +115,13 @@ export interface CMSAppProps { * Primary color of the theme of the CMS */ secondaryColor?: string + + /** + * Font family string + * e.g. + * '"Roboto", "Helvetica", "Arial", sans-serif' + */ + fontFamily?: string } /** @@ -141,8 +148,6 @@ export interface AdditionalView { const googleAuthProvider = new firebase.auth.GoogleAuthProvider(); -export const AuthContext = React.createContext(null); - interface HideOnScrollProps { /** * Injected by the documentation to work in an iframe. @@ -167,18 +172,19 @@ function HideOnScroll(props: HideOnScrollProps) { } export function CMSApp({ - name, - logo, - navigation, - includeMedia, - authentication, - allowSkipLogin, - firebaseConfig, - additionalViews, - primaryColor, - secondaryColor, - ...props - }: CMSAppProps) { + name, + logo, + navigation, + includeMedia, + authentication, + allowSkipLogin, + firebaseConfig, + additionalViews, + primaryColor, + secondaryColor, + fontFamily, + ...props + }: CMSAppProps) { const classes = useStyles(); const theme = createMuiTheme({ palette: { @@ -195,6 +201,9 @@ export function CMSApp({ main: red.A400 } }, + typography: { + "fontFamily": fontFamily ? fontFamily : `"Rubik", "Roboto", "Helvetica", "Arial", sans-serif` + }, shape: { borderRadius: 2 }, @@ -202,15 +211,15 @@ export function CMSApp({ MuiTableRow: { root: { "&:last-child td": { - borderBottom: 0, - }, + borderBottom: 0 + } } } - }, + } }); - const [mobileOpen, setMobileOpen] = React.useState(false); + const [drawerOpen, setDrawerOpen] = React.useState(false); const [ firebaseConfigInitialized, @@ -291,7 +300,7 @@ export function CMSApp({ } }, []); - const handleDrawerToggle = () => setMobileOpen(!mobileOpen); + const handleDrawerToggle = () => setDrawerOpen(!drawerOpen); function googleSignIn() { setAuthProviderError(null); @@ -493,78 +502,68 @@ export function CMSApp({ ); return ( - - - - - - - - - - - {name} - - + + + + + + + + + + + + {name} + + - - {loggedUser && loggedUser.photoURL ? - - : - {loggedUser?.displayName ? loggedUser.displayName[0] : "A"} - } - + + {loggedUser && loggedUser.photoURL ? + + : + {loggedUser?.displayName ? loggedUser.displayName[0] : "A"} + } + - + - - + + - +
+ + {getRouterSwitch(shouldIncludeMedia)} +
+
+
+
+ ); } @@ -574,8 +573,11 @@ export function CMSApp({ {firebaseConfigError ? It seems like the provided Firebase config is not - correct. If you are using the credentials provided automatically - by Firebase Hosting, make sure you your Firebase app to Firebase Hosting. + correct. If you are using the credentials provided + automatically + by Firebase Hosting, make sure you link + your Firebase app to + Firebase Hosting. : ( authLoading ? ( diff --git a/src/collection/CollectionTable.tsx b/src/collection/CollectionTable.tsx index 7447989..760be16 100644 --- a/src/collection/CollectionTable.tsx +++ b/src/collection/CollectionTable.tsx @@ -26,6 +26,8 @@ import { TextSearchDelegate } from "../text_search_delegate"; import SearchBar from "./SearchBar"; import PreviewComponent from "../preview/PreviewComponent"; import SkeletonComponent, { renderSkeletonText } from "../preview/SkeletonComponent"; +import firebase from "firebase"; +import FieldPath = firebase.firestore.FieldPath; interface CollectionTableProps { /** @@ -159,7 +161,7 @@ export default function CollectionTable(props: Collectio setOrderBy(undefined); }; - const handleRequestSort = (event: React.MouseEvent, property: string) => { + const handleRequestSort = (event: React.MouseEvent, property: any) => { if (filter) { const filterKeys = Object.keys(filter); if (filterKeys.length > 1 || filterKeys[0] !== property) { @@ -449,9 +451,9 @@ type Order = "asc" | "desc" | undefined; interface CollectionTableHeadProps { classes: ReturnType; - onRequestSort: (event: React.MouseEvent, property: string) => void; + onRequestSort: (event: React.MouseEvent, property: any) => void; order?: Order; - orderBy?: string; + orderBy?: any; sortable: boolean; schema: S; additionalColumns?: AdditionalColumnDelegate[]; @@ -477,7 +479,7 @@ function CollectionTableHead({ }: CollectionTableHeadProps) { - const createSortHandler = (property: string) => (event: React.MouseEvent) => { + const createSortHandler = (property: any) => (event: React.MouseEvent) => { onRequestSort(event, property); }; @@ -492,6 +494,7 @@ function CollectionTableHead({ }); }); + const sortedById = orderBy === FieldPath.documentId(); return ( @@ -499,7 +502,20 @@ function CollectionTableHead({ Id + padding={"default"}> + + Id + {sortedById ? + + {order === "desc" ? "Sorted descending" : (order === "asc" ? "Sorted ascending" : "")} + + : null} + + {headCells.map(headCell => { const active = sortable && orderBy === headCell.id; diff --git a/src/contexts.tsx b/src/contexts.tsx new file mode 100644 index 0000000..baf4a63 --- /dev/null +++ b/src/contexts.tsx @@ -0,0 +1,5 @@ +import React from "react"; +import { User } from "firebase"; + +export const AuthContext = React.createContext(null); +export const FirebaseConfigContext = React.createContext(null); diff --git a/src/form/fields/SwitchField.tsx b/src/form/fields/SwitchField.tsx index 67ccb53..07899c5 100644 --- a/src/form/fields/SwitchField.tsx +++ b/src/form/fields/SwitchField.tsx @@ -1,4 +1,8 @@ -import { FormControlLabel, FormHelperText, Switch } from "@material-ui/core"; +import { + FormControlLabel, + FormHelperText, + Switch +} from "@material-ui/core"; import React from "react"; import { CMSFieldProps } from "../form_props"; diff --git a/src/form/index.tsx b/src/form/index.tsx index 78b8f8a..a881c63 100644 --- a/src/form/index.tsx +++ b/src/form/index.tsx @@ -184,3 +184,4 @@ export { SwitchField, TextField }; + diff --git a/src/models.ts b/src/models.ts index 9247335..4bcb46c 100644 --- a/src/models.ts +++ b/src/models.ts @@ -574,7 +574,7 @@ export interface StorageMeta { /** * Media type of this reference, used for displaying the preview */ - mediaType: MediaType; + mediaType?: MediaType; /** * Absolute path in your bucket @@ -628,7 +628,8 @@ export type StorageFileTypes = | "audio/*" | "application/*" | "text/*" - | "font/*" ; + | "font/*" + | string; // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types export interface NumberFieldConfig extends FieldConfig { diff --git a/src/preview/EntityDetailDialog.tsx b/src/preview/EntityDetailDialog.tsx index dfd777c..ab1b5cc 100644 --- a/src/preview/EntityDetailDialog.tsx +++ b/src/preview/EntityDetailDialog.tsx @@ -1,15 +1,19 @@ import { Entity, EntitySchema } from "../models"; import React, { useEffect, useState } from "react"; -import { listenEntityFromRef } from "../firebase/firestore"; -import Dialog from "@material-ui/core/Dialog"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import { DialogContent } from "@material-ui/core"; +import { listenEntityFromRef } from "../firebase"; + import EntityPreview from "../preview/EntityPreview"; -import DialogActions from "@material-ui/core/DialogActions"; -import Button from "@material-ui/core/Button"; +import { + Box, + Container, + Drawer, + IconButton, + Typography +} from "@material-ui/core"; +import CloseIcon from "@material-ui/icons/Close"; export interface EntityDetailDialogProps { - entity: Entity, + entity?: Entity, schema: S open: boolean; onClose: () => void; @@ -19,52 +23,53 @@ export default function EntityDetailDialog(props: Entity const { entity, schema, onClose, open, ...other } = props; - const [updatedEntity, setUpdatedEntity] = useState>(entity); + const [updatedEntity, setUpdatedEntity] = useState | undefined>(entity); useEffect(() => { - const cancelSubscription = listenEntityFromRef( - entity?.reference, - schema, - (e) => { - if (e) { - setUpdatedEntity(e); - console.log("Updated entity from Firestore", e); - } - }); + const cancelSubscription = + entity ? + listenEntityFromRef( + entity?.reference, + schema, + (e) => { + if (e) { + setUpdatedEntity(e); + console.log("Updated entity from Firestore", e); + } + }) + : + () => { + }; return () => cancelSubscription(); }, [entity]); return ( - + + + + + + + + + {schema.name} + + + - - - {schema.name} - + {updatedEntity && + } - - {updatedEntity && - } - + + - - - - - - - ); } diff --git a/src/preview/EntityPreview.tsx b/src/preview/EntityPreview.tsx index d096367..0e80be1 100644 --- a/src/preview/EntityPreview.tsx +++ b/src/preview/EntityPreview.tsx @@ -1,21 +1,24 @@ import * as React from "react"; import { + Box, Table, TableBody, TableCell, TableContainer, - TableRow, Typography + TableRow, + Typography } from "@material-ui/core"; import { Entity, EntitySchema } from "../models"; import PreviewComponent from "./PreviewComponent"; +import OpenInNewIcon from "@material-ui/icons/OpenInNew"; +import IconButton from "@material-ui/core/IconButton"; +import { FirebaseConfigContext } from "../contexts"; + export interface EntityPreviewProps { - entity: Entity; - schema: S; - } export default function EntityPreview( @@ -25,37 +28,56 @@ export default function EntityPreview( }: EntityPreviewProps) { return ( - - - - - - - Id - - - - {entity.id} - - - {Object.entries(schema.properties).map(([key, property]) => ( - - - - {property.title} - - - - - - - ))} - -
-
+ + {config => ( + + + + + + + Id + + + + + {entity.id} + + + + + + + + + {Object.entries(schema.properties).map(([key, property]) => ( + + + + {property.title} + + + + + + + ))} + +
+
+ )} +
+ ); } diff --git a/src/preview/PreviewComponent.tsx b/src/preview/PreviewComponent.tsx index 2570251..d9589ba 100644 --- a/src/preview/PreviewComponent.tsx +++ b/src/preview/PreviewComponent.tsx @@ -14,8 +14,7 @@ import { firestore } from "firebase/app"; import { Box, - CardMedia, - Checkbox, + CardMedia, Checkbox, Chip, Divider, Grid, @@ -27,7 +26,10 @@ import { TableRow, Typography } from "@material-ui/core"; +import CheckBoxOutlineBlankIcon from "@material-ui/icons/CheckBoxOutlineBlank"; +import CheckBoxIcon from "@material-ui/icons/CheckBox"; import StorageThumbnail from "./StorageThumbnail"; +import DescriptionOutlinedIcon from "@material-ui/icons/DescriptionOutlined"; import ReferencePreview from "./ReferencePreview"; import { PreviewComponentProps } from "./PreviewComponentProps"; import { useStyles } from "../styles"; @@ -136,7 +138,7 @@ function renderMap(property: MapProperty, value: T, small: boolean) { className={classes.tableNoBottomBorder}> - {property.properties[key].title} @@ -303,18 +305,23 @@ function renderUrlAudioComponent(value: string) { function renderUrlImageThumbnail(url: string, small: boolean) { return ( - - - + e.stopPropagation()} + > + + + + ); } @@ -330,6 +337,23 @@ function renderUrlVideo(url: string, ); } +function renderUrlFile(url: string, small: boolean) { + return ( + e.stopPropagation()}> + + + + + ); +} + function renderReference( ref: firestore.DocumentReference, refSchema: S, @@ -363,8 +387,9 @@ export function renderUrlComponent(property: StringProperty, return renderUrlAudioComponent(url); } else if (mediaType === "video") { return renderUrlVideo(url, small); + } else { + return renderUrlFile(url, small); } - throw Error("URL component misconfiguration"); } export function renderString(property: StringProperty, @@ -374,10 +399,12 @@ export function renderString(property: StringProperty, if (property.config?.enumValues) { return property.config?.enumValues[value]; } else if (property.config?.multiline) { - return - {value} - ; + return ( + + {value} + + ); } else { return {value} @@ -408,7 +435,7 @@ export function renderTimestamp(value: Date) { - + {value.toLocaleString()} diff --git a/src/routes/CollectionRoute.tsx b/src/routes/CollectionRoute.tsx index 8760194..298728e 100644 --- a/src/routes/CollectionRoute.tsx +++ b/src/routes/CollectionRoute.tsx @@ -21,6 +21,7 @@ import { BreadcrumbContainer } from "../util"; import DeleteEntityDialog from "../collection/DeleteEntityDialog"; import EntityDetailDialog from "../preview/EntityDetailDialog"; + interface CollectionRouteProps { view: EntityCollectionView; entityPlaceholderPath: string, @@ -107,11 +108,10 @@ export function CollectionRoute({ filterableProperties={view.filterableProperties} properties={view.properties}/> - {entityClicked && setEntityClicked(undefined)}/>} + onClose={() => setEntityClicked(undefined)}/> {deleteEntityClicked && marginTop: theme.spacing(3) }, drawer: { - [theme.breakpoints.up("md")]: { - width: drawerWidth, - flexShrink: 0 - } + // [theme.breakpoints.up("md")]: { + // width: drawerWidth, + // flexShrink: 0 + // } }, appBar: { [theme.breakpoints.up("md")]: { @@ -24,9 +24,9 @@ export const useStyles = makeStyles((theme: Theme) => }, menuButton: { marginRight: theme.spacing(2), - [theme.breakpoints.up("md")]: { - display: "none" - } + // [theme.breakpoints.up("md")]: { + // display: "none" + // } }, grow: { flexGrow: 1 diff --git a/yarn.lock b/yarn.lock index f86f30b..8770f80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13828,6 +13828,11 @@ typeface-roboto@^0.0.75: resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-0.0.75.tgz#98d5ba35ec234bbc7172374c8297277099cc712b" integrity sha512-VrR/IiH00Z1tFP4vDGfwZ1esNqTiDMchBEXYY9kilT6wRGgFoCAlgkEUMHb1E3mB0FsfZhv756IF0+R+SFPfdg== +typeface-rubik@^0.0.72: + version "0.0.72" + resolved "https://registry.yarnpkg.com/typeface-rubik/-/typeface-rubik-0.0.72.tgz#85d028dc66e8388a941f8c7ce194fad963d1824e" + integrity sha512-dRJrtCEHfY3e39zq/U4JpFPVVbEogpDKCGnS70sXur253gnrwunjai26KCZbwdF3NEdMAYMTBu4CIn3hJp4xSA== + typescript@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"