@madebywild/sanity-color-field 0.0.8 → 0.0.10

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.
package/README.md CHANGED
@@ -1 +1,3 @@
1
1
  # @madebywild/sanity-color-field
2
+
3
+ Field to select an color from predefined sets of colors. The color set can be customized in the plugin configuration. In addition to the selected color, the field also stores the color's luminance value for easier use in conditional logic.
package/dist/index.cjs CHANGED
@@ -1,27 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), sanityUtils = require("@madebywild/sanity-utils"), ui = require("@sanity/ui");
3
+ var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), sanityUtils = require("@madebywild/sanity-utils"), ui = require("@sanity/ui"), color2k = require("color2k");
4
+ function getColorLuminance(color) {
5
+ try {
6
+ const safeVar = color.replace(/^var\((.*)\)$/, "$1"), parsed = safeVar.startsWith("--") ? getComputedStyle(document.documentElement).getPropertyValue(safeVar) : color;
7
+ return color2k.getLuminance(parsed);
8
+ } catch (e) {
9
+ console.warn("Failed to extract color luminance", e);
10
+ return;
11
+ }
12
+ }
4
13
  function ColorInput({
5
14
  config,
6
- schemaType,
7
- onChange,
8
- value
15
+ ...props
9
16
  }) {
10
- const options = schemaType.options, colorList = options?.colorList ?? config.colorList, renderOption = options?.renderOption ?? config.renderOption, renderSelected = options?.renderSelected ?? config.renderSelected;
17
+ const options = props.schemaType.options, colorList = options?.colorList ?? config.colorList, renderOption = options?.renderOption ?? config.renderOption, renderSelected = options?.renderSelected ?? config.renderSelected;
11
18
  return /* @__PURE__ */ jsxRuntime.jsx(
12
19
  sanityUtils.AsyncAutocomplete,
13
20
  {
14
- defaultValue: value,
21
+ defaultValue: props.value?.value,
15
22
  placeholder: "Select color",
16
23
  listItems: colorList,
17
- renderValue: (value2, opt) => opt?.label ?? value2,
18
- onChange: (value2) => {
19
- const next = value2 ? sanity.set(value2) : sanity.unset();
20
- return onChange(next);
24
+ renderValue: (value, opt) => opt?.label ?? value,
25
+ onChange: (value) => {
26
+ if (!value) return props.onChange(sanity.unset());
27
+ const luminance = getColorLuminance(value);
28
+ return props.onChange(sanity.set({ ...props.value, value, luminance }));
21
29
  },
22
- renderSelected: (value2) => renderSelected ? renderSelected(value2) : /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { style: { backgroundColor: value2 } }),
23
- renderOption: ({ label, value: value2 }) => renderOption ? renderOption({ label, value: value2 }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { as: "button", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", padding: 2, children: [
24
- /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { size: 1, style: { backgroundColor: value2 } }),
30
+ renderSelected: (value) => renderSelected ? renderSelected(value) : /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { style: { backgroundColor: value } }),
31
+ renderOption: ({ label, value }) => renderOption ? renderOption({ label, value }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { as: "button", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", padding: 2, children: [
32
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { size: 1, style: { backgroundColor: value } }),
25
33
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { flex: 1, padding: 2, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, children: label }) })
26
34
  ] }) })
27
35
  }
@@ -33,13 +41,23 @@ const typeName = "wild.color", wildSanityColorFieldPlugin = sanity.definePlugin(
33
41
  types: [
34
42
  sanity.defineType({
35
43
  name: typeName,
36
- type: "string",
44
+ type: "object",
37
45
  title: "Color",
38
46
  description: "Select a color.",
39
47
  icon: () => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u{1F3A8}" }),
40
48
  components: {
41
49
  input: (props) => /* @__PURE__ */ jsxRuntime.jsx(ColorInput, { config, ...props })
42
- }
50
+ },
51
+ fields: [
52
+ sanity.defineField({
53
+ name: "value",
54
+ type: "string"
55
+ }),
56
+ sanity.defineField({
57
+ name: "luminance",
58
+ type: "number"
59
+ })
60
+ ]
43
61
  })
44
62
  ]
