@madebywild/sanity-color-field 0.3.18 → 1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Made by Wild
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1 +1,43 @@
1
+ > [!IMPORTANT]
2
+ > This package is primarily intended for internal use.
3
+
1
4
  # @madebywild/sanity-color-field
5
+
6
+ Sanity field type for curated color selection with optional filtering.
7
+
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @madebywild/sanity-color-field
13
+ ```
14
+
15
+ ## Configure Plugin
16
+
17
+ ```ts
18
+ import { wildSanityColorFieldPlugin } from "@madebywild/sanity-color-field";
19
+
20
+ export default defineConfig({
21
+ plugins: [
22
+ wildSanityColorFieldPlugin({
23
+ colorList: [
24
+ { label: "Brand Red", value: "#d92d20" },
25
+ { label: "Brand Blue", value: "#175cd3" },
26
+ ],
27
+ }),
28
+ ],
29
+ });
30
+ ```
31
+
32
+ ## Use in Schema
33
+
34
+ ```ts
35
+ defineField({
36
+ name: "themeColor",
37
+ type: "wild.color",
38
+ options: {
39
+ // Per-field subset of the plugin's global `colorList`.
40
+ whitelist: ["#d92d20", "#175cd3"],
41
+ },
42
+ });
43
+ ```
package/dist/index.cjs CHANGED
@@ -1,32 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), compilerRuntime = require("react/compiler-runtime"), asyncAutocomplete = require("@madebywild/sanity-utils/async-autocomplete"), ui = require("@sanity/ui"), color2k = require("color2k");
4
- function maybeGetColorValue(source) {
5
- const varMatch = source.match(/^var\((.+)\)$/), cssVarName = (varMatch?.[1] ? varMatch[1].trim() : source.trim()).split(",")[0]?.trim();
6
- return (cssVarName?.startsWith("--") ? getComputedStyle(document.documentElement).getPropertyValue(cssVarName).trim() : null) || source;
7
- }
8
- function safeGetLuminance(maybeColor) {
9
- try {
10
- return color2k.getLuminance(maybeColor);
11
- } catch (e) {
12
- console.warn("Failed to get luminance", e);
13
- return;
14
- }
15
- }
3
+ var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), compilerRuntime = require("react/compiler-runtime"), asyncAutocomplete = require("@madebywild/sanity-utils/async-autocomplete"), ui = require("@sanity/ui");
16
4
  function defaultRenderSelected(value) {
17
- return /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { style: {
5
+ return typeof value == "string" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { style: {
18
6
  backgroundColor: value
19
- } });
7
+ } }) : value;
20
8
  }
21
9
  function defaultRenderOption({
22
10
  label,
23
11
  value
24
12
  }) {
25
13
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { as: "button", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", padding: 2, children: [
26
- /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { size: 1, style: {
14
+ typeof value == "string" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Avatar, { size: 1, style: {
27
15
  backgroundColor: value
28
- } }),
29
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { flex: 1, padding: 2, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, children: label }) })
16
+ } }) : value,
17
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { flex: 1, padding: 2, children: typeof label == "string" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, children: label }) : label ?? value })
30
18
  ] }) });
31
19
  }
32
20
  function ColorInput(t0) {
@@ -36,33 +24,27 @@ function ColorInput(t0) {
36
24
  pluginConfig,
37
25
  ...props
38
26
  } = t0, $[0] = t0, $[1] = pluginConfig, $[2] = props) : (pluginConfig = $[1], props = $[2]);
39
- const options = props.schemaType.options, colorList = pluginConfig.colorList, renderOption = pluginConfig.renderOption ?? defaultRenderOption, renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected, whitelist = options?.whitelist, blacklist = options?.blacklist, t1 = props.value?.value;
27
+ const options = props.schemaType.options, colorList = pluginConfig.colorList, renderOption = pluginConfig.renderOption ?? defaultRenderOption, renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected, whitelist = options?.whitelist, blacklist = options?.blacklist;
28
+ let t1;
29
+ $[3] !== props ? (t1 = (value_0) => {
30
+ const next = value_0 ? sanity.set(value_0) : sanity.unset();
31
+ return props.onChange(next);
32
+ }, $[3] = props, $[4] = t1) : t1 = $[4];
40
33
  let t2;
41
- $[3] !== props ? (t2 = (value_0) => {
42
- if (!value_0)
43
- return props.onChange(sanity.unset());
44
- const color = maybeGetColorValue(value_0), luminance = color ? safeGetLuminance(color) : void 0;
45
- return props.onChange(sanity.set({
46
- ...props.value,
47
- value: value_0,
48
- luminance
49
- }));
50
- }, $[3] = props, $[4] = t2) : t2 = $[4];
34
+ $[5] !== renderSelected ? (t2 = (value_1) => renderSelected(value_1), $[5] = renderSelected, $[6] = t2) : t2 = $[6];
51
35
  let t3;
52
- $[5] !== renderSelected ? (t3 = (value_1) => renderSelected(value_1), $[5] = renderSelected, $[6] = t3) : t3 = $[6];
53
- let t4;
54
- $[7] !== renderOption ? (t4 = (t52) => {
36
+ $[7] !== renderOption ? (t3 = (t42) => {
55
37
  const {
56
38
  label,
57
39
  value: value_2
58
- } = t52;
40
+ } = t42;
59
41
  return renderOption({
60
42
  label,
61
43
  value: value_2
62
44
  });
63
- }, $[7] = renderOption, $[8] = t4) : t4 = $[8];
64
- let t5;
65
- return $[9] !== blacklist || $[10] !== colorList || $[11] !== t1 || $[12] !== t2 || $[13] !== t3 || $[14] !== t4 || $[15] !== whitelist ? (t5 = /* @__PURE__ */ jsxRuntime.jsx(asyncAutocomplete.AsyncAutocomplete, { placeholder: "Select color", noOptionsPlaceholder: "No colors found", listItems: colorList, whitelist, blacklist, value: t1, renderValue: _temp, onChange: t2, renderSelected: t3, renderOption: t4 }), $[9] = blacklist, $[10] = colorList, $[11] = t1, $[12] = t2, $[13] = t3, $[14] = t4, $[15] = whitelist, $[16] = t5) : t5 = $[16], t5;
45
+ }, $[7] = renderOption, $[8] = t3) : t3 = $[8];
46
+ let t4;
47
+ return $[9] !== blacklist || $[10] !== colorList || $[11] !== props.value || $[12] !== t1 || $[13] !== t2 || $[14] !== t3 || $[15] !== whitelist ? (t4 = /* @__PURE__ */ jsxRuntime.jsx(asyncAutocomplete.AsyncAutocomplete, { placeholder: "Select color", noOptionsPlaceholder: "No colors found", listItems: colorList, whitelist, blacklist, value: props.value, renderValue: _temp, onChange: t1, renderSelected: t2, renderOption: t3 }), $[9] = blacklist, $[10] = colorList, $[11] = props.value, $[12] = t1, $[13] = t2, $[14] = t3, $[15] = whitelist, $[16] = t4) : t4 = $[16], t4;
66
48
  }
67
49
  function _temp(value, opt) {
68
50
  return opt?.label ?? value;
@@ -72,23 +54,18 @@ const typeName = "wild.color", wildSanityColorFieldPlugin = sanity.definePlugin(
72
54
  schema: {
73
55
  types: [sanity.defineType({
74
56
  name: typeName,
75
- type: "object",
57
+ type: "string",
76
58
  title: "Color",
77
59
  description: "Select a color.",
78
60
  icon: () => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u{1F3A8}" }),
79
61
  components: {
80
62
  input: (props) => /* @__PURE__ */ jsxRuntime.jsx(ColorInput, { pluginConfig: config, ...props })
81
- },
82
- fields: [sanity.defineField({
83
- name: "value",
84
- type: "string"
85
- }), sanity.defineField({
86
- name: "luminance",
87
- type: "number"
88
- })]
63
+ }
89
64
  })]
90
65
  }
91
66
  }));
67
+ exports.defaultRenderOption = defaultRenderOption;
68
+ exports.defaultRenderSelected = defaultRenderSelected;
92
69
  exports.typeName = typeName;
93
70
  exports.wildSanityColorFieldPlugin = wildSanityColorFieldPlugin;
94
71
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/input.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\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\n// Maybe parse CSS variable and return its computed value.\n// Handles var(--variable), --variable.\n// If the input is not a CSS variable, returns it as-is.\nfunction maybeGetColorValue(source: string) {\n const varMatch = source.match(/^var\\((.+)\\)$/);\n const inner = varMatch?.[1] ? varMatch[1].trim() : source.trim();\n\n // Only take the first part for var(--variable, fallback) and ignore the fallback.\n const cssVarName = inner.split(\",\")[0]?.trim();\n const resolved = cssVarName?.startsWith(\"--\")\n ? getComputedStyle(document.documentElement).getPropertyValue(cssVarName).trim()\n : null;\n\n // Fallback to the source if we couldn't resolve the CSS variable.\n return resolved || source;\n}\n\n// The getLuminance function can throw for invalid colors.\nfunction safeGetLuminance(maybeColor: string) {\n try {\n return getLuminance(maybeColor);\n } catch (e) {\n console.warn(\"Failed to get luminance\", e);\n return undefined;\n }\n}\n\nfunction defaultRenderSelected(value: string) {\n return <Avatar style={{ backgroundColor: value }} />;\n}\n\nfunction defaultRenderOption({ label, value }: { label?: string; value: string }) {\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\nfunction ColorInput({\n pluginConfig,\n ...props\n}: ObjectInputProps<FieldValues> & {\n pluginConfig: PluginConfig;\n}) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const colorList = pluginConfig.colorList;\n const renderOption = pluginConfig.renderOption ?? defaultRenderOption;\n const renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected;\n\n const whitelist = options?.whitelist;\n const blacklist = options?.blacklist;\n\n return (\n <AsyncAutocomplete\n placeholder=\"Select color\"\n noOptionsPlaceholder=\"No colors found\"\n listItems={colorList}\n whitelist={whitelist}\n blacklist={blacklist}\n value={props.value?.value}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n if (!value) return props.onChange(unset());\n const color = maybeGetColorValue(value);\n const luminance = color ? safeGetLuminance(color) : undefined;\n return props.onChange(set({ ...props.value, value, luminance }));\n }}\n renderSelected={(value) => {\n return renderSelected(value);\n }}\n renderOption={({ label, value }) => {\n return renderOption({ label, value });\n }}\n />\n );\n}\n\nexport { ColorInput };\n","import type { ListItems } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectOptions, StringDefinition } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n/** @public */\nexport type PluginConfig = {\n /**\n * A list of colors to show in the color picker.\n * In addition to the value, the field will also calculate\n * and store the color's luminance in a separate field.\n * @example\n * ```ts\n * colorList: [\n * { label: \"Red\", value: \"#ff0000\" },\n * { label: \"Green\", value: \"#00ff00\" },\n * { label: \"Blue\", value: \"#0000ff\" },\n * ]\n * ```\n */\n colorList: ListItems;\n /**\n * A function to render the selected color value.\n * @example\n * ```ts\n * renderSelected: (value) => <div style={{ backgroundColor: value, width: 20, height: 20 }} />\n * ```\n */\n renderSelected?: (value: string) => React.JSX.Element;\n /** A function to render each color option in the dropdown.\n * @example\n * ```ts\n * renderOption: (item) => <div>{item.label}</div>\n * ```\n */\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * Whitelist colors by their values.\n */\n whitelist?: string[];\n /**\n * Blacklist colors by their values.\n */\n blacklist?: string[];\n};\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\" | \"options\" | \"components\"> & {\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 pluginConfig={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, typeName, type PluginConfig, type FieldOptions };\n"],"names":["maybeGetColorValue","source","varMatch","match","cssVarName","trim","split","startsWith","getComputedStyle","document","documentElement","getPropertyValue","safeGetLuminance","maybeColor","getLuminance","e","console","warn","defaultRenderSelected","value","jsx","Avatar","backgroundColor","defaultRenderOption","label","Card","Flex","Box","Text","ColorInput","t0","$","_c","pluginConfig","props","options","schemaType","colorList","renderOption","renderSelected","whitelist","blacklist","t1","t2","value_0","onChange","unset","color","luminance","undefined","set","t3","value_1","t4","t5","value_2","AsyncAutocomplete","_temp","opt","typeName","wildSanityColorFieldPlugin","definePlugin","config","name","schema","types","defineType","type","title","description","icon","Fragment","components","input","fields","defineField"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/input.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport { Avatar, Box, Card, Flex, Text } from \"@sanity/ui\";\nimport { type StringInputProps, set, unset } from \"sanity\";\nimport type { FieldOptions, PluginConfig } from \"./types\";\n\n/** @public */\nfunction defaultRenderSelected(value: React.ReactNode) {\n if (typeof value === \"string\") return <Avatar style={{ backgroundColor: value }} />;\n return value;\n}\n\n/** @public */\nfunction defaultRenderOption({ label, value }: { label?: React.ReactNode; value: React.ReactNode }) {\n const Color = typeof value === \"string\" ? <Avatar size={1} style={{ backgroundColor: value }} /> : value;\n const Label = typeof label === \"string\" ? <Text size={2}>{label}</Text> : (label ?? value);\n\n return (\n <Card as=\"button\">\n <Flex align=\"center\" padding={2}>\n {Color}\n <Box flex={1} padding={2}>\n {Label}\n </Box>\n </Flex>\n </Card>\n );\n}\n\nfunction ColorInput({\n pluginConfig,\n ...props\n}: StringInputProps & {\n pluginConfig: PluginConfig;\n}) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const colorList = pluginConfig.colorList;\n const renderOption = pluginConfig.renderOption ?? defaultRenderOption;\n const renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected;\n\n const whitelist = options?.whitelist;\n const blacklist = options?.blacklist;\n\n return (\n <AsyncAutocomplete\n placeholder=\"Select color\"\n noOptionsPlaceholder=\"No colors found\"\n listItems={colorList}\n whitelist={whitelist}\n blacklist={blacklist}\n value={props.value}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n const next = value ? set(value) : unset();\n return props.onChange(next);\n }}\n renderSelected={(value) => {\n return renderSelected(value);\n }}\n renderOption={({ label, value }) => {\n return renderOption({ label, value });\n }}\n />\n );\n}\n\nexport { ColorInput, defaultRenderSelected, defaultRenderOption };\n","import type { ListItems } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectOptions, StringDefinition } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n/** @public */\nexport type PluginConfig = {\n /**\n * A list of colors to show in the color picker.\n * In addition to the value, the field will also calculate\n * and store the color's luminance in a separate field.\n * @example\n * ```ts\n * colorList: [\n * { label: \"Red\", value: \"#ff0000\" },\n * { label: \"Green\", value: \"#00ff00\" },\n * { label: \"Blue\", value: \"#0000ff\" },\n * ]\n * ```\n */\n colorList: ListItems;\n /**\n * A function to render the selected color value.\n * Note: Import `defaultRenderSelected` to use the default rendering.\n * @example\n * ```ts\n * renderSelected: (value) => <div style={{ backgroundColor: value, width: 20, height: 20 }} />\n * ```\n */\n renderSelected?: (value: string) => React.JSX.Element;\n /** A function to render each color option in the dropdown.\n * Note: Import `defaultRenderOption` to use the default rendering.\n * @example\n * ```ts\n * renderOption: (item) => <div>{item.label}</div>\n * ```\n */\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * Whitelist colors by their values.\n */\n whitelist?: string[];\n /**\n * Blacklist colors by their values.\n */\n blacklist?: string[];\n};\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\" | \"options\" | \"components\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { definePlugin, defineType } from \"sanity\";\nimport { ColorInput, defaultRenderOption, defaultRenderSelected } 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 pluginConfig={config} {...props} />,\n },\n }),\n ],\n },\n };\n});\n\nexport { wildSanityColorFieldPlugin, typeName, defaultRenderOption, defaultRenderSelected, type PluginConfig, type FieldOptions };\n"],"names":["defaultRenderSelected","value","jsx","Avatar","backgroundColor","defaultRenderOption","label","Card","Flex","Color","Box","Label","Text","ColorInput","t0","$","_c","pluginConfig","props","options","schemaType","colorList","renderOption","renderSelected","whitelist","blacklist","t1","value_0","next","set","unset","onChange","t2","value_1","t3","t4","value_2","AsyncAutocomplete","_temp","opt","typeName","wildSanityColorFieldPlugin","definePlugin","config","name","schema","types","defineType","type","title","description","icon","Fragment","components","input"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as sanity0 from "sanity";
2
2
  import { ObjectOptions, StringDefinition } from "sanity";
3
+ import * as react7 from "react";
3
4
  /** @public */
4
5
  type ListItem = {
5
6
  value: string;
@@ -28,6 +29,7 @@ type PluginConfig = {
28
29
  colorList: ListItems;
29
30
  /**
30
31
  * A function to render the selected color value.
32
+ * Note: Import `defaultRenderSelected` to use the default rendering.
31
33
  * @example
32
34
  * ```ts
33
35
  * renderSelected: (value) => <div style={{ backgroundColor: value, width: 20, height: 20 }} />
@@ -35,6 +37,7 @@ type PluginConfig = {
35
37
  */
36
38
  renderSelected?: (value: string) => React.JSX.Element;
37
39
  /** A function to render each color option in the dropdown.
40
+ * Note: Import `defaultRenderOption` to use the default rendering.
38
41
  * @example
39
42
  * ```ts
40
43
  * renderOption: (item) => <div>{item.label}</div>
@@ -65,5 +68,15 @@ declare module "sanity" {
65
68
  }
66
69
  }
67
70
  /** @public */
71
+ declare function defaultRenderSelected(value: React.ReactNode): number | bigint | boolean | react7.JSX.Element | Iterable<react7.ReactNode> | Promise<string | number | bigint | boolean | react7.ReactPortal | react7.ReactElement<unknown, string | react7.JSXElementConstructor<any>> | Iterable<react7.ReactNode> | null | undefined> | null | undefined;
72
+ /** @public */
73
+ declare function defaultRenderOption({
74
+ label,
75
+ value
76
+ }: {
77
+ label?: React.ReactNode;
78
+ value: React.ReactNode;
79
+ }): react7.JSX.Element;
80
+ /** @public */
68
81
  declare const wildSanityColorFieldPlugin: sanity0.Plugin<PluginConfig>;
69
- export { type FieldOptions, type PluginConfig, typeName, wildSanityColorFieldPlugin };
82
+ export { type FieldOptions, type PluginConfig, defaultRenderOption, defaultRenderSelected, typeName, wildSanityColorFieldPlugin };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as sanity0 from "sanity";
2
2
  import { ObjectOptions, StringDefinition } from "sanity";
3
+ import * as react7 from "react";
3
4
  /** @public */
4
5
  type ListItem = {
5
6
  value: string;
@@ -28,6 +29,7 @@ type PluginConfig = {
28
29
  colorList: ListItems;
29
30
  /**
30
31
  * A function to render the selected color value.
32
+ * Note: Import `defaultRenderSelected` to use the default rendering.
31
33
  * @example
32
34
  * ```ts
33
35
  * renderSelected: (value) => <div style={{ backgroundColor: value, width: 20, height: 20 }} />
@@ -35,6 +37,7 @@ type PluginConfig = {
35
37
  */
36
38
  renderSelected?: (value: string) => React.JSX.Element;
37
39
  /** A function to render each color option in the dropdown.
40
+ * Note: Import `defaultRenderOption` to use the default rendering.
38
41
  * @example
39
42
  * ```ts
40
43
  * renderOption: (item) => <div>{item.label}</div>
@@ -65,5 +68,15 @@ declare module "sanity" {
65
68
  }
66
69
  }
67
70
  /** @public */
71
+ declare function defaultRenderSelected(value: React.ReactNode): number | bigint | boolean | react7.JSX.Element | Iterable<react7.ReactNode> | Promise<string | number | bigint | boolean | react7.ReactPortal | react7.ReactElement<unknown, string | react7.JSXElementConstructor<any>> | Iterable<react7.ReactNode> | null | undefined> | null | undefined;
72
+ /** @public */
73
+ declare function defaultRenderOption({
74
+ label,
75
+ value
76
+ }: {
77
+ label?: React.ReactNode;
78
+ value: React.ReactNode;
79
+ }): react7.JSX.Element;
80
+ /** @public */
68
81
  declare const wildSanityColorFieldPlugin: sanity0.Plugin<PluginConfig>;
69
- export { type FieldOptions, type PluginConfig, typeName, wildSanityColorFieldPlugin };
82
+ export { type FieldOptions, type PluginConfig, defaultRenderOption, defaultRenderSelected, typeName, wildSanityColorFieldPlugin };
package/dist/index.js CHANGED
@@ -1,35 +1,22 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { unset, set, definePlugin, defineType, defineField } from "sanity";
2
+ import { set, unset, definePlugin, defineType } from "sanity";
3
3
  import { c } from "react/compiler-runtime";
4
4
  import { AsyncAutocomplete } from "@madebywild/sanity-utils/async-autocomplete";
5
5
  import { Avatar, Card, Flex, Box, Text } from "@sanity/ui";
6
- import { getLuminance } from "color2k";
7
- function maybeGetColorValue(source) {
8
- const varMatch = source.match(/^var\((.+)\)$/), cssVarName = (varMatch?.[1] ? varMatch[1].trim() : source.trim()).split(",")[0]?.trim();
9
- return (cssVarName?.startsWith("--") ? getComputedStyle(document.documentElement).getPropertyValue(cssVarName).trim() : null) || source;
10
- }
11
- function safeGetLuminance(maybeColor) {
12
- try {
13
- return getLuminance(maybeColor);
14
- } catch (e) {
15
- console.warn("Failed to get luminance", e);
16
- return;
17
- }
18
- }
19
6
  function defaultRenderSelected(value) {
20
- return /* @__PURE__ */ jsx(Avatar, { style: {
7
+ return typeof value == "string" ? /* @__PURE__ */ jsx(Avatar, { style: {
21
8
  backgroundColor: value
22
- } });
9
+ } }) : value;
23
10
  }
24
11
  function defaultRenderOption({
25
12
  label,
26
13
  value
27
14
  }) {
28
15
  return /* @__PURE__ */ jsx(Card, { as: "button", children: /* @__PURE__ */ jsxs(Flex, { align: "center", padding: 2, children: [
29
- /* @__PURE__ */ jsx(Avatar, { size: 1, style: {
16
+ typeof value == "string" ? /* @__PURE__ */ jsx(Avatar, { size: 1, style: {
30
17
  backgroundColor: value
31
- } }),
32
- /* @__PURE__ */ jsx(Box, { flex: 1, padding: 2, children: /* @__PURE__ */ jsx(Text, { size: 2, children: label }) })
18
+ } }) : value,
19
+ /* @__PURE__ */ jsx(Box, { flex: 1, padding: 2, children: typeof label == "string" ? /* @__PURE__ */ jsx(Text, { size: 2, children: label }) : label ?? value })
33
20
  ] }) });
34
21
  }
35
22
  function ColorInput(t0) {
@@ -39,33 +26,27 @@ function ColorInput(t0) {
39
26
  pluginConfig,
40
27
  ...props
41
28
  } = t0, $[0] = t0, $[1] = pluginConfig, $[2] = props) : (pluginConfig = $[1], props = $[2]);
42
- const options = props.schemaType.options, colorList = pluginConfig.colorList, renderOption = pluginConfig.renderOption ?? defaultRenderOption, renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected, whitelist = options?.whitelist, blacklist = options?.blacklist, t1 = props.value?.value;
29
+ const options = props.schemaType.options, colorList = pluginConfig.colorList, renderOption = pluginConfig.renderOption ?? defaultRenderOption, renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected, whitelist = options?.whitelist, blacklist = options?.blacklist;
30
+ let t1;
31
+ $[3] !== props ? (t1 = (value_0) => {
32
+ const next = value_0 ? set(value_0) : unset();
33
+ return props.onChange(next);
34
+ }, $[3] = props, $[4] = t1) : t1 = $[4];
43
35
  let t2;
44
- $[3] !== props ? (t2 = (value_0) => {
45
- if (!value_0)
46
- return props.onChange(unset());
47
- const color = maybeGetColorValue(value_0), luminance = color ? safeGetLuminance(color) : void 0;
48
- return props.onChange(set({
49
- ...props.value,
50
- value: value_0,
51
- luminance
52
- }));
53
- }, $[3] = props, $[4] = t2) : t2 = $[4];
36
+ $[5] !== renderSelected ? (t2 = (value_1) => renderSelected(value_1), $[5] = renderSelected, $[6] = t2) : t2 = $[6];
54
37
  let t3;
55
- $[5] !== renderSelected ? (t3 = (value_1) => renderSelected(value_1), $[5] = renderSelected, $[6] = t3) : t3 = $[6];
56
- let t4;
57
- $[7] !== renderOption ? (t4 = (t52) => {
38
+ $[7] !== renderOption ? (t3 = (t42) => {
58
39
  const {
59
40
  label,
60
41
  value: value_2
61
- } = t52;
42
+ } = t42;
62
43
  return renderOption({
63
44
  label,
64
45
  value: value_2
65
46
  });
66
- }, $[7] = renderOption, $[8] = t4) : t4 = $[8];
67
- let t5;
68
- return $[9] !== blacklist || $[10] !== colorList || $[11] !== t1 || $[12] !== t2 || $[13] !== t3 || $[14] !== t4 || $[15] !== whitelist ? (t5 = /* @__PURE__ */ jsx(AsyncAutocomplete, { placeholder: "Select color", noOptionsPlaceholder: "No colors found", listItems: colorList, whitelist, blacklist, value: t1, renderValue: _temp, onChange: t2, renderSelected: t3, renderOption: t4 }), $[9] = blacklist, $[10] = colorList, $[11] = t1, $[12] = t2, $[13] = t3, $[14] = t4, $[15] = whitelist, $[16] = t5) : t5 = $[16], t5;
47
+ }, $[7] = renderOption, $[8] = t3) : t3 = $[8];
48
+ let t4;
49
+ return $[9] !== blacklist || $[10] !== colorList || $[11] !== props.value || $[12] !== t1 || $[13] !== t2 || $[14] !== t3 || $[15] !== whitelist ? (t4 = /* @__PURE__ */ jsx(AsyncAutocomplete, { placeholder: "Select color", noOptionsPlaceholder: "No colors found", listItems: colorList, whitelist, blacklist, value: props.value, renderValue: _temp, onChange: t1, renderSelected: t2, renderOption: t3 }), $[9] = blacklist, $[10] = colorList, $[11] = props.value, $[12] = t1, $[13] = t2, $[14] = t3, $[15] = whitelist, $[16] = t4) : t4 = $[16], t4;
69
50
  }
70
51
  function _temp(value, opt) {
71
52
  return opt?.label ?? value;
@@ -75,24 +56,19 @@ const typeName = "wild.color", wildSanityColorFieldPlugin = definePlugin((config
75
56
  schema: {
76
57
  types: [defineType({
77
58
  name: typeName,
78
- type: "object",
59
+ type: "string",
79
60
  title: "Color",
80
61
  description: "Select a color.",
81
62
  icon: () => /* @__PURE__ */ jsx(Fragment, { children: "\u{1F3A8}" }),
82
63
  components: {
83
64
  input: (props) => /* @__PURE__ */ jsx(ColorInput, { pluginConfig: config, ...props })
84
- },
85
- fields: [defineField({
86
- name: "value",
87
- type: "string"
88
- }), defineField({
89
- name: "luminance",
90
- type: "number"
91
- })]
65
+ }
92
66
  })]
93
67
  }
94
68
  }));
95
69
  export {
70
+ defaultRenderOption,
71
+ defaultRenderSelected,
96
72
  typeName,
97
73
  wildSanityColorFieldPlugin
98
74
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/input.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\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\n// Maybe parse CSS variable and return its computed value.\n// Handles var(--variable), --variable.\n// If the input is not a CSS variable, returns it as-is.\nfunction maybeGetColorValue(source: string) {\n const varMatch = source.match(/^var\\((.+)\\)$/);\n const inner = varMatch?.[1] ? varMatch[1].trim() : source.trim();\n\n // Only take the first part for var(--variable, fallback) and ignore the fallback.\n const cssVarName = inner.split(\",\")[0]?.trim();\n const resolved = cssVarName?.startsWith(\"--\")\n ? getComputedStyle(document.documentElement).getPropertyValue(cssVarName).trim()\n : null;\n\n // Fallback to the source if we couldn't resolve the CSS variable.\n return resolved || source;\n}\n\n// The getLuminance function can throw for invalid colors.\nfunction safeGetLuminance(maybeColor: string) {\n try {\n return getLuminance(maybeColor);\n } catch (e) {\n console.warn(\"Failed to get luminance\", e);\n return undefined;\n }\n}\n\nfunction defaultRenderSelected(value: string) {\n return <Avatar style={{ backgroundColor: value }} />;\n}\n\nfunction defaultRenderOption({ label, value }: { label?: string; value: string }) {\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\nfunction ColorInput({\n pluginConfig,\n ...props\n}: ObjectInputProps<FieldValues> & {\n pluginConfig: PluginConfig;\n}) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const colorList = pluginConfig.colorList;\n const renderOption = pluginConfig.renderOption ?? defaultRenderOption;\n const renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected;\n\n const whitelist = options?.whitelist;\n const blacklist = options?.blacklist;\n\n return (\n <AsyncAutocomplete\n placeholder=\"Select color\"\n noOptionsPlaceholder=\"No colors found\"\n listItems={colorList}\n whitelist={whitelist}\n blacklist={blacklist}\n value={props.value?.value}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n if (!value) return props.onChange(unset());\n const color = maybeGetColorValue(value);\n const luminance = color ? safeGetLuminance(color) : undefined;\n return props.onChange(set({ ...props.value, value, luminance }));\n }}\n renderSelected={(value) => {\n return renderSelected(value);\n }}\n renderOption={({ label, value }) => {\n return renderOption({ label, value });\n }}\n />\n );\n}\n\nexport { ColorInput };\n","import type { ListItems } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectOptions, StringDefinition } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n/** @public */\nexport type PluginConfig = {\n /**\n * A list of colors to show in the color picker.\n * In addition to the value, the field will also calculate\n * and store the color's luminance in a separate field.\n * @example\n * ```ts\n * colorList: [\n * { label: \"Red\", value: \"#ff0000\" },\n * { label: \"Green\", value: \"#00ff00\" },\n * { label: \"Blue\", value: \"#0000ff\" },\n * ]\n * ```\n */\n colorList: ListItems;\n /**\n * A function to render the selected color value.\n * @example\n * ```ts\n * renderSelected: (value) => <div style={{ backgroundColor: value, width: 20, height: 20 }} />\n * ```\n */\n renderSelected?: (value: string) => React.JSX.Element;\n /** A function to render each color option in the dropdown.\n * @example\n * ```ts\n * renderOption: (item) => <div>{item.label}</div>\n * ```\n */\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * Whitelist colors by their values.\n */\n whitelist?: string[];\n /**\n * Blacklist colors by their values.\n */\n blacklist?: string[];\n};\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\" | \"options\" | \"components\"> & {\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 pluginConfig={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, typeName, type PluginConfig, type FieldOptions };\n"],"names":["maybeGetColorValue","source","varMatch","match","cssVarName","trim","split","startsWith","getComputedStyle","document","documentElement","getPropertyValue","safeGetLuminance","maybeColor","getLuminance","e","console","warn","defaultRenderSelected","value","backgroundColor","defaultRenderOption","label","ColorInput","t0","$","_c","pluginConfig","props","options","schemaType","colorList","renderOption","renderSelected","whitelist","blacklist","t1","t2","value_0","onChange","unset","color","luminance","undefined","set","t3","value_1","t4","t5","value_2","_temp","opt","typeName","wildSanityColorFieldPlugin","definePlugin","config","name","schema","types","defineType","type","title","description","icon","components","input","fields","defineField"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/input.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport { Avatar, Box, Card, Flex, Text } from \"@sanity/ui\";\nimport { type StringInputProps, set, unset } from \"sanity\";\nimport type { FieldOptions, PluginConfig } from \"./types\";\n\n/** @public */\nfunction defaultRenderSelected(value: React.ReactNode) {\n if (typeof value === \"string\") return <Avatar style={{ backgroundColor: value }} />;\n return value;\n}\n\n/** @public */\nfunction defaultRenderOption({ label, value }: { label?: React.ReactNode; value: React.ReactNode }) {\n const Color = typeof value === \"string\" ? <Avatar size={1} style={{ backgroundColor: value }} /> : value;\n const Label = typeof label === \"string\" ? <Text size={2}>{label}</Text> : (label ?? value);\n\n return (\n <Card as=\"button\">\n <Flex align=\"center\" padding={2}>\n {Color}\n <Box flex={1} padding={2}>\n {Label}\n </Box>\n </Flex>\n </Card>\n );\n}\n\nfunction ColorInput({\n pluginConfig,\n ...props\n}: StringInputProps & {\n pluginConfig: PluginConfig;\n}) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const colorList = pluginConfig.colorList;\n const renderOption = pluginConfig.renderOption ?? defaultRenderOption;\n const renderSelected = pluginConfig.renderSelected ?? defaultRenderSelected;\n\n const whitelist = options?.whitelist;\n const blacklist = options?.blacklist;\n\n return (\n <AsyncAutocomplete\n placeholder=\"Select color\"\n noOptionsPlaceholder=\"No colors found\"\n listItems={colorList}\n whitelist={whitelist}\n blacklist={blacklist}\n value={props.value}\n renderValue={(value, opt) => opt?.label ?? value}\n onChange={(value) => {\n const next = value ? set(value) : unset();\n return props.onChange(next);\n }}\n renderSelected={(value) => {\n return renderSelected(value);\n }}\n renderOption={({ label, value }) => {\n return renderOption({ label, value });\n }}\n />\n );\n}\n\nexport { ColorInput, defaultRenderSelected, defaultRenderOption };\n","import type { ListItems } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectOptions, StringDefinition } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.color\" as const;\n\n/** @public */\nexport type PluginConfig = {\n /**\n * A list of colors to show in the color picker.\n * In addition to the value, the field will also calculate\n * and store the color's luminance in a separate field.\n * @example\n * ```ts\n * colorList: [\n * { label: \"Red\", value: \"#ff0000\" },\n * { label: \"Green\", value: \"#00ff00\" },\n * { label: \"Blue\", value: \"#0000ff\" },\n * ]\n * ```\n */\n colorList: ListItems;\n /**\n * A function to render the selected color value.\n * Note: Import `defaultRenderSelected` to use the default rendering.\n * @example\n * ```ts\n * renderSelected: (value) => <div style={{ backgroundColor: value, width: 20, height: 20 }} />\n * ```\n */\n renderSelected?: (value: string) => React.JSX.Element;\n /** A function to render each color option in the dropdown.\n * Note: Import `defaultRenderOption` to use the default rendering.\n * @example\n * ```ts\n * renderOption: (item) => <div>{item.label}</div>\n * ```\n */\n renderOption?: (item: { label?: string; value: string }) => React.JSX.Element;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * Whitelist colors by their values.\n */\n whitelist?: string[];\n /**\n * Blacklist colors by their values.\n */\n blacklist?: string[];\n};\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\" | \"options\" | \"components\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { definePlugin, defineType } from \"sanity\";\nimport { ColorInput, defaultRenderOption, defaultRenderSelected } 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 pluginConfig={config} {...props} />,\n },\n }),\n ],\n },\n };\n});\n\nexport { wildSanityColorFieldPlugin, typeName, defaultRenderOption, defaultRenderSelected, type PluginConfig, type FieldOptions };\n"],"names":["defaultRenderSelected","value","backgroundColor","defaultRenderOption","label","Color","Label","ColorInput","t0","$","_c","pluginConfig","props","options","schemaType","colorList","renderOption","renderSelected","whitelist","blacklist","t1","value_0","next","set","unset","onChange","t2","value_1","t3","t4","value_2","_temp","opt","typeName","wildSanityColorFieldPlugin","definePlugin","config","name","schema","types","defineType","type","title","description","icon","components","input"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madebywild/sanity-color-field",
3
- "version": "0.3.18",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "UNLICENSED",
@@ -21,22 +21,21 @@
21
21
  "access": "public"
22
22
  },
23
23
  "dependencies": {
24
- "color2k": "2.0.3",
25
- "@madebywild/sanity-utils": "0.3.16"
24
+ "@madebywild/sanity-utils": "1.0.0"
26
25
  },
27
26
  "peerDependencies": {
28
27
  "@sanity/ui": "^3.1",
29
- "react": "^19",
30
- "react-dom": "^19",
28
+ "react": "^19.2.2",
29
+ "react-dom": "^19.2.2",
31
30
  "sanity": "^4.17 || ^5.0.0"
32
31
  },
33
32
  "devDependencies": {
34
33
  "@sanity/pkg-utils": "^9.2",
35
- "@types/react": "^19",
36
- "@types/react-dom": "^19",
34
+ "@types/react": "^19.2.2",
35
+ "@types/react-dom": "^19.2.2",
37
36
  "babel-plugin-react-compiler": "1.0.0",
38
- "react": "^19",
39
- "react-dom": "^19",
37
+ "react": "^19.2.2",
38
+ "react-dom": "^19.2.2",
40
39
  "sanity": "^4.17 || ^5.0.0",
41
40
  "typescript": "^5"
42
41
  },
package/src/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
- import { defineField, definePlugin, defineType } from "sanity";
2
- import { ColorInput } from "./input";
1
+ import { definePlugin, defineType } from "sanity";
2
+ import { ColorInput, defaultRenderOption, defaultRenderSelected } from "./input";
3
3
  import { type FieldOptions, type PluginConfig, typeName } from "./types";
4
4
 
5
5
  /** @public */
@@ -10,27 +10,17 @@ const wildSanityColorFieldPlugin = definePlugin<PluginConfig>((config) => {
10
10
  types: [
11
11
  defineType({
12
12
  name: typeName,
13
- type: "object",
13
+ type: "string",
14
14
  title: "Color",
15
15
  description: "Select a color.",
16
16
  icon: () => <>🎨</>,
17
17
  components: {
18
18
  input: (props) => <ColorInput pluginConfig={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
- ],
30
20
  }),
31
21
  ],
32
22
  },
33
23
  };
34
24
  });
35
25
 
36
- export { wildSanityColorFieldPlugin, typeName, type PluginConfig, type FieldOptions };
26
+ export { wildSanityColorFieldPlugin, typeName, defaultRenderOption, defaultRenderSelected, type PluginConfig, type FieldOptions };
package/src/input.tsx CHANGED
@@ -1,54 +1,25 @@
1
1
  import { AsyncAutocomplete } from "@madebywild/sanity-utils/async-autocomplete";
2
2
  import { Avatar, Box, Card, Flex, Text } from "@sanity/ui";
3
- import { getLuminance } from "color2k";
4
- import { type ObjectInputProps, set, unset } from "sanity";
3
+ import { type StringInputProps, set, unset } from "sanity";
5
4
  import type { FieldOptions, PluginConfig } from "./types";
6
5
 
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
- // Maybe parse CSS variable and return its computed value.
15
- // Handles var(--variable), --variable.
16
- // If the input is not a CSS variable, returns it as-is.
17
- function maybeGetColorValue(source: string) {
18
- const varMatch = source.match(/^var\((.+)\)$/);
19
- const inner = varMatch?.[1] ? varMatch[1].trim() : source.trim();
20
-
21
- // Only take the first part for var(--variable, fallback) and ignore the fallback.
22
- const cssVarName = inner.split(",")[0]?.trim();
23
- const resolved = cssVarName?.startsWith("--")
24
- ? getComputedStyle(document.documentElement).getPropertyValue(cssVarName).trim()
25
- : null;
26
-
27
- // Fallback to the source if we couldn't resolve the CSS variable.
28
- return resolved || source;
6
+ /** @public */
7
+ function defaultRenderSelected(value: React.ReactNode) {
8
+ if (typeof value === "string") return <Avatar style={{ backgroundColor: value }} />;
9
+ return value;
29
10
  }
30
11
 
31
- // The getLuminance function can throw for invalid colors.
32
- function safeGetLuminance(maybeColor: string) {
33
- try {
34
- return getLuminance(maybeColor);
35
- } catch (e) {
36
- console.warn("Failed to get luminance", e);
37
- return undefined;
38
- }
39
- }
40
-
41
- function defaultRenderSelected(value: string) {
42
- return <Avatar style={{ backgroundColor: value }} />;
43
- }
12
+ /** @public */
13
+ function defaultRenderOption({ label, value }: { label?: React.ReactNode; value: React.ReactNode }) {
14
+ const Color = typeof value === "string" ? <Avatar size={1} style={{ backgroundColor: value }} /> : value;
15
+ const Label = typeof label === "string" ? <Text size={2}>{label}</Text> : (label ?? value);
44
16
 
45
- function defaultRenderOption({ label, value }: { label?: string; value: string }) {
46
17
  return (
47
18
  <Card as="button">
48
19
  <Flex align="center" padding={2}>
49
- <Avatar size={1} style={{ backgroundColor: value }} />
20
+ {Color}
50
21
  <Box flex={1} padding={2}>
51
- <Text size={2}>{label}</Text>
22
+ {Label}
52
23
  </Box>
53
24
  </Flex>
54
25
  </Card>
@@ -58,7 +29,7 @@ function defaultRenderOption({ label, value }: { label?: string; value: string }
58
29
  function ColorInput({
59
30
  pluginConfig,
60
31
  ...props
61
- }: ObjectInputProps<FieldValues> & {
32
+ }: StringInputProps & {
62
33
  pluginConfig: PluginConfig;
63
34
  }) {
64
35
  const options = props.schemaType.options as FieldOptions | undefined;
@@ -76,13 +47,11 @@ function ColorInput({
76
47
  listItems={colorList}
77
48
  whitelist={whitelist}
78
49
  blacklist={blacklist}
79
- value={props.value?.value}
50
+ value={props.value}
80
51
  renderValue={(value, opt) => opt?.label ?? value}
81
52
  onChange={(value) => {
82
- if (!value) return props.onChange(unset());
83
- const color = maybeGetColorValue(value);
84
- const luminance = color ? safeGetLuminance(color) : undefined;
85
- return props.onChange(set({ ...props.value, value, luminance }));
53
+ const next = value ? set(value) : unset();
54
+ return props.onChange(next);
86
55
  }}
87
56
  renderSelected={(value) => {
88
57
  return renderSelected(value);
@@ -94,4 +63,4 @@ function ColorInput({
94
63
  );
95
64
  }
96
65
 
97
- export { ColorInput };
66
+ export { ColorInput, defaultRenderSelected, defaultRenderOption };
package/src/types.tsx CHANGED
@@ -22,6 +22,7 @@ export type PluginConfig = {
22
22
  colorList: ListItems;
23
23
  /**
24
24
  * A function to render the selected color value.
25
+ * Note: Import `defaultRenderSelected` to use the default rendering.
25
26
  * @example
26
27
  * ```ts
27
28
  * renderSelected: (value) => <div style={{ backgroundColor: value, width: 20, height: 20 }} />
@@ -29,6 +30,7 @@ export type PluginConfig = {
29
30
  */
30
31
  renderSelected?: (value: string) => React.JSX.Element;
31
32
  /** A function to render each color option in the dropdown.
33
+ * Note: Import `defaultRenderOption` to use the default rendering.
32
34
  * @example
33
35
  * ```ts
34
36
  * renderOption: (item) => <div>{item.label}</div>