From cd139c9fdfe5ea65eef85ab80f25197e87ecd650 Mon Sep 17 00:00:00 2001 From: olliethedev <3martynov@gmail.com> Date: Fri, 27 Oct 2023 15:28:47 -0400 Subject: [PATCH] feat: ui elements added --- package-lock.json | 14 +- package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- packages/amplify-util-blog/commands/add.js | 138 +++- packages/amplify-util-blog/package.json | 8 +- .../blog/CommonPostCardCollection.tsx | 54 ++ .../blog/MemoizedReactMarkdown.tsx | 11 + .../ui-components/blog/NewPostLayout.tsx | 36 + .../ui-components/blog/Page.jsx | 58 -- .../ui-components/blog/SearchResults.tsx | 60 ++ .../ui-components/blog/UpdatePostLayout.tsx | 43 + .../blog/WrappedBadgeElementCollection.tsx | 44 + .../{Page.d.ts => base/BadgeElement.d.ts} | 18 +- .../ui-components/blog/base/BadgeElement.jsx | 47 ++ .../blog/base/BadgeElementCollection.d.ts | 25 + .../blog/base/BadgeElementCollection.jsx | 51 ++ .../ui-components/blog/base/PostCard.d.ts | 34 + .../ui-components/blog/base/PostCard.jsx | 247 ++++++ .../blog/base/PostCardCollection.d.ts | 25 + .../blog/base/PostCardCollection.jsx | 55 ++ .../blog/base/PostCreateForm.d.ts | 56 ++ .../blog/base/PostCreateForm.jsx | 694 ++++++++++++++++ .../ui-components/blog/base/PostDetail.d.ts | 29 + .../ui-components/blog/base/PostDetail.jsx | 145 ++++ .../blog/base/PostUpdateForm.d.ts | 57 ++ .../blog/base/PostUpdateForm.jsx | 768 ++++++++++++++++++ .../blog/base/TagCreateForm.d.ts | 36 + .../ui-components/blog/base/TagCreateForm.jsx | 162 ++++ .../ui-components/blog/index.js | 15 + 32 files changed, 2838 insertions(+), 102 deletions(-) create mode 100644 packages/amplify-util-blog/ui-components/blog/CommonPostCardCollection.tsx create mode 100644 packages/amplify-util-blog/ui-components/blog/MemoizedReactMarkdown.tsx create mode 100644 packages/amplify-util-blog/ui-components/blog/NewPostLayout.tsx delete mode 100644 packages/amplify-util-blog/ui-components/blog/Page.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/SearchResults.tsx create mode 100644 packages/amplify-util-blog/ui-components/blog/UpdatePostLayout.tsx create mode 100644 packages/amplify-util-blog/ui-components/blog/WrappedBadgeElementCollection.tsx rename packages/amplify-util-blog/ui-components/blog/{Page.d.ts => base/BadgeElement.d.ts} (54%) create mode 100644 packages/amplify-util-blog/ui-components/blog/base/BadgeElement.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.d.ts create mode 100644 packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostCard.d.ts create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostCard.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.d.ts create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.d.ts create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostDetail.d.ts create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostDetail.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.d.ts create mode 100644 packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.d.ts create mode 100644 packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.jsx create mode 100644 packages/amplify-util-blog/ui-components/blog/index.js diff --git a/package-lock.json b/package-lock.json index c7cbbe9..756b4c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "amplifiers", - "version": "1.1.15", + "version": "1.1.16", "lockfileVersion": 3, "requires": true, "packages": { "": { - "version": "1.1.15", + "version": "1.1.16", "license": "MIT", "workspaces": [ "./packages/*" @@ -7276,7 +7276,7 @@ }, "packages/amplify-graphql-amplifiers-core": { "name": "@amplifiers/amplify-graphql-amplifiers-core", - "version": "1.1.15", + "version": "1.1.16", "license": "MIT", "dependencies": { "amplify-prompts": "2.6.0" @@ -7296,7 +7296,7 @@ }, "packages/amplify-graphql-create-model-transformer": { "name": "@amplifiers/amplify-graphql-create-model-transformer", - "version": "1.1.15", + "version": "1.1.16", "license": "MIT", "dependencies": { "@amplifiers/amplify-graphql-amplifiers-core": "^1.0.0", @@ -7330,7 +7330,7 @@ }, "packages/amplify-graphql-process-image-transformer": { "name": "@amplifiers/amplify-graphql-process-image-transformer", - "version": "1.1.15", + "version": "1.1.16", "license": "MIT", "dependencies": { "@amplifiers/amplify-graphql-amplifiers-core": "^1.0.0", @@ -7364,7 +7364,7 @@ }, "packages/amplify-graphql-send-email-transformer": { "name": "@amplifiers/amplify-graphql-send-email-transformer", - "version": "1.1.15", + "version": "1.1.16", "license": "MIT", "dependencies": { "@amplifiers/amplify-graphql-amplifiers-core": "^1.0.0", @@ -7398,7 +7398,7 @@ }, "packages/amplify-util-blog": { "name": "@amplifiers/amplify-util-blog", - "version": "1.1.15", + "version": "1.1.16", "license": "ISC", "dependencies": { "fs-extra": "^11.1.1" diff --git a/package.json b/package.json index 380c5c1..65ed570 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.1.15", + "version": "1.1.16", "private": true, "workspaces": [ "./packages/*" diff --git a/packages/amplify-graphql-amplifiers-core/package.json b/packages/amplify-graphql-amplifiers-core/package.json index bf9a3ed..f73cae9 100644 --- a/packages/amplify-graphql-amplifiers-core/package.json +++ b/packages/amplify-graphql-amplifiers-core/package.json @@ -1,6 +1,6 @@ { "name": "@amplifiers/amplify-graphql-amplifiers-core", - "version": "1.1.15", + "version": "1.1.16", "description": "Amplify GraphQL @createModel transformer. This directive is intended to be used for creating a new model once a Cognito Event is fired.", "keywords": [ "aws", diff --git a/packages/amplify-graphql-create-model-transformer/package.json b/packages/amplify-graphql-create-model-transformer/package.json index cb003b9..4bee202 100644 --- a/packages/amplify-graphql-create-model-transformer/package.json +++ b/packages/amplify-graphql-create-model-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@amplifiers/amplify-graphql-create-model-transformer", - "version": "1.1.15", + "version": "1.1.16", "description": "Amplify GraphQL @createModel transformer. This directive is intended to be used for creating a new model once a Cognito Event is fired.", "keywords": [ "aws", diff --git a/packages/amplify-graphql-process-image-transformer/package.json b/packages/amplify-graphql-process-image-transformer/package.json index 74a2c1d..58a193c 100644 --- a/packages/amplify-graphql-process-image-transformer/package.json +++ b/packages/amplify-graphql-process-image-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@amplifiers/amplify-graphql-process-image-transformer", - "version": "1.1.15", + "version": "1.1.16", "description": "This directive allows you to process images.", "keywords": [ "aws", diff --git a/packages/amplify-graphql-send-email-transformer/package.json b/packages/amplify-graphql-send-email-transformer/package.json index 8dcc4ed..098980d 100644 --- a/packages/amplify-graphql-send-email-transformer/package.json +++ b/packages/amplify-graphql-send-email-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@amplifiers/amplify-graphql-send-email-transformer", - "version": "1.1.15", + "version": "1.1.16", "description": "Amplify GraphQL @sendEmail transformer.", "keywords": [ "aws", diff --git a/packages/amplify-util-blog/commands/add.js b/packages/amplify-util-blog/commands/add.js index 441c009..b2b5a51 100644 --- a/packages/amplify-util-blog/commands/add.js +++ b/packages/amplify-util-blog/commands/add.js @@ -5,18 +5,16 @@ const eventName = "add"; async function run(context) { context.print.info(`Event handler ${eventName} is running.`); updateSchema(context); + updateTransformerConfig(context); + updateParametersConfig(context); updateUIElements(context); + updateUIElementsIndex(context); context.print.info(`Done running ${eventName}`); } function updateSchema(context) { // Get the API name from the context - const apiMeta = context.amplify.getProjectMeta().api; - console.log(apiMeta); - const appSyncApis = Object.keys(apiMeta) - .map((key) => ({ ...apiMeta[key], name: key })) - .filter((api) => api.service === "AppSync"); - console.log(`Found ${appSyncApis.length} APIs `); + const appSyncApis = getAppSyncAPIs(context); if (appSyncApis.length === 0) { context.print.info("No AppSync API found in the project"); @@ -86,25 +84,65 @@ function updateSchema(context) { context.print.info("Merged schema.graphql and blogSchema.graphql"); } -function updateUIElements(context) { - const projectConfigPath = - context.amplify.pathManager.getProjectConfigFilePath(); +function updateParametersConfig(context) { + const appSyncApis = getAppSyncAPIs(context); - const projectConfig = JSON.parse(fs.readFileSync(projectConfigPath, "utf8")); + const apiName = appSyncApis[0].name; - context.print.info(projectConfig); + const paramsFilePath = path.join( + context.amplify.pathManager.getBackendDirPath(), + "api", + apiName, + "parameters.json" + ); - if (projectConfig.frontend !== "javascript") { - context.print.info( - "Blog Plugin only supports javascript/typescript front-end project for now" - ); - return; + const parameters = fs.readFileSync(paramsFilePath, "utf8"); + let parametersJSON = JSON.parse(parameters); + + parametersJSON = { + ...parametersJSON, + TypesenseApiKey: "", + TypesenseHost: "", + TypesensePort: "443", + TypesenseProtocol: "https", + }; + + fs.writeFileSync(paramsFilePath, JSON.stringify(parametersJSON, null, 4)); +} + +function updateTransformerConfig(context) { + const appSyncApis = getAppSyncAPIs(context); + + const apiName = appSyncApis[0].name; + + const configFilePath = path.join( + context.amplify.pathManager.getBackendDirPath(), + "api", + apiName, + "transform.conf.json" + ); + + const config = fs.readFileSync(configFilePath, "utf8"); + let configJSON = JSON.parse(config); + + if (configJSON.transformers) { + configJSON.transformers.push("amplify-graphql-typesense-transformer"); + } else { + configJSON = { + ...configJSON, + transformers: ["amplify-graphql-typesense-transformer"], + }; } - if (projectConfig.javascript.framework !== "react") { - context.print.info( - "Blog Plugin only supports react front-end project for now" - ); + fs.writeFileSync(configFilePath, JSON.stringify(configJSON, null, 4)); +} + +function updateUIElements(context) { + const projectConfig = getProjectConfig(context); + + context.print.info(projectConfig); + + if (!isSupportedConfig(context)) { return; } @@ -137,6 +175,66 @@ function updateUIElements(context) { fs.copySync(uiComponentsSource, uiComponentsDestination); } +function updateUIElementsIndex(context) { + const projectConfig = getProjectConfig(context); + if (!isSupportedConfig(context)) { + return; + } + + const srcDir = projectConfig.javascript.config.SourceDir; + + const uiComponentsIndexPath = path.join( + context.amplify.pathManager.searchProjectRootPath(), + srcDir, + "ui-components", + "index.js" + ); + + const uiComponentsIndex = fs.readFileSync(uiComponentsIndexPath, "utf8"); + + const blogImport = `export * from './blog';`; + + if (!uiComponentsIndex.includes(blogImport)) { + const newUIComponentsIndex = `${uiComponentsIndex}\n${blogImport}`; + fs.writeFileSync(uiComponentsIndexPath, newUIComponentsIndex); + } +} + +function getAppSyncAPIs(context) { + const apiMeta = context.amplify.getProjectMeta().api; + + const appSyncApis = Object.keys(apiMeta) + .map((key) => ({ ...apiMeta[key], name: key })) + .filter((api) => api.service === "AppSync"); + console.log(`Found ${appSyncApis.length} APIs `); + return appSyncApis; +} + +function getProjectConfig(context) { + const projectConfigPath = + context.amplify.pathManager.getProjectConfigFilePath(); + + return JSON.parse(fs.readFileSync(projectConfigPath, "utf8")); +} + +function isSupportedConfig(context) { + const projectConfig = getProjectConfig(context); + + if (projectConfig.frontend !== "javascript") { + context.print.info( + "Blog Plugin only supports javascript/typescript front-end project for now" + ); + return false; + } else if (projectConfig.javascript.framework !== "react") { + context.print.info( + "Blog Plugin only supports react front-end project for now" + ); + return false; + } + + return true; +} + module.exports = { run, }; diff --git a/packages/amplify-util-blog/package.json b/packages/amplify-util-blog/package.json index 9469d6e..501810e 100644 --- a/packages/amplify-util-blog/package.json +++ b/packages/amplify-util-blog/package.json @@ -1,12 +1,12 @@ { "name": "@amplifiers/amplify-util-blog", - "version": "1.1.15", + "version": "1.1.16", "description": "", "main": "index.js", "files": [ - "commands/*", - "event-handlers/*", - "ui-components/*", + "commands/**/*", + "event-handlers/**/*", + "ui-components/**/*", "amplify-plugin.json" ], "scripts": { diff --git a/packages/amplify-util-blog/ui-components/blog/CommonPostCardCollection.tsx b/packages/amplify-util-blog/ui-components/blog/CommonPostCardCollection.tsx new file mode 100644 index 0000000..233d83a --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/CommonPostCardCollection.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import Image from "next/image"; +import { StorageImage } from "@aws-amplify/ui-react-storage"; +import { default as PostCardCollection } from "./base/PostCardCollection"; +import { default as WrappedBadgeElementCollection } from "./WrappedBadgeElementCollection"; +import { Post } from "./../../models"; + +interface CommonPostCardCollectionProps { + posts?: Post[]; +} + +const CommonPostCardCollection = ({ + posts, +}: CommonPostCardCollectionProps) => { + return ( + ({ + overrides: { + Tags: { + children: , + }, + ReadMoreLayout: { + style: { + cursor: "pointer", + }, + }, + ImageContainer: { + padding: 0, + borderRadius: "10px", + children: item.image.startsWith('http') ? ( + post image + ) : ( + + ), + }, + }, + })} + /> + ); +}; + +export default CommonPostCardCollection; diff --git a/packages/amplify-util-blog/ui-components/blog/MemoizedReactMarkdown.tsx b/packages/amplify-util-blog/ui-components/blog/MemoizedReactMarkdown.tsx new file mode 100644 index 0000000..e5fb1f1 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/MemoizedReactMarkdown.tsx @@ -0,0 +1,11 @@ +import { FC, memo } from 'react' +import ReactMarkdown, { Options } from 'react-markdown' + +const MemoizedReactMarkdown: FC = memo( + ReactMarkdown, + (prevProps, nextProps) => + prevProps.children === nextProps.children && + prevProps.className === nextProps.className +) + +export default MemoizedReactMarkdown; \ No newline at end of file diff --git a/packages/amplify-util-blog/ui-components/blog/NewPostLayout.tsx b/packages/amplify-util-blog/ui-components/blog/NewPostLayout.tsx new file mode 100644 index 0000000..aa677f8 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/NewPostLayout.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { useRouter } from "next/navigation"; //todo: refactor +import { Flex, Heading } from "@aws-amplify/ui-react"; +import { default as PostCreateForm } from "./base/PostCreateForm"; +import { default as TagCreateForm } from "./base/TagCreateForm"; + + const NewPostLayout = () => { + const { push } = useRouter(); + return ( + <> + + Create a New Tag + + Create a New Post + { + push("/"); + }} + /> + + + ); +} + +export default NewPostLayout; diff --git a/packages/amplify-util-blog/ui-components/blog/Page.jsx b/packages/amplify-util-blog/ui-components/blog/Page.jsx deleted file mode 100644 index 72fb50a..0000000 --- a/packages/amplify-util-blog/ui-components/blog/Page.jsx +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************** - * The contents of this file were generated with Amplify Studio. * - * Please refrain from making any modifications to this file. * - * Any changes to this file will be overwritten when running amplify pull. * - **************************************************************************/ - -/* eslint-disable */ -import * as React from "react"; -import { getOverrideProps } from "@aws-amplify/ui-react/internal"; -import { Flex } from "@aws-amplify/ui-react"; -export default function Page(props) { - const { overrides, ...rest } = props; - return ( - - - - - ); -} diff --git a/packages/amplify-util-blog/ui-components/blog/SearchResults.tsx b/packages/amplify-util-blog/ui-components/blog/SearchResults.tsx new file mode 100644 index 0000000..30f31ea --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/SearchResults.tsx @@ -0,0 +1,60 @@ +import React, { useEffect, useState } from "react"; +import { API } from "aws-amplify"; +import { Flex } from "@aws-amplify/ui-react"; +import { deserializeModel } from "@aws-amplify/datastore/ssr"; +import { GraphQLQuery } from "@aws-amplify/api"; + +import * as queries from "./../../graphql/queries"; +import { SearchPostsQuery } from "./../../API"; +import { default as CommonPostCardCollection } from "./CommonPostCardCollection"; +import { Post } from "./../../models"; + +interface SearchResultsProps { + searchText: string; +} + +const SearchResults = ({ searchText }: SearchResultsProps) => { + const [searchResults, setSearchResults] = useState(); + + const searchPost = async (text: string) => { + if (searchText === "") return; + + const results = await API.graphql>({ + query: queries.searchPosts, + variables: { + searchParameters: JSON.stringify({ + q: `*${text}*`, + query_by: "title", + }), + }, + }); + if (results.data?.searchPosts) { + try { + const posts = JSON.parse(results.data?.searchPosts).hits.map( + (hit: any) => { + return deserializeModel(Post, hit.document); + } + ); + setSearchResults(posts); + } catch (error) { + console.error("could not parse search results"); + } + } else { + alert("no results found"); + } + }; + + useEffect(() => { + searchPost(searchText); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + {searchResults && } + {!searchResults &&

No results found

} +
+ ); +}; + +export default SearchResults; diff --git a/packages/amplify-util-blog/ui-components/blog/UpdatePostLayout.tsx b/packages/amplify-util-blog/ui-components/blog/UpdatePostLayout.tsx new file mode 100644 index 0000000..9c88481 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/UpdatePostLayout.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { useRouter } from "next/navigation"; +import { Flex, Heading } from "@aws-amplify/ui-react"; + +import { default as PostUpdateForm } from "./base/PostUpdateForm"; +import { default as TagCreateForm } from "./base/TagCreateForm"; +import { Post } from "./../../models"; + +interface UpdatePostLayoutProps { + post: Post; +} + +const UpdatePostLayout = ({ post }: UpdatePostLayoutProps) => { + const { push } = useRouter(); + return ( + <> + + Create a New Tag + + Update Post + { + push("/"); + }} + post={post as Post} + /> + + + ); +}; + +export default UpdatePostLayout; diff --git a/packages/amplify-util-blog/ui-components/blog/WrappedBadgeElementCollection.tsx b/packages/amplify-util-blog/ui-components/blog/WrappedBadgeElementCollection.tsx new file mode 100644 index 0000000..ef826de --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/WrappedBadgeElementCollection.tsx @@ -0,0 +1,44 @@ +import { Post, Tag } from "@/models"; +import { default as BadgeElementCollection } from "./base/BadgeElementCollection"; +import { useEffect, useState } from "react"; + +const WrappedBadgeElementCollection = ({ post }: { post: Post }) => { + const [postTags, setPostTags] = useState([]); + + useEffect( + () => { + const work = async () => { + const tags = await post.tags.toArray(); + setPostTags( + tags.map( + (tag) => + ({ + name: tag.tagName, + } as Tag) + ) + ); + }; + work(); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + return ( + ({ + overrides: { + Badge: { + style: { + cursor: "pointer", + }, + }, + }, + })} + /> + ); + }; + +export default WrappedBadgeElementCollection; + \ No newline at end of file diff --git a/packages/amplify-util-blog/ui-components/blog/Page.d.ts b/packages/amplify-util-blog/ui-components/blog/base/BadgeElement.d.ts similarity index 54% rename from packages/amplify-util-blog/ui-components/blog/Page.d.ts rename to packages/amplify-util-blog/ui-components/blog/base/BadgeElement.d.ts index fb87f97..7e4150b 100644 --- a/packages/amplify-util-blog/ui-components/blog/Page.d.ts +++ b/packages/amplify-util-blog/ui-components/blog/base/BadgeElement.d.ts @@ -5,15 +5,17 @@ **************************************************************************/ import * as React from "react"; +import { Tag } from "../../../models"; import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; -import { FlexProps } from "@aws-amplify/ui-react"; +import { BadgeProps, FlexProps } from "@aws-amplify/ui-react"; export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; -export declare type PageOverridesProps = { - Page?: PrimitiveOverrideProps; - "Frame 1"?: PrimitiveOverrideProps; - "Frame 2"?: PrimitiveOverrideProps; +export declare type BadgeElementOverridesProps = { + BadgeElement?: PrimitiveOverrideProps; + Badge?: PrimitiveOverrideProps; } & EscapeHatchProps; -export declare type PageProps = React.PropsWithChildren & { - overrides?: PageOverridesProps | undefined | null; +export declare type BadgeElementProps = React.PropsWithChildren & { + tag?: Tag; +} & { + overrides?: BadgeElementOverridesProps | undefined | null; }>; -export default function Page(props: PageProps): React.ReactElement; +export default function BadgeElement(props: BadgeElementProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/BadgeElement.jsx b/packages/amplify-util-blog/ui-components/blog/base/BadgeElement.jsx new file mode 100644 index 0000000..41d92ea --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/BadgeElement.jsx @@ -0,0 +1,47 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { + getOverrideProps, + useNavigateAction, +} from "@aws-amplify/ui-react/internal"; +import { Badge, Flex } from "@aws-amplify/ui-react"; +export default function BadgeElement(props) { + const { tag, overrides, ...rest } = props; + const badgeElementOnClick = useNavigateAction({ + type: "url", + url: `${"/tag/"}${tag?.name}`, + }); + return ( + { + badgeElementOnClick(); + }} + {...getOverrideProps(overrides, "BadgeElement")} + {...rest} + > + + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.d.ts b/packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.d.ts new file mode 100644 index 0000000..ea3e130 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.d.ts @@ -0,0 +1,25 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +import * as React from "react"; +import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; +import { BadgeElementProps } from "./BadgeElement"; +import { CollectionProps } from "@aws-amplify/ui-react"; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type BadgeElementCollectionOverridesProps = { + BadgeElementCollection?: PrimitiveOverrideProps; + BadgeElement?: BadgeElementProps; +} & EscapeHatchProps; +export declare type BadgeElementCollectionProps = React.PropsWithChildren> & { + items?: any[]; + overrideItems?: (collectionItem: { + item: any; + index: number; + }) => BadgeElementProps; +} & { + overrides?: BadgeElementCollectionOverridesProps | undefined | null; +}>; +export default function BadgeElementCollection(props: BadgeElementCollectionProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.jsx b/packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.jsx new file mode 100644 index 0000000..7d7601c --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/BadgeElementCollection.jsx @@ -0,0 +1,51 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { Tag } from "../../../models"; +import { + getOverrideProps, + useDataStoreBinding, +} from "@aws-amplify/ui-react/internal"; +import BadgeElement from "./BadgeElement"; +import { Collection } from "@aws-amplify/ui-react"; +export default function BadgeElementCollection(props) { + const { items: itemsProp, overrideItems, overrides, ...rest } = props; + const [items, setItems] = React.useState(undefined); + const itemsDataStore = useDataStoreBinding({ + type: "collection", + model: Tag, + }).items; + React.useEffect(() => { + if (itemsProp !== undefined) { + setItems(itemsProp); + return; + } + setItems(itemsDataStore); + }, [itemsProp, itemsDataStore]); + return ( + + {(item, index) => ( + + )} + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostCard.d.ts b/packages/amplify-util-blog/ui-components/blog/base/PostCard.d.ts new file mode 100644 index 0000000..b142000 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostCard.d.ts @@ -0,0 +1,34 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +import * as React from "react"; +import { Post } from "../../../models"; +import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; +import { FlexProps, TextProps } from "@aws-amplify/ui-react"; +import { MyIconProps } from "../../MyIcon"; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type PostCardOverridesProps = { + PostCard?: PrimitiveOverrideProps; + Body?: PrimitiveOverrideProps; + ImageContainer?: PrimitiveOverrideProps; + Text?: PrimitiveOverrideProps; + Headline?: PrimitiveOverrideProps; + HeadlineText?: PrimitiveOverrideProps; + Tags?: PrimitiveOverrideProps; + DateText?: PrimitiveOverrideProps; + Article?: PrimitiveOverrideProps; + ArticleText?: PrimitiveOverrideProps; + ReadMoreLayout?: PrimitiveOverrideProps; + MyIcon?: MyIconProps; + ReadMoreText?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type PostCardProps = React.PropsWithChildren & { + post?: Post; + imageContainer?: React.ReactNode; +} & { + overrides?: PostCardOverridesProps | undefined | null; +}>; +export default function PostCard(props: PostCardProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostCard.jsx b/packages/amplify-util-blog/ui-components/blog/base/PostCard.jsx new file mode 100644 index 0000000..6d964e9 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostCard.jsx @@ -0,0 +1,247 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { + getOverrideProps, + useNavigateAction, +} from "@aws-amplify/ui-react/internal"; +import { Flex, Text } from "@aws-amplify/ui-react"; +import MyIcon from "../../MyIcon"; +export default function PostCard(props) { + const { post, imageContainer, overrides, ...rest } = props; + const readMoreLayoutOnClick = useNavigateAction({ + type: "url", + url: `${"/post/"}${post?.slug}`, + }); + return ( + + + + + + + + + + + + + { + readMoreLayoutOnClick(); + }} + {...getOverrideProps(overrides, "ReadMoreLayout")} + > + + + + + + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.d.ts b/packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.d.ts new file mode 100644 index 0000000..6ea0e56 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.d.ts @@ -0,0 +1,25 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +import * as React from "react"; +import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; +import { PostCardProps } from "./PostCard"; +import { CollectionProps } from "@aws-amplify/ui-react"; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type PostCardCollectionOverridesProps = { + PostCardCollection?: PrimitiveOverrideProps; + PostCard?: PostCardProps; +} & EscapeHatchProps; +export declare type PostCardCollectionProps = React.PropsWithChildren> & { + items?: any[]; + overrideItems?: (collectionItem: { + item: any; + index: number; + }) => PostCardProps; +} & { + overrides?: PostCardCollectionOverridesProps | undefined | null; +}>; +export default function PostCardCollection(props: PostCardCollectionProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.jsx b/packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.jsx new file mode 100644 index 0000000..bda08c1 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostCardCollection.jsx @@ -0,0 +1,55 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { Post } from "../../../models"; +import { + createDataStorePredicate, + getOverrideProps, + useDataStoreBinding, +} from "@aws-amplify/ui-react/internal"; +import PostCard from "./PostCard"; +import { Collection } from "@aws-amplify/ui-react"; +export default function PostCardCollection(props) { + const { items: itemsProp, overrideItems, overrides, ...rest } = props; + const itemsFilterObj = { field: "published", operand: true, operator: "eq" }; + const itemsFilter = createDataStorePredicate(itemsFilterObj); + const [items, setItems] = React.useState(undefined); + const itemsDataStore = useDataStoreBinding({ + type: "collection", + model: Post, + criteria: itemsFilter, + }).items; + React.useEffect(() => { + if (itemsProp !== undefined) { + setItems(itemsProp); + return; + } + setItems(itemsDataStore); + }, [itemsProp, itemsDataStore]); + return ( + + {(item, index) => ( + + )} + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.d.ts b/packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.d.ts new file mode 100644 index 0000000..a336eff --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.d.ts @@ -0,0 +1,56 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +import * as React from "react"; +import { AutocompleteProps, GridProps, SwitchFieldProps, TextAreaFieldProps, TextFieldProps } from "@aws-amplify/ui-react"; +import { StorageManagerProps } from "@aws-amplify/ui-react-storage"; +import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; +import { Tag } from "../../../models"; +export declare type ValidationResponse = { + hasError: boolean; + errorMessage?: string; +}; +export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; +export declare type PostCreateFormInputValues = { + slug?: string; + title?: string; + description?: string; + content?: string; + image?: string; + published?: boolean; + tags?: Tag[]; +}; +export declare type PostCreateFormValidationValues = { + slug?: ValidationFunction; + title?: ValidationFunction; + description?: ValidationFunction; + content?: ValidationFunction; + image?: ValidationFunction; + published?: ValidationFunction; + tags?: ValidationFunction; +}; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type PostCreateFormOverridesProps = { + PostCreateFormGrid?: PrimitiveOverrideProps; + slug?: PrimitiveOverrideProps; + title?: PrimitiveOverrideProps; + description?: PrimitiveOverrideProps; + content?: PrimitiveOverrideProps; + image?: PrimitiveOverrideProps; + published?: PrimitiveOverrideProps; + tags?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type PostCreateFormProps = React.PropsWithChildren<{ + overrides?: PostCreateFormOverridesProps | undefined | null; +} & { + clearOnSuccess?: boolean; + onSubmit?: (fields: PostCreateFormInputValues) => PostCreateFormInputValues; + onSuccess?: (fields: PostCreateFormInputValues) => void; + onError?: (fields: PostCreateFormInputValues, errorMessage: string) => void; + onChange?: (fields: PostCreateFormInputValues) => PostCreateFormInputValues; + onValidate?: PostCreateFormValidationValues; +} & React.CSSProperties>; +export default function PostCreateForm(props: PostCreateFormProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.jsx b/packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.jsx new file mode 100644 index 0000000..61b81f6 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostCreateForm.jsx @@ -0,0 +1,694 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { + Autocomplete, + Badge, + Button, + Divider, + Flex, + Grid, + Icon, + ScrollView, + SwitchField, + Text, + TextAreaField, + TextField, + useTheme, +} from "@aws-amplify/ui-react"; +import { StorageManager } from "@aws-amplify/ui-react-storage"; +import { + Field, + getOverrideProps, + useDataStoreBinding, +} from "@aws-amplify/ui-react/internal"; +import { Post, Tag, PostTag } from "../../../models"; +import { fetchByPath, processFile, validateField } from "../../utils"; +import { DataStore } from "aws-amplify"; +function ArrayField({ + items = [], + onChange, + label, + inputFieldRef, + children, + hasError, + setFieldValue, + currentFieldValue, + defaultFieldValue, + lengthLimit, + getBadgeText, + runValidationTasks, + errorMessage, +}) { + const labelElement = {label}; + const { + tokens: { + components: { + fieldmessages: { error: errorStyles }, + }, + }, + } = useTheme(); + const [selectedBadgeIndex, setSelectedBadgeIndex] = React.useState(); + const [isEditing, setIsEditing] = React.useState(); + React.useEffect(() => { + if (isEditing) { + inputFieldRef?.current?.focus(); + } + }, [isEditing]); + const removeItem = async (removeIndex) => { + const newItems = items.filter((value, index) => index !== removeIndex); + await onChange(newItems); + setSelectedBadgeIndex(undefined); + }; + const addItem = async () => { + const { hasError } = runValidationTasks(); + if ( + currentFieldValue !== undefined && + currentFieldValue !== null && + currentFieldValue !== "" && + !hasError + ) { + const newItems = [...items]; + if (selectedBadgeIndex !== undefined) { + newItems[selectedBadgeIndex] = currentFieldValue; + setSelectedBadgeIndex(undefined); + } else { + newItems.push(currentFieldValue); + } + await onChange(newItems); + setIsEditing(false); + } + }; + const arraySection = ( + + {!!items?.length && ( + + {items.map((value, index) => { + return ( + { + setSelectedBadgeIndex(index); + setFieldValue(items[index]); + setIsEditing(true); + }} + > + {getBadgeText ? getBadgeText(value) : value.toString()} + { + event.stopPropagation(); + removeItem(index); + }} + /> + + ); + })} + + )} + + + ); + if (lengthLimit !== undefined && items.length >= lengthLimit && !isEditing) { + return ( + + {labelElement} + {arraySection} + + ); + } + return ( + + {labelElement} + {isEditing && children} + {!isEditing ? ( + <> + + {errorMessage && hasError && ( + + {errorMessage} + + )} + + ) : ( + + {(currentFieldValue || isEditing) && ( + + )} + + + )} + {arraySection} + + ); +} +export default function PostCreateForm(props) { + const { + clearOnSuccess = true, + onSuccess, + onError, + onSubmit, + onValidate, + onChange, + overrides, + ...rest + } = props; + const initialValues = { + slug: "", + title: "", + description: "", + content: "", + image: undefined, + published: false, + tags: [], + }; + const [slug, setSlug] = React.useState(initialValues.slug); + const [title, setTitle] = React.useState(initialValues.title); + const [description, setDescription] = React.useState( + initialValues.description + ); + const [content, setContent] = React.useState(initialValues.content); + const [image, setImage] = React.useState(initialValues.image); + const [published, setPublished] = React.useState(initialValues.published); + const [tags, setTags] = React.useState(initialValues.tags); + const [errors, setErrors] = React.useState({}); + const resetStateValues = () => { + setSlug(initialValues.slug); + setTitle(initialValues.title); + setDescription(initialValues.description); + setContent(initialValues.content); + setImage(initialValues.image); + setPublished(initialValues.published); + setTags(initialValues.tags); + setCurrentTagsValue(undefined); + setCurrentTagsDisplayValue(""); + setErrors({}); + }; + const [currentTagsDisplayValue, setCurrentTagsDisplayValue] = + React.useState(""); + const [currentTagsValue, setCurrentTagsValue] = React.useState(undefined); + const tagsRef = React.createRef(); + const getIDValue = { + tags: (r) => JSON.stringify({ name: r?.name }), + }; + const tagsIdSet = new Set( + Array.isArray(tags) + ? tags.map((r) => getIDValue.tags?.(r)) + : getIDValue.tags?.(tags) + ); + const tagRecords = useDataStoreBinding({ + type: "collection", + model: Tag, + }).items; + const getDisplayValue = { + tags: (r) => r?.name, + }; + const validations = { + slug: [ + { type: "Required" }, + { + type: "NotContains", + strValues: ["' '"], + validationMessage: "The value must not contain spaces", + }, + { + type: "NotContains", + strValues: ["&", "?", "$", "#", "/", "=", "@", "+"], + validationMessage: + 'The value must not contain "&" or "?" or "$" or "#" or "/" or "=" or "@" or "+"', + }, + ], + title: [{ type: "Required" }], + description: [{ type: "Required" }], + content: [{ type: "Required" }], + image: [{ type: "Required" }], + published: [{ type: "Required" }], + tags: [], + }; + const runValidationTasks = async ( + fieldName, + currentValue, + getDisplayValue + ) => { + const value = + currentValue && getDisplayValue + ? getDisplayValue(currentValue) + : currentValue; + let validationResponse = validateField(value, validations[fieldName]); + const customValidator = fetchByPath(onValidate, fieldName); + if (customValidator) { + validationResponse = await customValidator(value, validationResponse); + } + setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); + return validationResponse; + }; + return ( + { + event.preventDefault(); + let modelFields = { + slug, + title, + description, + content, + image, + published, + tags, + }; + const validationResponses = await Promise.all( + Object.keys(validations).reduce((promises, fieldName) => { + if (Array.isArray(modelFields[fieldName])) { + promises.push( + ...modelFields[fieldName].map((item) => + runValidationTasks( + fieldName, + item, + getDisplayValue[fieldName] + ) + ) + ); + return promises; + } + promises.push( + runValidationTasks( + fieldName, + modelFields[fieldName], + getDisplayValue[fieldName] + ) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === "string" && value === "") { + modelFields[key] = null; + } + }); + const modelFieldsToSave = { + slug: modelFields.slug, + title: modelFields.title, + description: modelFields.description, + content: modelFields.content, + image: modelFields.image, + published: modelFields.published, + }; + const post = await DataStore.save(new Post(modelFieldsToSave)); + const promises = []; + promises.push( + ...tags.reduce((promises, tag) => { + promises.push( + DataStore.save( + new PostTag({ + post, + tag, + }) + ) + ); + return promises; + }, []) + ); + await Promise.all(promises); + if (onSuccess) { + onSuccess(modelFields); + } + if (clearOnSuccess) { + resetStateValues(); + } + } catch (err) { + if (onError) { + onError(modelFields, err.message); + } + } + }} + {...getOverrideProps(overrides, "PostCreateForm")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug: value, + title, + description, + content, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.slug ?? value; + } + if (errors.slug?.hasError) { + runValidationTasks("slug", value); + } + setSlug(value); + }} + onBlur={() => runValidationTasks("slug", slug)} + errorMessage={errors.slug?.errorMessage} + hasError={errors.slug?.hasError} + {...getOverrideProps(overrides, "slug")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug, + title: value, + description, + content, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.title ?? value; + } + if (errors.title?.hasError) { + runValidationTasks("title", value); + } + setTitle(value); + }} + onBlur={() => runValidationTasks("title", title)} + errorMessage={errors.title?.errorMessage} + hasError={errors.title?.hasError} + {...getOverrideProps(overrides, "title")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug, + title, + description: value, + content, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.description ?? value; + } + if (errors.description?.hasError) { + runValidationTasks("description", value); + } + setDescription(value); + }} + onBlur={() => runValidationTasks("description", description)} + errorMessage={errors.description?.errorMessage} + hasError={errors.description?.hasError} + {...getOverrideProps(overrides, "description")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug, + title, + description, + content: value, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.content ?? value; + } + if (errors.content?.hasError) { + runValidationTasks("content", value); + } + setContent(value); + }} + onBlur={() => runValidationTasks("content", content)} + errorMessage={errors.content?.errorMessage} + hasError={errors.content?.hasError} + {...getOverrideProps(overrides, "content")} + > + + { + setImage((prev) => { + let value = key; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image: value, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.image ?? value; + } + return value; + }); + }} + onFileRemove={({ key }) => { + setImage((prev) => { + let value = initialValues?.image; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image: value, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.image ?? value; + } + return value; + }); + }} + processFile={processFile} + accessLevel={"public"} + acceptedFileTypes={[]} + isResumable={false} + showThumbnails={true} + maxFileCount={1} + {...getOverrideProps(overrides, "image")} + > + + { + let value = e.target.checked; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image, + published: value, + tags, + }; + const result = onChange(modelFields); + value = result?.published ?? value; + } + if (errors.published?.hasError) { + runValidationTasks("published", value); + } + setPublished(value); + }} + onBlur={() => runValidationTasks("published", published)} + errorMessage={errors.published?.errorMessage} + hasError={errors.published?.hasError} + {...getOverrideProps(overrides, "published")} + > + { + let values = items; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image, + published, + tags: values, + }; + const result = onChange(modelFields); + values = result?.tags ?? values; + } + setTags(values); + setCurrentTagsValue(undefined); + setCurrentTagsDisplayValue(""); + }} + currentFieldValue={currentTagsValue} + label={"Tags"} + items={tags} + hasError={errors?.tags?.hasError} + runValidationTasks={async () => + await runValidationTasks("tags", currentTagsValue) + } + errorMessage={errors?.tags?.errorMessage} + getBadgeText={getDisplayValue.tags} + setFieldValue={(model) => { + setCurrentTagsDisplayValue(model ? getDisplayValue.tags(model) : ""); + setCurrentTagsValue(model); + }} + inputFieldRef={tagsRef} + defaultFieldValue={""} + > + !tagsIdSet.has(getIDValue.tags?.(r))) + .map((r) => ({ + id: getIDValue.tags?.(r), + label: getDisplayValue.tags?.(r), + }))} + onSelect={({ id, label }) => { + setCurrentTagsValue( + tagRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentTagsDisplayValue(label); + runValidationTasks("tags", label); + }} + onClear={() => { + setCurrentTagsDisplayValue(""); + }} + onChange={(e) => { + let { value } = e.target; + if (errors.tags?.hasError) { + runValidationTasks("tags", value); + } + setCurrentTagsDisplayValue(value); + setCurrentTagsValue(undefined); + }} + onBlur={() => runValidationTasks("tags", currentTagsDisplayValue)} + errorMessage={errors.tags?.errorMessage} + hasError={errors.tags?.hasError} + ref={tagsRef} + labelHidden={true} + {...getOverrideProps(overrides, "tags")} + > + + + + + + + + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostDetail.d.ts b/packages/amplify-util-blog/ui-components/blog/base/PostDetail.d.ts new file mode 100644 index 0000000..a75fa7b --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostDetail.d.ts @@ -0,0 +1,29 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +import * as React from "react"; +import { Post } from "../../../models"; +import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; +import { FlexProps, HeadingProps, TextProps } from "@aws-amplify/ui-react"; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type PostDetailOverridesProps = { + PostDetail?: PrimitiveOverrideProps; + Body?: PrimitiveOverrideProps; + ImageContainer?: PrimitiveOverrideProps; + Text?: PrimitiveOverrideProps; + Headline?: PrimitiveOverrideProps; + Heading?: PrimitiveOverrideProps; + Tags?: PrimitiveOverrideProps; + DateText?: PrimitiveOverrideProps; + MarkdownContainer?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type PostDetailProps = React.PropsWithChildren & { + post?: Post; + imageContainer?: React.ReactNode; +} & { + overrides?: PostDetailOverridesProps | undefined | null; +}>; +export default function PostDetail(props: PostDetailProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostDetail.jsx b/packages/amplify-util-blog/ui-components/blog/base/PostDetail.jsx new file mode 100644 index 0000000..ad4b261 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostDetail.jsx @@ -0,0 +1,145 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { getOverrideProps } from "@aws-amplify/ui-react/internal"; +import { Flex, Heading, Text } from "@aws-amplify/ui-react"; +export default function PostDetail(props) { + const { post, imageContainer, overrides, ...rest } = props; + return ( + + + + + + + + + + + + + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.d.ts b/packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.d.ts new file mode 100644 index 0000000..ac72b5b --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.d.ts @@ -0,0 +1,57 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +import * as React from "react"; +import { AutocompleteProps, GridProps, SwitchFieldProps, TextFieldProps } from "@aws-amplify/ui-react"; +import { StorageManagerProps } from "@aws-amplify/ui-react-storage"; +import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; +import { Post, Tag } from "../../../models"; +export declare type ValidationResponse = { + hasError: boolean; + errorMessage?: string; +}; +export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; +export declare type PostUpdateFormInputValues = { + slug?: string; + title?: string; + description?: string; + content?: string; + image?: string; + published?: boolean; + tags?: Tag[]; +}; +export declare type PostUpdateFormValidationValues = { + slug?: ValidationFunction; + title?: ValidationFunction; + description?: ValidationFunction; + content?: ValidationFunction; + image?: ValidationFunction; + published?: ValidationFunction; + tags?: ValidationFunction; +}; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type PostUpdateFormOverridesProps = { + PostUpdateFormGrid?: PrimitiveOverrideProps; + slug?: PrimitiveOverrideProps; + title?: PrimitiveOverrideProps; + description?: PrimitiveOverrideProps; + content?: PrimitiveOverrideProps; + image?: PrimitiveOverrideProps; + published?: PrimitiveOverrideProps; + tags?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type PostUpdateFormProps = React.PropsWithChildren<{ + overrides?: PostUpdateFormOverridesProps | undefined | null; +} & { + slug?: string; + post?: Post; + onSubmit?: (fields: PostUpdateFormInputValues) => PostUpdateFormInputValues; + onSuccess?: (fields: PostUpdateFormInputValues) => void; + onError?: (fields: PostUpdateFormInputValues, errorMessage: string) => void; + onChange?: (fields: PostUpdateFormInputValues) => PostUpdateFormInputValues; + onValidate?: PostUpdateFormValidationValues; +} & React.CSSProperties>; +export default function PostUpdateForm(props: PostUpdateFormProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.jsx b/packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.jsx new file mode 100644 index 0000000..a690d3f --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/PostUpdateForm.jsx @@ -0,0 +1,768 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { + Autocomplete, + Badge, + Button, + Divider, + Flex, + Grid, + Icon, + ScrollView, + SwitchField, + Text, + TextField, + useTheme, +} from "@aws-amplify/ui-react"; +import { StorageManager } from "@aws-amplify/ui-react-storage"; +import { + Field, + getOverrideProps, + useDataStoreBinding, +} from "@aws-amplify/ui-react/internal"; +import { Post, Tag, PostTag } from "../../../models"; +import { fetchByPath, processFile, validateField } from "../../utils"; +import { DataStore } from "aws-amplify"; +function ArrayField({ + items = [], + onChange, + label, + inputFieldRef, + children, + hasError, + setFieldValue, + currentFieldValue, + defaultFieldValue, + lengthLimit, + getBadgeText, + runValidationTasks, + errorMessage, +}) { + const labelElement = {label}; + const { + tokens: { + components: { + fieldmessages: { error: errorStyles }, + }, + }, + } = useTheme(); + const [selectedBadgeIndex, setSelectedBadgeIndex] = React.useState(); + const [isEditing, setIsEditing] = React.useState(); + React.useEffect(() => { + if (isEditing) { + inputFieldRef?.current?.focus(); + } + }, [isEditing]); + const removeItem = async (removeIndex) => { + const newItems = items.filter((value, index) => index !== removeIndex); + await onChange(newItems); + setSelectedBadgeIndex(undefined); + }; + const addItem = async () => { + const { hasError } = runValidationTasks(); + if ( + currentFieldValue !== undefined && + currentFieldValue !== null && + currentFieldValue !== "" && + !hasError + ) { + const newItems = [...items]; + if (selectedBadgeIndex !== undefined) { + newItems[selectedBadgeIndex] = currentFieldValue; + setSelectedBadgeIndex(undefined); + } else { + newItems.push(currentFieldValue); + } + await onChange(newItems); + setIsEditing(false); + } + }; + const arraySection = ( + + {!!items?.length && ( + + {items.map((value, index) => { + return ( + { + setSelectedBadgeIndex(index); + setFieldValue(items[index]); + setIsEditing(true); + }} + > + {getBadgeText ? getBadgeText(value) : value.toString()} + { + event.stopPropagation(); + removeItem(index); + }} + /> + + ); + })} + + )} + + + ); + if (lengthLimit !== undefined && items.length >= lengthLimit && !isEditing) { + return ( + + {labelElement} + {arraySection} + + ); + } + return ( + + {labelElement} + {isEditing && children} + {!isEditing ? ( + <> + + {errorMessage && hasError && ( + + {errorMessage} + + )} + + ) : ( + + {(currentFieldValue || isEditing) && ( + + )} + + + )} + {arraySection} + + ); +} +export default function PostUpdateForm(props) { + const { + slug: slugProp, + post: postModelProp, + onSuccess, + onError, + onSubmit, + onValidate, + onChange, + overrides, + ...rest + } = props; + const initialValues = { + slug: "", + title: "", + description: "", + content: "", + image: undefined, + published: false, + tags: [], + }; + const [slug, setSlug] = React.useState(initialValues.slug); + const [title, setTitle] = React.useState(initialValues.title); + const [description, setDescription] = React.useState( + initialValues.description + ); + const [content, setContent] = React.useState(initialValues.content); + const [image, setImage] = React.useState(initialValues.image); + const [published, setPublished] = React.useState(initialValues.published); + const [tags, setTags] = React.useState(initialValues.tags); + const [errors, setErrors] = React.useState({}); + const resetStateValues = () => { + const cleanValues = postRecord + ? { ...initialValues, ...postRecord, tags: linkedTags } + : initialValues; + setSlug(cleanValues.slug); + setTitle(cleanValues.title); + setDescription(cleanValues.description); + setContent(cleanValues.content); + setImage(cleanValues.image); + setPublished(cleanValues.published); + setTags(cleanValues.tags ?? []); + setCurrentTagsValue(undefined); + setCurrentTagsDisplayValue(""); + setErrors({}); + }; + const [postRecord, setPostRecord] = React.useState(postModelProp); + const [linkedTags, setLinkedTags] = React.useState([]); + const canUnlinkTags = false; + React.useEffect(() => { + const queryData = async () => { + const record = slugProp + ? await DataStore.query(Post, slugProp) + : postModelProp; + setPostRecord(record); + const linkedTags = record + ? await Promise.all( + ( + await record.tags.toArray() + ).map((r) => { + return r.tag; + }) + ) + : []; + setLinkedTags(linkedTags); + }; + queryData(); + }, [slugProp, postModelProp]); + React.useEffect(resetStateValues, [postRecord, linkedTags]); + const [currentTagsDisplayValue, setCurrentTagsDisplayValue] = + React.useState(""); + const [currentTagsValue, setCurrentTagsValue] = React.useState(undefined); + const tagsRef = React.createRef(); + const getIDValue = { + tags: (r) => JSON.stringify({ name: r?.name }), + }; + const tagsIdSet = new Set( + Array.isArray(tags) + ? tags.map((r) => getIDValue.tags?.(r)) + : getIDValue.tags?.(tags) + ); + const tagRecords = useDataStoreBinding({ + type: "collection", + model: Tag, + }).items; + const getDisplayValue = { + tags: (r) => r?.name, + }; + const validations = { + slug: [{ type: "Required" }], + title: [{ type: "Required" }], + description: [{ type: "Required" }], + content: [{ type: "Required" }], + image: [{ type: "Required" }], + published: [{ type: "Required" }], + tags: [], + }; + const runValidationTasks = async ( + fieldName, + currentValue, + getDisplayValue + ) => { + const value = + currentValue && getDisplayValue + ? getDisplayValue(currentValue) + : currentValue; + let validationResponse = validateField(value, validations[fieldName]); + const customValidator = fetchByPath(onValidate, fieldName); + if (customValidator) { + validationResponse = await customValidator(value, validationResponse); + } + setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); + return validationResponse; + }; + return ( + { + event.preventDefault(); + let modelFields = { + slug, + title, + description, + content, + image, + published, + tags, + }; + const validationResponses = await Promise.all( + Object.keys(validations).reduce((promises, fieldName) => { + if (Array.isArray(modelFields[fieldName])) { + promises.push( + ...modelFields[fieldName].map((item) => + runValidationTasks( + fieldName, + item, + getDisplayValue[fieldName] + ) + ) + ); + return promises; + } + promises.push( + runValidationTasks( + fieldName, + modelFields[fieldName], + getDisplayValue[fieldName] + ) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === "string" && value === "") { + modelFields[key] = null; + } + }); + const promises = []; + const tagsToLinkMap = new Map(); + const tagsToUnLinkMap = new Map(); + const tagsMap = new Map(); + const linkedTagsMap = new Map(); + tags.forEach((r) => { + const count = tagsMap.get(getIDValue.tags?.(r)); + const newCount = count ? count + 1 : 1; + tagsMap.set(getIDValue.tags?.(r), newCount); + }); + linkedTags.forEach((r) => { + const count = linkedTagsMap.get(getIDValue.tags?.(r)); + const newCount = count ? count + 1 : 1; + linkedTagsMap.set(getIDValue.tags?.(r), newCount); + }); + linkedTagsMap.forEach((count, id) => { + const newCount = tagsMap.get(id); + if (newCount) { + const diffCount = count - newCount; + if (diffCount > 0) { + tagsToUnLinkMap.set(id, diffCount); + } + } else { + tagsToUnLinkMap.set(id, count); + } + }); + tagsMap.forEach((count, id) => { + const originalCount = linkedTagsMap.get(id); + if (originalCount) { + const diffCount = count - originalCount; + if (diffCount > 0) { + tagsToLinkMap.set(id, diffCount); + } + } else { + tagsToLinkMap.set(id, count); + } + }); + tagsToUnLinkMap.forEach(async (count, id) => { + const recordKeys = JSON.parse(id); + const postTagRecords = await DataStore.query(PostTag, (r) => + r.and((r) => { + return [ + r.tagName.eq(recordKeys.name), + r.postSlug.eq(postRecord.slug), + ]; + }) + ); + for (let i = 0; i < count; i++) { + promises.push(DataStore.delete(postTagRecords[i])); + } + }); + tagsToLinkMap.forEach((count, id) => { + const tagToLink = tagRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ); + for (let i = count; i > 0; i--) { + promises.push( + DataStore.save( + new PostTag({ + post: postRecord, + tag: tagToLink, + }) + ) + ); + } + }); + const modelFieldsToSave = { + slug: modelFields.slug, + title: modelFields.title, + description: modelFields.description, + content: modelFields.content, + image: modelFields.image, + published: modelFields.published, + }; + promises.push( + DataStore.save( + Post.copyOf(postRecord, (updated) => { + Object.assign(updated, modelFieldsToSave); + }) + ) + ); + await Promise.all(promises); + if (onSuccess) { + onSuccess(modelFields); + } + } catch (err) { + if (onError) { + onError(modelFields, err.message); + } + } + }} + {...getOverrideProps(overrides, "PostUpdateForm")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug: value, + title, + description, + content, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.slug ?? value; + } + if (errors.slug?.hasError) { + runValidationTasks("slug", value); + } + setSlug(value); + }} + onBlur={() => runValidationTasks("slug", slug)} + errorMessage={errors.slug?.errorMessage} + hasError={errors.slug?.hasError} + {...getOverrideProps(overrides, "slug")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug, + title: value, + description, + content, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.title ?? value; + } + if (errors.title?.hasError) { + runValidationTasks("title", value); + } + setTitle(value); + }} + onBlur={() => runValidationTasks("title", title)} + errorMessage={errors.title?.errorMessage} + hasError={errors.title?.hasError} + {...getOverrideProps(overrides, "title")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug, + title, + description: value, + content, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.description ?? value; + } + if (errors.description?.hasError) { + runValidationTasks("description", value); + } + setDescription(value); + }} + onBlur={() => runValidationTasks("description", description)} + errorMessage={errors.description?.errorMessage} + hasError={errors.description?.hasError} + {...getOverrideProps(overrides, "description")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + slug, + title, + description, + content: value, + image, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.content ?? value; + } + if (errors.content?.hasError) { + runValidationTasks("content", value); + } + setContent(value); + }} + onBlur={() => runValidationTasks("content", content)} + errorMessage={errors.content?.errorMessage} + hasError={errors.content?.hasError} + {...getOverrideProps(overrides, "content")} + > + + {postRecord && ( + { + setImage((prev) => { + let value = key; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image: value, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.image ?? value; + } + return value; + }); + }} + onFileRemove={({ key }) => { + setImage((prev) => { + let value = initialValues?.image; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image: value, + published, + tags, + }; + const result = onChange(modelFields); + value = result?.image ?? value; + } + return value; + }); + }} + processFile={processFile} + accessLevel={"public"} + acceptedFileTypes={[]} + isResumable={false} + showThumbnails={true} + maxFileCount={1} + {...getOverrideProps(overrides, "image")} + > + )} + + { + let value = e.target.checked; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image, + published: value, + tags, + }; + const result = onChange(modelFields); + value = result?.published ?? value; + } + if (errors.published?.hasError) { + runValidationTasks("published", value); + } + setPublished(value); + }} + onBlur={() => runValidationTasks("published", published)} + errorMessage={errors.published?.errorMessage} + hasError={errors.published?.hasError} + {...getOverrideProps(overrides, "published")} + > + { + let values = items; + if (onChange) { + const modelFields = { + slug, + title, + description, + content, + image, + published, + tags: values, + }; + const result = onChange(modelFields); + values = result?.tags ?? values; + } + setTags(values); + setCurrentTagsValue(undefined); + setCurrentTagsDisplayValue(""); + }} + currentFieldValue={currentTagsValue} + label={"Tags"} + items={tags} + hasError={errors?.tags?.hasError} + runValidationTasks={async () => + await runValidationTasks("tags", currentTagsValue) + } + errorMessage={errors?.tags?.errorMessage} + getBadgeText={getDisplayValue.tags} + setFieldValue={(model) => { + setCurrentTagsDisplayValue(model ? getDisplayValue.tags(model) : ""); + setCurrentTagsValue(model); + }} + inputFieldRef={tagsRef} + defaultFieldValue={""} + > + !tagsIdSet.has(getIDValue.tags?.(r))) + .map((r) => ({ + id: getIDValue.tags?.(r), + label: getDisplayValue.tags?.(r), + }))} + onSelect={({ id, label }) => { + setCurrentTagsValue( + tagRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentTagsDisplayValue(label); + runValidationTasks("tags", label); + }} + onClear={() => { + setCurrentTagsDisplayValue(""); + }} + onChange={(e) => { + let { value } = e.target; + if (errors.tags?.hasError) { + runValidationTasks("tags", value); + } + setCurrentTagsDisplayValue(value); + setCurrentTagsValue(undefined); + }} + onBlur={() => runValidationTasks("tags", currentTagsDisplayValue)} + errorMessage={errors.tags?.errorMessage} + hasError={errors.tags?.hasError} + ref={tagsRef} + labelHidden={true} + {...getOverrideProps(overrides, "tags")} + > + + + + + + + + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.d.ts b/packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.d.ts new file mode 100644 index 0000000..5ff4d33 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.d.ts @@ -0,0 +1,36 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +import * as React from "react"; +import { GridProps, TextFieldProps } from "@aws-amplify/ui-react"; +import { EscapeHatchProps } from "@aws-amplify/ui-react/internal"; +export declare type ValidationResponse = { + hasError: boolean; + errorMessage?: string; +}; +export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; +export declare type TagCreateFormInputValues = { + name?: string; +}; +export declare type TagCreateFormValidationValues = { + name?: ValidationFunction; +}; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type TagCreateFormOverridesProps = { + TagCreateFormGrid?: PrimitiveOverrideProps; + name?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type TagCreateFormProps = React.PropsWithChildren<{ + overrides?: TagCreateFormOverridesProps | undefined | null; +} & { + clearOnSuccess?: boolean; + onSubmit?: (fields: TagCreateFormInputValues) => TagCreateFormInputValues; + onSuccess?: (fields: TagCreateFormInputValues) => void; + onError?: (fields: TagCreateFormInputValues, errorMessage: string) => void; + onChange?: (fields: TagCreateFormInputValues) => TagCreateFormInputValues; + onValidate?: TagCreateFormValidationValues; +} & React.CSSProperties>; +export default function TagCreateForm(props: TagCreateFormProps): React.ReactElement; diff --git a/packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.jsx b/packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.jsx new file mode 100644 index 0000000..beb3c88 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/base/TagCreateForm.jsx @@ -0,0 +1,162 @@ +/*************************************************************************** + * The contents of this file were generated with Amplify Studio. * + * Please refrain from making any modifications to this file. * + * Any changes to this file will be overwritten when running amplify pull. * + **************************************************************************/ + +/* eslint-disable */ +import * as React from "react"; +import { Button, Flex, Grid, TextField } from "@aws-amplify/ui-react"; +import { getOverrideProps } from "@aws-amplify/ui-react/internal"; +import { Tag } from "../../../models"; +import { fetchByPath, validateField } from "../../utils"; +import { DataStore } from "aws-amplify"; +export default function TagCreateForm(props) { + const { + clearOnSuccess = true, + onSuccess, + onError, + onSubmit, + onValidate, + onChange, + overrides, + ...rest + } = props; + const initialValues = { + name: "", + }; + const [name, setName] = React.useState(initialValues.name); + const [errors, setErrors] = React.useState({}); + const resetStateValues = () => { + setName(initialValues.name); + setErrors({}); + }; + const validations = { + name: [{ type: "Required" }], + }; + const runValidationTasks = async ( + fieldName, + currentValue, + getDisplayValue + ) => { + const value = + currentValue && getDisplayValue + ? getDisplayValue(currentValue) + : currentValue; + let validationResponse = validateField(value, validations[fieldName]); + const customValidator = fetchByPath(onValidate, fieldName); + if (customValidator) { + validationResponse = await customValidator(value, validationResponse); + } + setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); + return validationResponse; + }; + return ( + { + event.preventDefault(); + let modelFields = { + name, + }; + const validationResponses = await Promise.all( + Object.keys(validations).reduce((promises, fieldName) => { + if (Array.isArray(modelFields[fieldName])) { + promises.push( + ...modelFields[fieldName].map((item) => + runValidationTasks(fieldName, item) + ) + ); + return promises; + } + promises.push( + runValidationTasks(fieldName, modelFields[fieldName]) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === "string" && value === "") { + modelFields[key] = null; + } + }); + await DataStore.save(new Tag(modelFields)); + if (onSuccess) { + onSuccess(modelFields); + } + if (clearOnSuccess) { + resetStateValues(); + } + } catch (err) { + if (onError) { + onError(modelFields, err.message); + } + } + }} + {...getOverrideProps(overrides, "TagCreateForm")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + name: value, + }; + const result = onChange(modelFields); + value = result?.name ?? value; + } + if (errors.name?.hasError) { + runValidationTasks("name", value); + } + setName(value); + }} + onBlur={() => runValidationTasks("name", name)} + errorMessage={errors.name?.errorMessage} + hasError={errors.name?.hasError} + {...getOverrideProps(overrides, "name")} + > + + + + + + + + ); +} diff --git a/packages/amplify-util-blog/ui-components/blog/index.js b/packages/amplify-util-blog/ui-components/blog/index.js new file mode 100644 index 0000000..0898e23 --- /dev/null +++ b/packages/amplify-util-blog/ui-components/blog/index.js @@ -0,0 +1,15 @@ +export { default as BadgeElement } from "./base/BadgeElement"; +export { default as BadgeElementCollection } from "./base/BadgeElementCollection"; +export { default as PostCard } from "./base/PostCard"; +export { default as PostCardCollection } from "./base/PostCardCollection"; +export { default as PostCreateForm } from "./base/PostCreateForm"; +export { default as PostDetail } from "./base/PostDetail"; +export { default as PostUpdateForm } from "./base/PostUpdateForm"; +export { default as TagCreateForm } from "./base/TagCreateForm"; + +export { default as CommonPostCardCollection } from "./CommonPostCardCollection"; +export { default as MemoizedReactMarkdown } from "./MemoizedReactMarkdown"; +export { default as NewPostLayout } from "./NewPostLayout"; +export { default as SearchResults } from "./SearchResults"; +export { default as UpdatePostLayout } from "./UpdatePostLayout"; +export { default as WrappedBadgeElementCollection } from "./WrappedBadgeElementCollection";