@madebywild/sanity-richtext-field 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,48 +3,64 @@ import { defineField } from "sanity";
3
3
  import { styled } from "styled-components";
4
4
  import { typeName } from "../../types";
5
5
 
6
- const ColorText = styled.span<{ color?: string }>`
6
+ type ColorAnnotationOptions = {
7
+ renderValue?: (value: string | undefined) => string | undefined;
8
+ };
9
+
10
+ const ColorText = styled.span<{ $color?: string }>`
7
11
  & > span {
8
12
  background-color: inherit;
9
13
  border-bottom: unset;
10
- color: ${({ color = "inherit" }) => color};
14
+ color: ${({ $color = "inherit" }) => $color};
11
15
  }
12
16
  `;
13
17
 
14
- export const textColor = defineField({
15
- type: "object",
16
- name: `${typeName}.annotation.textColor`,
17
- title: "Text Color",
18
- icon: ColorWheelIcon,
19
- fields: [defineField({ name: "color", type: "wild.color" })],
20
- components: {
21
- annotation: (props) => {
22
- // @ts-expect-error: Sanity types are not aware of the color field structure.
23
- const color = props.value?.color?.value as string;
24
- return <ColorText color={color}>{props.renderDefault(props)}</ColorText>;
18
+ function defaultResolveColor(value: string | undefined) {
19
+ return value;
20
+ }
21
+
22
+ function createTextColorAnnotation(options?: ColorAnnotationOptions) {
23
+ const renderValue = options?.renderValue ?? defaultResolveColor;
24
+
25
+ return defineField({
26
+ type: "object",
27
+ name: `${typeName}.annotation.textColor`,
28
+ title: "Text Color",
29
+ icon: ColorWheelIcon,
30
+ fields: [defineField({ name: "color", type: "wild.color" })],
31
+ components: {
32
+ annotation: (props) => {
33
+ const value = renderValue(props.value?.color);
34
+ return <ColorText $color={value}>{props.renderDefault(props)}</ColorText>;
35
+ },
25
36
  },
26
- },
27
- });
37
+ });
38
+ }
28
39
 
29
- const ColorBg = styled.span<{ color?: string }>`
40
+ const ColorBg = styled.span<{ $color?: string }>`
30
41
  & > span {
31
- background-color: ${({ color = "inherit" }) => color};
42
+ background-color: ${({ $color = "inherit" }) => $color};
32
43
  border-bottom: unset;
33
44
  color: inherit;
34
45
  }
35
46
  `;
36
47
 