45
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/input.tsx","../src/types.ts","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils\";\nimport { Avatar, Box, Card, Flex, Text } from \"@sanity/ui\";\nimport { type StringInputProps, set, unset } from \"sanity\";\nimport type { FieldOptions, PluginConfig } from \"./types\";\n\nfunction ColorInput({\n config,\n schemaType,\n onChange,\n value,\n}: StringInputProps & {\n config: PluginConfig;\n}) {\n const options = schemaType.options as FieldOptions | undefined;\n const colorList = options?.colorList ?? config.colorList;\n const renderOption = options?.renderOption ?? config.renderOption;\n const renderSelected = options?.renderSelected ?? config.renderSelected;\n\n return (\n <AsyncAutocomplete\n defaultValue={value}\n placeholder=\"Select color\"\n listItems={colorList}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n const next = value ? set(value) : unset();\n return onChange(next);\n }}\n renderSelected={(value) => {\n if (renderSelected) return renderSelected(value);\n return <Avatar style={{ backgroundColor: value }} />;\n }}\n renderOption={({ label, value }) => {\n if (renderOption) return renderOption({ label, value });\n return (\n <Card as=\"button\">\n <Flex align=\"center\" padding={2}>\n <Avatar size={1} style={{ backgroundColor: value }} />\n <Box flex={1} padding={2}>\n <Text size={2}>{label}</Text>\n </Box>\n </Flex>\n </Card>\n );\n }}\n />\n );\n}\n\nexport { ColorInput };\n","import type { ListItemsGetter } from \"@madebywild/sanity-utils\";\nimport type { StringDefinition, StringOptions } from \"sanity\";\n\n/** @public */\nexport type PluginConfig = {\n colorList: ListItemsGetter;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = StringOptions & {\n colorList?: ListItemsGetter;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<StringDefinition, \"type\" | \"fields\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { definePlugin, defineType } from \"sanity\";\nimport { ColorInput } from \"./input\";\nimport { type FieldOptions, type PluginConfig, typeName } from \"./types\";\n\n/** @public */\nconst wildSanityColorFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-color-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"string\",\n title: \"Color\",\n description: \"Select a color.\",\n icon: () => <>🎨</>,\n components: {\n input: (props) => <ColorInput config={config} {...props} />,\n },\n }),\n ],\n },\n };\n});\n\nexport { wildSanityColorFieldPlugin, type PluginConfig, type FieldOptions, typeName };\n"],"names":["jsx","AsyncAutocomplete","value","set","unset","Avatar","Card","Flex","Box","Text","definePlugin","defineType","Fragment"],"mappings":";;;AAKA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAEG;AACD,QAAM,UAAU,WAAW,SACrB,YAAY,SAAS,aAAa,OAAO,WACzC,eAAe,SAAS,gBAAgB,OAAO,cAC/C,iBAAiB,SAAS,kBAAkB,OAAO;AAEzD,SACEA,2BAAAA;AAAAA,IAACC,YAAAA;AAAAA,IAAA;AAAA,MACC,cAAc;AAAA,MACd,aAAY;AAAA,MACZ,WAAW;AAAA,MACX,aAAa,CAACC,QAAO,QAAQ,KAAK,SAASA;AAAAA,MAC3C,UAAU,CAACA,WAAU;AACnB,cAAM,OAAOA,SAAQC,OAAAA,IAAID,MAAK,IAAIE,OAAAA,MAAA;AAClC,eAAO,SAAS,IAAI;AAAA,MACtB;AAAA,MACA,gBAAgB,CAACF,WACX,iBAAuB,eAAeA,MAAK,IACxCF,+BAACK,GAAAA,QAAA,EAAO,OAAO,EAAE,iBAAiBH,SAAM,CAAG;AAAA,MAEpD,cAAc,CAAC,EAAE,OAAO,OAAAA,aAClB,eAAqB,aAAa,EAAE,OAAO,OAAAA,QAAO,IAEpDF,+BAACM,GAAAA,MAAA,EAAK,IAAG,UACP,0CAACC,SAAA,EAAK,OAAM,UAAS,SAAS,GAC5B,UAAA;AAAA,QAAAP,+BAACK,GAAAA,UAAO,MAAM,GAAG,OAAO,EAAE,iBAAiBH,UAAS;AAAA,QACpDF,2BAAAA,IAACQ,GAAAA,KAAA,EAAI,MAAM,GAAG,SAAS,GACrB,UAAAR,2BAAAA,IAACS,GAAAA,MAAA,EAAK,MAAM,GAAI,UAAA,MAAA,CAAM,EAAA,CACxB;AAAA,MAAA,EAAA,CACF,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAKV;AC7BO,MAAM,WAAW,cCblB,6BAA6BC,OAAAA,aAA2B,CAAC,YACtD;AAAA,EACL,MAAM;AAAA,EACN,QAAQ;AAAA,IACN,OAAO;AAAA,MACLC,kBAAW;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM,MAAMX,2BAAAA,IAAAY,WAAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,QAChB,YAAY;AAAA,UACV,OAAO,CAAC,yCAAW,YAAA,EAAW,QAAiB,GAAG,MAAA,CAAO;AAAA,QAAA;AAAA,MAC3D,CACD;AAAA,IAAA;AAAA,EACH;AAEJ,EACD;;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/input.tsx","../src/types.ts","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils\";\nimport { Avatar, Box, Card, Flex, Text } from \"@sanity/ui\";\nimport { getLuminance } from \"color2k\";\nimport { type ObjectInputProps, set, unset } from \"sanity\";\nimport type { FieldOptions, PluginConfig } from \"./types\";\n\ntype FieldValues = {\n // The selected value from the color list.\n value?: string;\n // The calculated luminance of the selected color (0 to 1).\n luminance?: number;\n};\n\nfunction getColorLuminance(color: string) {\n try {\n const safeVar = color.replace(/^var\\((.*)\\)$/, \"$1\");\n const parsed = safeVar.startsWith(\"--\") ? getComputedStyle(document.documentElement).getPropertyValue(safeVar) : color;\n return getLuminance(parsed);\n } catch (e) {\n console.warn(\"Failed to extract color luminance\", e);\n return undefined;\n }\n}\n\nfunction ColorInput({\n config,\n ...props\n}: ObjectInputProps<FieldValues> & {\n config: PluginConfig;\n}) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const colorList = options?.colorList ?? config.colorList;\n const renderOption = options?.renderOption ?? config.renderOption;\n const renderSelected = options?.renderSelected ?? config.renderSelected;\n\n return (\n <AsyncAutocomplete\n defaultValue={props.value?.value}\n placeholder=\"Select color\"\n listItems={colorList}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n if (!value) return props.onChange(unset());\n const luminance = getColorLuminance(value);\n return props.onChange(set({ ...props.value, value, luminance }));\n }}\n renderSelected={(value) => {\n if (renderSelected) return renderSelected(value);\n return <Avatar style={{ backgroundColor: value }} />;\n }}\n renderOption={({ label, value }) => {\n if (renderOption) return renderOption({ label, value });\n return (\n <Card as=\"button\">\n <Flex align=\"center\" padding={2}>\n <Avatar size={1} style={{ backgroundColor: value }} />\n <Box flex={1} padding={2}>\n <Text size={2}>{label}</Text>\n </Box>\n </Flex>\n </Card>\n );\n }}\n />\n );\n}\n\nexport { ColorInput };\n","import type { ListItems } from \"@madebywild/sanity-utils\";\nimport type { ObjectDefinition, StringOptions } from \"sanity\";\n\n/** @public */\nexport type PluginConfig = {\n colorList: ListItems;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = StringOptions & {\n colorList?: ListItems;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<ObjectDefinition, \"type\" | \"fields\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { defineField, definePlugin, defineType } from \"sanity\";\nimport { ColorInput } from \"./input\";\nimport { type FieldOptions, type PluginConfig, typeName } from \"./types\";\n\n/** @public */\nconst wildSanityColorFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-color-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"object\",\n title: \"Color\",\n description: \"Select a color.\",\n icon: () => <>🎨</>,\n components: {\n input: (props) => <ColorInput config={config} {...props} />,\n },\n fields: [\n defineField({\n name: \"value\",\n type: \"string\",\n }),\n defineField({\n name: \"luminance\",\n type: \"number\",\n }),\n ],\n }),\n ],\n },\n };\n});\n\nexport { wildSanityColorFieldPlugin, type PluginConfig, type FieldOptions, typeName };\n"],"names":["getLuminance","jsx","AsyncAutocomplete","unset","set","Avatar","Card","Flex","Box","Text","definePlugin","defineType","Fragment","defineField"],"mappings":";;;AAaA,SAAS,kBAAkB,OAAe;AACxC,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,iBAAiB,IAAI,GAC7C,SAAS,QAAQ,WAAW,IAAI,IAAI,iBAAiB,SAAS,eAAe,EAAE,iBAAiB,OAAO,IAAI;AACjH,WAAOA,QAAAA,aAAa,MAAM;AAAA,EAC5B,SAAS,GAAG;AACV,YAAQ,KAAK,qCAAqC,CAAC;AACnD;AAAA,EACF;AACF;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA,GAAG;AACL,GAEG;AACD,QAAM,UAAU,MAAM,WAAW,SAC3B,YAAY,SAAS,aAAa,OAAO,WACzC,eAAe,SAAS,gBAAgB,OAAO,cAC/C,iBAAiB,SAAS,kBAAkB,OAAO;AAEzD,SACEC,2BAAAA;AAAAA,IAACC,YAAAA;AAAAA,IAAA;AAAA,MACC,cAAc,MAAM,OAAO;AAAA,MAC3B,aAAY;AAAA,MACZ,WAAW;AAAA,MACX,aAAa,CAAC,OAAO,QAAQ,KAAK,SAAS;AAAA,MAC3C,UAAU,CAAC,UAAU;AACnB,YAAI,CAAC,MAAO,QAAO,MAAM,SAASC,OAAAA,OAAO;AACzC,cAAM,YAAY,kBAAkB,KAAK;AACzC,eAAO,MAAM,SAASC,OAAAA,IAAI,EAAE,GAAG,MAAM,OAAO,OAAO,UAAA,CAAW,CAAC;AAAA,MACjE;AAAA,MACA,gBAAgB,CAAC,UACX,iBAAuB,eAAe,KAAK,IACxCH,+BAACI,GAAAA,QAAA,EAAO,OAAO,EAAE,iBAAiB,QAAM,CAAG;AAAA,MAEpD,cAAc,CAAC,EAAE,OAAO,YAClB,eAAqB,aAAa,EAAE,OAAO,MAAA,CAAO,IAEpDJ,2BAAAA,IAACK,WAAK,IAAG,UACP,0CAACC,GAAAA,MAAA,EAAK,OAAM,UAAS,SAAS,GAC5B,UAAA;AAAA,QAAAN,+BAACI,GAAAA,UAAO,MAAM,GAAG,OAAO,EAAE,iBAAiB,SAAS;AAAA,QACpDJ,2BAAAA,IAACO,GAAAA,KAAA,EAAI,MAAM,GAAG,SAAS,GACrB,UAAAP,2BAAAA,IAACQ,GAAAA,MAAA,EAAK,MAAM,GAAI,UAAA,MAAA,CAAM,EAAA,CACxB;AAAA,MAAA,EAAA,CACF,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAKV;AC/CO,MAAM,WAAW,cCblB,6BAA6BC,OAAAA,aAA2B,CAAC,YACtD;AAAA,EACL,MAAM;AAAA,EACN,QAAQ;AAAA,IACN,OAAO;AAAA,MACLC,kBAAW;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM,MAAMV,2BAAAA,IAAAW,WAAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,QAChB,YAAY;AAAA,UACV,OAAO,CAAC,yCAAW,YAAA,EAAW,QAAiB,GAAG,MAAA,CAAO;AAAA,QAAA;AAAA,QAE3D,QAAQ;AAAA,UACNC,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,UAAA,CACP;AAAA,UACDA,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,UAAA,CACP;AAAA,QAAA;AAAA,MACH,CACD;AAAA,IAAA;AAAA,EACH;AAEJ,EACD;;;"}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as sanity0 from "sanity";
2
- import { StringDefinition, StringOptions } from "sanity";
3
- import { ListItemsGetter } from "@madebywild/sanity-utils";
2
+ import { ObjectDefinition, StringOptions } from "sanity";
3
+ import { ListItems } from "@madebywild/sanity-utils";
4
4
  /** @public */
