Skip to content

Commit

Permalink
Merge pull request #119 from tinloof/tin-2666-characters-count-component
Browse files Browse the repository at this point in the history
Input with characters count component
  • Loading branch information
stilyan-tinloof authored Jan 6, 2025
2 parents 741983a + 45d907e commit fd5b9c1
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/six-lies-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tinloof/sanity-studio": minor
---

Input with characters count component
36 changes: 35 additions & 1 deletion packages/sanity-studio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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: [
Expand All @@ -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.
Expand Down
1 change: 1 addition & 0 deletions packages/sanity-studio/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { IconSelectComponent } from "./IconSelectComponent";
export { InputWithCharacterCount } from "./input-with-characters-count";
export { PathnameFieldComponent } from "./PathnameFieldComponent";
Original file line number Diff line number Diff line change
@@ -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 (
<Badge tone={tone}>
{value.length} / {props.maxLength}
</Badge>
);
}

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 (
<Stack space={2}>
{props.renderDefault(props)}
<Flex justify="flex-end">
<CharacterCount
value={props.value}
{...((props.schemaType.options || {}) as CountedTextOptions)}
/>
</Flex>
</Stack>
);
}

0 comments on commit fd5b9c1

Please sign in to comment.