37
- export const highlightColor = defineField({
38
- type: "object",
39
- name: `${typeName}.annotation.highlightColor`,
40
- title: "Highlight Color",
41
- icon: HighlightIcon,
42
- fields: [defineField({ name: "color", type: "wild.color" })],
43
- components: {
44
- annotation: (props) => {
45
- // @ts-expect-error: Sanity types are not aware of the color field structure.
46
- const color = props.value?.color?.value as string;
47
- return <ColorBg color={color}>{props.renderDefault(props)}</ColorBg>;
48
+ function createHighlightColorAnnotation(options?: ColorAnnotationOptions) {
49
+ const renderValue = options?.renderValue ?? defaultResolveColor;
50
+
51
+ return defineField({
52
+ type: "object",
53
+ name: `${typeName}.annotation.highlightColor`,
54
+ title: "Highlight Color",
55
+ icon: HighlightIcon,
56
+ fields: [defineField({ name: "color", type: "wild.color" })],
57
+ components: {
58
+ annotation: (props) => {
59
+ const value = renderValue(props.value?.color);
60
+ return <ColorBg $color={value}>{props.renderDefault(props)}</ColorBg>;
61
+ },
48
62
  },
49
- },
50
- });
63
+ });
64
+ }
65
+
66
+ export { createTextColorAnnotation, createHighlightColorAnnotation, type ColorAnnotationOptions };
@@ -1,9 +1,9 @@
1
- import { highlightColor, textColor } from "./color";
1
+ import { type ColorAnnotationOptions, createHighlightColorAnnotation, createTextColorAnnotation } from "./color";
2
2
  import { link } from "./link";
3
3
 
4
4
  /** @public */
5
- function createAnnotations() {
6
- return [link, textColor, highlightColor] as const;
5
+ function createAnnotations(options?: { textColor?: ColorAnnotationOptions; highlightColor?: ColorAnnotationOptions }) {
6
+ return [link, createTextColorAnnotation(options?.textColor), createHighlightColorAnnotation(options?.highlightColor)] as const;
7
7
  }
8
8
 
9
9
  export { createAnnotations };
@@ -2,6 +2,7 @@ import { createAnnotations } from "./annotations";
2
2
  import { createDecorators } from "./decorators";
3
3
  import { createLists } from "./lists";
4
4
  import { createSpans } from "./spans";
5
+ import { createIconSpan } from "./spans/icon";
5
6
  import { createStyles } from "./styles";
6
7
 
7
- export { createAnnotations, createDecorators, createLists, createSpans, createStyles };
8
+ export { createAnnotations, createDecorators, createLists, createSpans, createStyles, createIconSpan };
@@ -2,32 +2,47 @@ import * as React from "react";
2
2
  import { defineArrayMember, defineField } from "sanity";
3
3
  import { typeName } from "../../types";
4
4
 
5
- export const icon = defineArrayMember({
6
- name: `${typeName}.span.icon`,
7
- type: "object",
8
- title: "Icon",
9
- description: "Insert an icon.",
10
- icon: () => <>🧿</>,
11
- fields: [defineField({ name: "icon", type: "wild.icon" })],
12
- components: {
13
- // This is the inline-preview in the PortableText editor.
14
- // It gets its props from the prepare function below.
15
- preview: (props) => {
16
- return React.isValidElement(props.media) ? props.media : props.fallbackTitle;
17
- },
18
- },
19
- preview: {
20
- select: {
21
- icon: "icon",
5
+ type IconSpanOptions = {
6
+ renderValuePreview?: (value?: string) => React.ReactNode;
7
+ };
8
+
9
+ function defaultRenderIconPreview(value?: React.ReactNode) {
10
+ if (React.isValidElement(value)) return value;
11
+ return (
12
+ <span role="img" className="inline-block size-[1em] shrink-0 text-current" title={typeof value === "string" ? value : "Icon"}>
13
+ 🧿
14
+ </span>
15
+ );
16
+ }
17
+
18
+ function createIconSpan(options?: IconSpanOptions) {
19
+ const renderValuePreview = options?.renderValuePreview;
20
+
21
+ return defineArrayMember({
22
+ name: `${typeName}.span.icon`,
23
+ type: "object",
24
+ title: "Icon",
25
+ description: "Insert an icon.",
26
+ icon: () => <>🧿</>,
27
+ fields: [defineField({ name: "icon", type: "wild.icon" })],
28
+ components: {
29
+ // This is the inline-preview in the PortableText editor.
30
+ // It gets its props from the prepare function below.
31
+ preview: (props) => {
32
+ return React.isValidElement(props.media) ? props.media : props.fallbackTitle;
33
+ },
22
34
  },
23
- prepare({ icon }) {
24
- return {
25
- media: (
26
- <span role="img" className="inline-block size-[1em] shrink-0 text-current" title={icon ?? "Icon"}>
27
- 🧿
28
- </span>
29
- ),
30
- };
35
+ preview: {
36
+ select: {
37
+ icon: "icon",
38
+ },
39
+ prepare({ icon }) {
40
+ return {
41
+ media: renderValuePreview?.(icon) ?? defaultRenderIconPreview(icon),
42
+ };
43
+ },
31
44
  },
32
- },
33
- });
45
+ });
46
+ }
47
+
48
+ export { createIconSpan, type IconSpanOptions };
@@ -1,9 +1,9 @@
1
- import { icon } from "./icon";
1
+ import { createIconSpan, type IconSpanOptions } from "./icon";
2
2
  import { media } from "./media";
3
3
 
4
4
  /** @public */
5
- function createSpans() {
6
- return [icon, media] as const;
5
+ function createSpans(options?: { icon?: IconSpanOptions }) {
6
+ return [createIconSpan(options?.icon), media] as const;
7
7
  }
8
8
 
9
9
  export { createSpans };