5
5
  type PluginConfig = {
6
- colorList: ListItemsGetter;
6
+ colorList: ListItems;
7
7
  renderSelected?: (value: string) => React.JSX.Element;
8
8
  renderOption?: (item: {
9
9
  label?: string;
@@ -12,7 +12,7 @@ type PluginConfig = {
12
12
  };
13
13
  /** @public */
14
14
  type FieldOptions = StringOptions & {
15
- colorList?: ListItemsGetter;
15
+ colorList?: ListItems;
16
16
  renderSelected?: (value: string) => React.JSX.Element;
17
17
  renderOption?: (item: {
18
18
  label?: string;
@@ -23,7 +23,7 @@ type FieldOptions = StringOptions & {
23
23
  declare const typeName: "wild.color";
24
24
  declare module "sanity" {
25
25
  interface IntrinsicDefinitions {
26
- [typeName]: Omit<StringDefinition, "type" | "fields"> & {
26
+ [typeName]: Omit<ObjectDefinition, "type" | "fields"> & {
27
27
  type: typeof typeName;
28
28
  options?: FieldOptions;
29
29
  };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as sanity0 from "sanity";
2
- import { StringDefinition, StringOptions } from "sanity";
3
- import { ListItemsGetter } from "@madebywild/sanity-utils";
2
+ import { ObjectDefinition, StringOptions } from "sanity";
3
+ import { ListItems } from "@madebywild/sanity-utils";
4
4
  /** @public */
5
5
  type PluginConfig = {
6
- colorList: ListItemsGetter;
6
+ colorList: ListItems;
7
7
  renderSelected?: (value: string) => React.JSX.Element;
8
8
  renderOption?: (item: {
9
9
  label?: string;
@@ -12,7 +12,7 @@ type PluginConfig = {
12
12
  };
13
13
  /** @public */
14
14
  type FieldOptions = StringOptions & {
15
- colorList?: ListItemsGetter;
15
+ colorList?: ListItems;
16
16
  renderSelected?: (value: string) => React.JSX.Element;
17
17
  renderOption?: (item: {
18
18
  label?: string;
@@ -23,7 +23,7 @@ type FieldOptions = StringOptions & {
23
23
  declare const typeName: "wild.color";
24
24
  declare module "sanity" {
25
25
  interface IntrinsicDefinitions {
26
- [typeName]: Omit<StringDefinition, "type" | "fields"> & {
26
+ [typeName]: Omit<ObjectDefinition, "type" | "fields"> & {
27
27
  type: typeof typeName;
28
28
  options?: FieldOptions;
29
29
  };
package/dist/index.js CHANGED
@@ -1,28 +1,37 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { set, unset, definePlugin, defineType } from "sanity";
2
+ import { unset, set, definePlugin, defineType, defineField } from "sanity";
3
3
  import { AsyncAutocomplete } from "@madebywild/sanity-utils";
4
4
  import { Card, Flex, Avatar, Box, Text } from "@sanity/ui";
5
+ import { getLuminance } from "color2k";
6
+ function getColorLuminance(color) {
7
+ try {
8
+ const safeVar = color.replace(/^var\((.*)\)$/, "$1"), parsed = safeVar.startsWith("--") ? getComputedStyle(document.documentElement).getPropertyValue(safeVar) : color;
9
+ return getLuminance(parsed);
10
+ } catch (e) {
11
+ console.warn("Failed to extract color luminance", e);
12
+ return;
13
+ }
14
+ }
5
15
  function ColorInput({
6
16
  config,
7
- schemaType,
8
- onChange,
9
- value
17
+ ...props
10
18
  }) {
11
- const options = schemaType.options, colorList = options?.colorList ?? config.colorList, renderOption = options?.renderOption ?? config.renderOption, renderSelected = options?.renderSelected ?? config.renderSelected;
19
+ const options = props.schemaType.options, colorList = options?.colorList ?? config.colorList, renderOption = options?.renderOption ?? config.renderOption, renderSelected = options?.renderSelected ?? config.renderSelected;
12
20
  return /* @__PURE__ */ jsx(
13
21
  AsyncAutocomplete,
14
22
  {
15
- defaultValue: value,
23
+ defaultValue: props.value?.value,
16
24
  placeholder: "Select color",
17
25
  listItems: colorList,
18
- renderValue: (value2, opt) => opt?.label ?? value2,
19
- onChange: (value2) => {
20
- const next = value2 ? set(value2) : unset();
21
- return onChange(next);
26
+ renderValue: (value, opt) => opt?.label ?? value,
27
+ onChange: (value) => {
28
+ if (!value) return props.onChange(unset());
29
+ const luminance = getColorLuminance(value);
30
+ return props.onChange(set({ ...props.value, value, luminance }));
22
31
  },
23
- renderSelected: (value2) => renderSelected ? renderSelected(value2) : /* @__PURE__ */ jsx(Avatar, { style: { backgroundColor: value2 } }),
24
- renderOption: ({ label, value: value2 }) => renderOption ? renderOption({ label, value: value2 }) : /* @__PURE__ */ jsx(Card, { as: "button", children: /* @__PURE__ */ jsxs(Flex, { align: "center", padding: 2, children: [
25
- /* @__PURE__ */ jsx(Avatar, { size: 1, style: { backgroundColor: value2 } }),
32
+ renderSelected: (value) => renderSelected ? renderSelected(value) : /* @__PURE__ */ jsx(Avatar, { style: { backgroundColor: value } }),
33
+ renderOption: ({ label, value }) => renderOption ? renderOption({ label, value }) : /* @__PURE__ */ jsx(Card, { as: "button", children: /* @__PURE__ */ jsxs(Flex, { align: "center", padding: 2, children: [
34
+ /* @__PURE__ */ jsx(Avatar, { size: 1, style: { backgroundColor: value } }),
26
35
  /* @__PURE__ */ jsx(Box, { flex: 1, padding: 2, children: /* @__PURE__ */ jsx(Text, { size: 2, children: label }) })
27
36
  ] }) })
28
37
  }
@@ -34,13 +43,23 @@ const typeName = "wild.color", wildSanityColorFieldPlugin = definePlugin((config
34
43
  types: [
35
44
  defineType({
36
45
  name: typeName,
37
- type: "string",
46
+ type: "object",
38
47
  title: "Color",
39
48
  description: "Select a color.",
40
49
  icon: () => /* @__PURE__ */ jsx(Fragment, { children: "\u{1F3A8}" }),
41
50
  components: {
42
51
  input: (props) => /* @__PURE__ */ jsx(ColorInput, { config, ...props })
43
- }
52
+ },
53
+ fields: [
54
+ defineField({
55
+ name: "value",
56
+ type: "string"
57
+ }),
58
+ defineField({
59
+ name: "luminance",
60
+ type: "number"
61
+ })
62
+ ]
44
63
  })
45
64
  ]
46
65
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/input.tsx","../src/types.ts","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils\";\nimport { Avatar, Box, Card, Flex, Text } from \"@sanity/ui\";\nimport { type StringInputProps, set, unset } from \"sanity\";\nimport type { FieldOptions, PluginConfig } from \"./types\";\n\nfunction ColorInput({\n config,\n schemaType,\n onChange,\n value,\n}: StringInputProps & {\n config: PluginConfig;\n}) {\n const options = schemaType.options as FieldOptions | undefined;\n const colorList = options?.colorList ?? config.colorList;\n const renderOption = options?.renderOption ?? config.renderOption;\n const renderSelected = options?.renderSelected ?? config.renderSelected;\n\n return (\n <AsyncAutocomplete\n defaultValue={value}\n placeholder=\"Select color\"\n listItems={colorList}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n const next = value ? set(value) : unset();\n return onChange(next);\n }}\n renderSelected={(value) => {\n if (renderSelected) return renderSelected(value);\n return <Avatar style={{ backgroundColor: value }} />;\n }}\n renderOption={({ label, value }) => {\n if (renderOption) return renderOption({ label, value });\n return (\n <Card as=\"button\">\n <Flex align=\"center\" padding={2}>\n <Avatar size={1} style={{ backgroundColor: value }} />\n <Box flex={1} padding={2}>\n <Text size={2}>{label}</Text>\n </Box>\n </Flex>\n </Card>\n );\n }}\n />\n );\n}\n\nexport { ColorInput };\n","import type { ListItemsGetter } from \"@madebywild/sanity-utils\";\nimport type { StringDefinition, StringOptions } from \"sanity\";\n\n/** @public */\nexport type PluginConfig = {\n colorList: ListItemsGetter;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = StringOptions & {\n colorList?: ListItemsGetter;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<StringDefinition, \"type\" | \"fields\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { definePlugin, defineType } from \"sanity\";\nimport { ColorInput } from \"./input\";\nimport { type FieldOptions, type PluginConfig, typeName } from \"./types\";\n\n/** @public */\nconst wildSanityColorFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-color-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"string\",\n title: \"Color\",\n description: \"Select a color.\",\n icon: () => <>🎨</>,\n components: {\n input: (props) => <ColorInput config={config} {...props} />,\n },\n }),\n ],\n },\n };\n});\n\nexport { wildSanityColorFieldPlugin, type PluginConfig, type FieldOptions, typeName };\n"],"names":["value"],"mappings":";;;;AAKA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAEG;AACD,QAAM,UAAU,WAAW,SACrB,YAAY,SAAS,aAAa,OAAO,WACzC,eAAe,SAAS,gBAAgB,OAAO,cAC/C,iBAAiB,SAAS,kBAAkB,OAAO;AAEzD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAc;AAAA,MACd,aAAY;AAAA,MACZ,WAAW;AAAA,MACX,aAAa,CAACA,QAAO,QAAQ,KAAK,SAASA;AAAAA,MAC3C,UAAU,CAACA,WAAU;AACnB,cAAM,OAAOA,SAAQ,IAAIA,MAAK,IAAI,MAAA;AAClC,eAAO,SAAS,IAAI;AAAA,MACtB;AAAA,MACA,gBAAgB,CAACA,WACX,iBAAuB,eAAeA,MAAK,IACxC,oBAAC,QAAA,EAAO,OAAO,EAAE,iBAAiBA,SAAM,CAAG;AAAA,MAEpD,cAAc,CAAC,EAAE,OAAO,OAAAA,aAClB,eAAqB,aAAa,EAAE,OAAO,OAAAA,QAAO,IAEpD,oBAAC,MAAA,EAAK,IAAG,UACP,+BAAC,MAAA,EAAK,OAAM,UAAS,SAAS,GAC5B,UAAA;AAAA,QAAA,oBAAC,UAAO,MAAM,GAAG,OAAO,EAAE,iBAAiBA,UAAS;AAAA,QACpD,oBAAC,KAAA,EAAI,MAAM,GAAG,SAAS,GACrB,UAAA,oBAAC,MAAA,EAAK,MAAM,GAAI,UAAA,MAAA,CAAM,EAAA,CACxB;AAAA,MAAA,EAAA,CACF,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAKV;AC7BO,MAAM,WAAW,cCblB,6BAA6B,aAA2B,CAAC,YACtD;AAAA,EACL,MAAM;AAAA,EACN,QAAQ;AAAA,IACN,OAAO;AAAA,MACL,WAAW;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,QAChB,YAAY;AAAA,UACV,OAAO,CAAC,8BAAW,YAAA,EAAW,QAAiB,GAAG,MAAA,CAAO;AAAA,QAAA;AAAA,MAC3D,CACD;AAAA,IAAA;AAAA,EACH;AAEJ,EACD;"}
1
+ {"version":3,"file":"index.js","sources":["../src/input.tsx","../src/types.ts","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils\";\nimport { Avatar, Box, Card, Flex, Text } from \"@sanity/ui\";\nimport { getLuminance } from \"color2k\";\nimport { type ObjectInputProps, set, unset } from \"sanity\";\nimport type { FieldOptions, PluginConfig } from \"./types\";\n\ntype FieldValues = {\n // The selected value from the color list.\n value?: string;\n // The calculated luminance of the selected color (0 to 1).\n luminance?: number;\n};\n\nfunction getColorLuminance(color: string) {\n try {\n const safeVar = color.replace(/^var\\((.*)\\)$/, \"$1\");\n const parsed = safeVar.startsWith(\"--\") ? getComputedStyle(document.documentElement).getPropertyValue(safeVar) : color;\n return getLuminance(parsed);\n } catch (e) {\n console.warn(\"Failed to extract color luminance\", e);\n return undefined;\n }\n}\n\nfunction ColorInput({\n config,\n ...props\n}: ObjectInputProps<FieldValues> & {\n config: PluginConfig;\n}) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const colorList = options?.colorList ?? config.colorList;\n const renderOption = options?.renderOption ?? config.renderOption;\n const renderSelected = options?.renderSelected ?? config.renderSelected;\n\n return (\n <AsyncAutocomplete\n defaultValue={props.value?.value}\n placeholder=\"Select color\"\n listItems={colorList}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n if (!value) return props.onChange(unset());\n const luminance = getColorLuminance(value);\n return props.onChange(set({ ...props.value, value, luminance }));\n }}\n renderSelected={(value) => {\n if (renderSelected) return renderSelected(value);\n return <Avatar style={{ backgroundColor: value }} />;\n }}\n renderOption={({ label, value }) => {\n if (renderOption) return renderOption({ label, value });\n return (\n <Card as=\"button\">\n <Flex align=\"center\" padding={2}>\n <Avatar size={1} style={{ backgroundColor: value }} />\n <Box flex={1} padding={2}>\n <Text size={2}>{label}</Text>\n </Box>\n </Flex>\n </Card>\n );\n }}\n />\n );\n}\n\nexport { ColorInput };\n","import type { ListItems } from \"@madebywild/sanity-utils\";\nimport type { ObjectDefinition, StringOptions } from \"sanity\";\n\n/** @public */\nexport type PluginConfig = {\n colorList: ListItems;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = StringOptions & {\n colorList?: ListItems;\n renderSelected?: (value: string) => React.JSX.Element;\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<ObjectDefinition, \"type\" | \"fields\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { defineField, definePlugin, defineType } from \"sanity\";\nimport { ColorInput } from \"./input\";\nimport { type FieldOptions, type PluginConfig, typeName } from \"./types\";\n\n/** @public */\nconst wildSanityColorFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-color-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"object\",\n title: \"Color\",\n description: \"Select a color.\",\n icon: () => <>🎨</>,\n components: {\n input: (props) => <ColorInput config={config} {...props} />,\n },\n fields: [\n defineField({\n name: \"value\",\n type: \"string\",\n }),\n defineField({\n name: \"luminance\",\n type: \"number\",\n }),\n ],\n }),\n ],\n },\n };\n});\n\nexport { wildSanityColorFieldPlugin, type PluginConfig, type FieldOptions, typeName };\n"],"names":[],"mappings":";;;;;AAaA,SAAS,kBAAkB,OAAe;AACxC,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,iBAAiB,IAAI,GAC7C,SAAS,QAAQ,WAAW,IAAI,IAAI,iBAAiB,SAAS,eAAe,EAAE,iBAAiB,OAAO,IAAI;AACjH,WAAO,aAAa,MAAM;AAAA,EAC5B,SAAS,GAAG;AACV,YAAQ,KAAK,qCAAqC,CAAC;AACnD;AAAA,EACF;AACF;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA,GAAG;AACL,GAEG;AACD,QAAM,UAAU,MAAM,WAAW,SAC3B,YAAY,SAAS,aAAa,OAAO,WACzC,eAAe,SAAS,gBAAgB,OAAO,cAC/C,iBAAiB,SAAS,kBAAkB,OAAO;AAEzD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAc,MAAM,OAAO;AAAA,MAC3B,aAAY;AAAA,MACZ,WAAW;AAAA,MACX,aAAa,CAAC,OAAO,QAAQ,KAAK,SAAS;AAAA,MAC3C,UAAU,CAAC,UAAU;AACnB,YAAI,CAAC,MAAO,QAAO,MAAM,SAAS,OAAO;AACzC,cAAM,YAAY,kBAAkB,KAAK;AACzC,eAAO,MAAM,SAAS,IAAI,EAAE,GAAG,MAAM,OAAO,OAAO,UAAA,CAAW,CAAC;AAAA,MACjE;AAAA,MACA,gBAAgB,CAAC,UACX,iBAAuB,eAAe,KAAK,IACxC,oBAAC,QAAA,EAAO,OAAO,EAAE,iBAAiB,QAAM,CAAG;AAAA,MAEpD,cAAc,CAAC,EAAE,OAAO,YAClB,eAAqB,aAAa,EAAE,OAAO,MAAA,CAAO,IAEpD,oBAAC,QAAK,IAAG,UACP,+BAAC,MAAA,EAAK,OAAM,UAAS,SAAS,GAC5B,UAAA;AAAA,QAAA,oBAAC,UAAO,MAAM,GAAG,OAAO,EAAE,iBAAiB,SAAS;AAAA,QACpD,oBAAC,KAAA,EAAI,MAAM,GAAG,SAAS,GACrB,UAAA,oBAAC,MAAA,EAAK,MAAM,GAAI,UAAA,MAAA,CAAM,EAAA,CACxB;AAAA,MAAA,EAAA,CACF,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAKV;AC/CO,MAAM,WAAW,cCblB,6BAA6B,aAA2B,CAAC,YACtD;AAAA,EACL,MAAM;AAAA,EACN,QAAQ;AAAA,IACN,OAAO;AAAA,MACL,WAAW;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,QAChB,YAAY;AAAA,UACV,OAAO,CAAC,8BAAW,YAAA,EAAW,QAAiB,GAAG,MAAA,CAAO;AAAA,QAAA;AAAA,QAE3D,QAAQ;AAAA,UACN,YAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,UAAA,CACP;AAAA,UACD,YAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,UAAA,CACP;AAAA,QAAA;AAAA,MACH,CACD;AAAA,IAAA;AAAA,EACH;AAEJ,EACD;"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "browserslist": "extends @sanity/browserslist-config",
7
- "version": "0.0.8",
7
+ "version": "0.0.10",
8
8
  "exports": {
9
9
  "./package.json": "./package.json",
10
10
  ".": {
@@ -27,22 +27,22 @@
27
27
  "typecheck": "tsc --noEmit"
28
28
  },
29
29
  "dependencies": {
30
- "@madebywild/sanity-utils": "*"
30
+ "@madebywild/sanity-utils": "*",
31
+ "color2k": "2.0.3"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "@sanity/ui": "^3.1.11",
34
- "sanity": "^4.17",
35
35
  "react": "^19",
36
- "react-dom": "^19"
36
+ "react-dom": "^19",
37
+ "sanity": "^4.17"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@sanity/pkg-utils": "^9.1.4",
40
- "@sanity/plugin-kit": "^4.0.20",
41
- "sanity": "^4.17",
41
+ "@types/react": "^19",
42
+ "@types/react-dom": "^19",
42
43
  "react": "^19",
43
44
  "react-dom": "^19",
44
- "typescript": "^5",
45
- "@types/react": "^19",
46
- "@types/react-dom": "^19"
45
+ "sanity": "^4.17",
46
+ "typescript": "^5"
47
47
  }
48
48
  }
package/src/index.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { definePlugin, defineType } from "sanity";
1
+ import { defineField, definePlugin, defineType } from "sanity";
2
2
  import { ColorInput } from "./input";
3
3
  import { type FieldOptions, type PluginConfig, typeName } from "./types";
4
4
 
@@ -10,13 +10,23 @@ const wildSanityColorFieldPlugin = definePlugin<PluginConfig>((config) => {
10
10
  types: [
11
11
  defineType({
12
12
  name: typeName,
13
- type: "string",
13
+ type: "object",
14
14
  title: "Color",
15
15
  description: "Select a color.",
16
16
  icon: () => <>🎨</>,
17
17
  components: {
18
18
  input: (props) => <ColorInput config={config} {...props} />,
19
19
  },
20
+ fields: [
21
+ defineField({
22
+ name: "value",
23
+ type: "string",
24
+ }),
25
+ defineField({
26
+ name: "luminance",
27
+ type: "number",
28
+ }),
29
+ ],
20
30
  }),
21
31
  ],
22
32
  },
package/src/input.tsx CHANGED
@@ -1,30 +1,48 @@
1
1
  import { AsyncAutocomplete } from "@madebywild/sanity-utils";
2
2
  import { Avatar, Box, Card, Flex, Text } from "@sanity/ui";
3
- import { type StringInputProps, set, unset } from "sanity";
3
+ import { getLuminance } from "color2k";
4
+ import { type ObjectInputProps, set, unset } from "sanity";
4
5
  import type { FieldOptions, PluginConfig } from "./types";
5
6
 
7
+ type FieldValues = {
8
+ // The selected value from the color list.
9
+ value?: string;
10
+ // The calculated luminance of the selected color (0 to 1).
11
+ luminance?: number;
12
+ };
13
+
14
+ function getColorLuminance(color: string) {
15
+ try {
16
+ const safeVar = color.replace(/^var\((.*)\)$/, "$1");
17
+ const parsed = safeVar.startsWith("--") ? getComputedStyle(document.documentElement).getPropertyValue(safeVar) : color;
18
+ return getLuminance(parsed);
19
+ } catch (e) {
20
+ console.warn("Failed to extract color luminance", e);
21
+ return undefined;
22
+ }
23
+ }
24
+
6
25
  function ColorInput({
7
26
  config,
8
- schemaType,
9
- onChange,
10
- value,
11
- }: StringInputProps & {
27
+ ...props
28
+ }: ObjectInputProps<FieldValues> & {
12
29
  config: PluginConfig;
13
30
  }) {
14
- const options = schemaType.options as FieldOptions | undefined;
31
+ const options = props.schemaType.options as FieldOptions | undefined;
15
32
  const colorList = options?.colorList ?? config.colorList;
16
33
  const renderOption = options?.renderOption ?? config.renderOption;
17
34
  const renderSelected = options?.renderSelected ?? config.renderSelected;
18
35
 
19
36
  return (
20
37
  <AsyncAutocomplete
21
- defaultValue={value}
38
+ defaultValue={props.value?.value}
22
39
  placeholder="Select color"
23
40
  listItems={colorList}
24
41
  renderValue={(value, opt) => opt?.label ?? value}
25
42
  onChange={(value) => {
26
- const next = value ? set(value) : unset();
27
- return onChange(next);
43
+ if (!value) return props.onChange(unset());
44
+ const luminance = getColorLuminance(value);
45
+ return props.onChange(set({ ...props.value, value, luminance }));
28
46
  }}
29
47
  renderSelected={(value) => {
30
48
  if (renderSelected) return renderSelected(value);
package/src/types.ts CHANGED
@@ -1,16 +1,16 @@
1
- import type { ListItemsGetter } from "@madebywild/sanity-utils";
2
- import type { StringDefinition, StringOptions } from "sanity";
1
+ import type { ListItems } from "@madebywild/sanity-utils";
2
+ import type { ObjectDefinition, StringOptions } from "sanity";
3
3
 
4
4
  /** @public */
5
5
  export type PluginConfig = {
6
- colorList: ListItemsGetter;
6
+ colorList: ListItems;
7
7
  renderSelected?: (value: string) => React.JSX.Element;
8
8
  renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;
9
9
  };
10
10
 
11
11
  /** @public */
12
12
  export type FieldOptions = StringOptions & {
13
- colorList?: ListItemsGetter;
13
+ colorList?: ListItems;
14
14
  renderSelected?: (value: string) => React.JSX.Element;
15
15
  renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;
16
16
  };
@@ -22,7 +22,7 @@ export const typeName = "wild.color" as const;
22
22
  // so that type checking works correctly when using this field type.
23
23
  declare module "sanity" {
24
24
  export interface IntrinsicDefinitions {
25
- [typeName]: Omit<StringDefinition, "type" | "fields"> & {
25
+ [typeName]: Omit<ObjectDefinition, "type" | "fields"> & {
26
26
  type: typeof typeName;
27
27
  options?: FieldOptions;
28
28
  };