From 281f590828622e686c20be01c858cd187861871f Mon Sep 17 00:00:00 2001 From: "stilyan.tinloof" Date: Mon, 6 Jan 2025 11:45:29 +0000 Subject: [PATCH 1/2] input with characters count component --- .changeset/six-lies-laugh.md | 5 ++ .../sanity-studio/src/components/index.ts | 1 + .../input-with-characters-count.tsx | 70 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 .changeset/six-lies-laugh.md create mode 100644 packages/sanity-studio/src/components/input-with-characters-count.tsx diff --git a/.changeset/six-lies-laugh.md b/.changeset/six-lies-laugh.md new file mode 100644 index 00000000..95f765f8 --- /dev/null +++ b/.changeset/six-lies-laugh.md @@ -0,0 +1,5 @@ +--- +"@tinloof/sanity-studio": minor +--- + +Input with characters count component diff --git a/packages/sanity-studio/src/components/index.ts b/packages/sanity-studio/src/components/index.ts index fb6affcf..f71854d4 100644 --- a/packages/sanity-studio/src/components/index.ts +++ b/packages/sanity-studio/src/components/index.ts @@ -1,2 +1,3 @@ export { IconSelectComponent } from "./IconSelectComponent"; +export { InputWithCharacterCount } from "./input-with-characters-count"; export { PathnameFieldComponent } from "./PathnameFieldComponent"; diff --git a/packages/sanity-studio/src/components/input-with-characters-count.tsx b/packages/sanity-studio/src/components/input-with-characters-count.tsx new file mode 100644 index 00000000..74ade506 --- /dev/null +++ b/packages/sanity-studio/src/components/input-with-characters-count.tsx @@ -0,0 +1,70 @@ +import type { BadgeTone } from "@sanity/ui"; +import { Badge, Flex, Stack } from "@sanity/ui"; +import type { TextInputProps, TextOptions } from "sanity"; +import { useFormValue } from "sanity"; + +type CountedTextOptions = { + maxLength?: number; + minLength?: number; +} & TextOptions; + +function CharacterCount(props: { value?: string } & CountedTextOptions) { + if (!props.maxLength && !props.minLength) { + return null; + } + + const { value = "" } = props; + + const maxPercentage = + props.maxLength && (value.length / props.maxLength) * 100; + + let tone: BadgeTone = "primary"; + + if (maxPercentage && maxPercentage > 100) { + tone = "critical"; + } else if (maxPercentage && maxPercentage > 75) { + tone = "caution"; + } + + if (props.minLength && value.length < props.minLength) { + tone = "caution"; + } + return ( + + {value.length} / {props.maxLength} + + ); +} + +export function InputWithCharacterCount(props: TextInputProps): JSX.Element { + const document = useFormValue([]); + + if (!document) { + return props.renderDefault(props); + } + + const { name, title } = document as { + name?: string; + title?: string; + }; + + let defaultTitle: string | undefined = undefined; + + if (props.id === "seo.title") { + defaultTitle = title ?? name ?? undefined; + } + + props.elementProps.placeholder = defaultTitle; + + return ( + + {props.renderDefault(props)} + + + + + ); +} From 45d907e1ae00f3178ad10ac669401075e79814e0 Mon Sep 17 00:00:00 2001 From: "stilyan.tinloof" Date: Mon, 6 Jan 2025 11:57:24 +0000 Subject: [PATCH 2/2] readme update --- packages/sanity-studio/README.md | 36 +++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/sanity-studio/README.md b/packages/sanity-studio/README.md index 88edec73..23334f76 100644 --- a/packages/sanity-studio/README.md +++ b/packages/sanity-studio/README.md @@ -27,6 +27,7 @@ npm install @tinloof/sanity-studio - [`localizedItem`](#localizedItem) - [`defineIcon`](#defineIcon) - [Disable creation plugin](#disable-creation-plugin) +- [Input with characters count](#input-with-characters-count) ## Pages @@ -528,7 +529,7 @@ export default defineConfig({ ### Important notice -When using this plugin, make sure you placing it after the `stucutureTool()`. +When using this plugin, make sure you are placing it after the `stucutureTool()`. ```tsx plugins: [ @@ -540,6 +541,39 @@ plugins: [ ], ``` +## Input with characters count + +This is a custom component which shows a characters count below a string input. Requires you to specify the minimum and maximum length of the string in order to render and also show tone states. + +### Basic usage + +Add as a `input` under `components` and include the options `minLength` and `maxLength`. + +```tsx +{ + type: 'object', + name: 'seo', + fields: [ + { + type: 'string', + name: 'title', + components: { + input: InputWithCharacterCount, + }, + options: { + maxLength: 100, + minLength: 10, + }, + }, + ], +}, +``` + +### Parameters + +- `minLength`: Number, minimum length of string +- `maxLength`: Number, maximum length of string + ## Examples Check the `/examples` folder.