@srothgan/sanity-plugin-autocomplete-input 3.0.3 → 3.1.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/README.md CHANGED
@@ -39,12 +39,12 @@ yarn add @srothgan/sanity-plugin-autocomplete-input
39
39
  Add it as a plugin in sanity.config.ts (or .js):
40
40
 
41
41
  ```js
42
- import { autocompletInput } from "@srothgan/sanity-plugin-autocomplete-input";
42
+ import {autocompletInput} from '@srothgan/sanity-plugin-autocomplete-input'
43
43
 
44
44
  export default defineConfig({
45
45
  // ...
46
46
  plugins: [autocompletInput()],
47
- });
47
+ })
48
48
  ```
49
49
 
50
50
  ### Configuration Options
@@ -112,11 +112,11 @@ It is also possible to refer to the current parent value (for a top-level field
112
112
  export default {
113
113
  fields: [
114
114
  {
115
- name: "autocomplete-input",
116
- type: "autocomplete",
115
+ name: 'autocomplete-input',
116
+ type: 'autocomplete',
117
117
  options: {
118
118
  groq: {
119
- query: "*[_id != $docId]",
119
+ query: '*[_id != $docId]',
120
120
  params: (parent) => ({
121
121
  docId: parent?._id,
122
122
  }),
@@ -124,7 +124,7 @@ export default {
124
124
  },
125
125
  },
126
126
  ],
127
- };
127
+ }
128
128
  ```
129
129
 
130
130
  ### TypeScript Usage
@@ -132,9 +132,9 @@ export default {
132
132
  The plugin is written in TypeScript and exports all necessary types:
133
133
 
134
134
  ```typescript
135
- import { defineConfig } from 'sanity'
136
- import { autocompletInput } from '@srothgan/sanity-plugin-autocomplete-input'
137
- import type { InputOptions } from '@srothgan/sanity-plugin-autocomplete-input'
135
+ import {defineConfig} from 'sanity'
136
+ import {autocompletInput} from '@srothgan/sanity-plugin-autocomplete-input'
137
+ import type {InputOptions} from '@srothgan/sanity-plugin-autocomplete-input'
138
138
 
139
139
  export default defineConfig({
140
140
  // ...
@@ -165,12 +165,14 @@ export default defineConfig({
165
165
  This maintained fork includes the following enhancements:
166
166
 
167
167
  ### Updated Dependencies
168
+
168
169
  - **Sanity v4 & v5 Support**: Fully compatible with both Sanity Studio v4 and v5
169
170
  - **React 18 & 19 Support**: Works with React 18 (Sanity v4) and React 19 (Sanity v5)
170
171
  - **Modern Build Tooling**: Migrated from Babel to SWC for faster builds
171
172
  - **TypeScript 5.3**: Updated to latest TypeScript with improved type safety
172
173
 
173
174
  ### Maintained & Active
175
+
174
176
  - Regular dependency updates for security and compatibility
175
177
  - Active issue tracking and bug fixes
176
178
  - Community-driven improvements
@@ -180,9 +182,11 @@ All original functionality and API remain unchanged for seamless migration.
180
182
  ## Troubleshooting
181
183
 
182
184
  ### Plugin not appearing in Studio
185
+
183
186
  Make sure you've added the plugin to your `sanity.config.ts`:
187
+
184
188
  ```typescript
185
- import { autocompletInput } from '@srothgan/sanity-plugin-autocomplete-input'
189
+ import {autocompletInput} from '@srothgan/sanity-plugin-autocomplete-input'
186
190
 
187
191
  export default defineConfig({
188
192
  plugins: [autocompletInput()],
@@ -190,12 +194,15 @@ export default defineConfig({
190
194
  ```
191
195
 
192
196
  ### Autocomplete options not loading
197
+
193
198
  - Verify your GROQ query returns data in the format `[{ "value": "..." }]`
194
199
  - Check the browser console for any query errors
195
200
  - Ensure the `autocompleteFieldPath` matches an existing field in your documents
196
201
 
197
202
  ### TypeScript errors
203
+
198
204
  Make sure your `tsconfig.json` includes:
205
+
199
206
  ```json
200
207
  {
201
208
  "compilerOptions": {
package/dist/index.cjs ADDED
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: !0 });
3
+ var sanity = require("sanity"), jsxRuntime = require("react/jsx-runtime"), ui = require("@sanity/ui"), compact = require("just-compact"), get = require("just-safe-get"), unique = require("just-unique"), React = require("react");
4
+ function _interopDefaultCompat(e) {
5
+ return e && typeof e == "object" && "default" in e ? e : { default: e };
6
+ }
7
+ var compact__default = /* @__PURE__ */ _interopDefaultCompat(compact), get__default = /* @__PURE__ */ _interopDefaultCompat(get), unique__default = /* @__PURE__ */ _interopDefaultCompat(unique), React__default = /* @__PURE__ */ _interopDefaultCompat(React);
8
+ const AutoCompleteInput = (props) => {
9
+ const { id, schemaType, value, validationError, readOnly, onChange } = props, sanityClient = sanity.useClient(), documentValue = sanity.useFormValue([]), [loading, setLoading] = React.useState(!1), [query, setQuery] = React__default.default.useState(""), [options, setOptions] = React__default.default.useState([]), canCreateNew = schemaType.options?.disableNew !== !0, optionsList = React.useMemo(() => {
10
+ const uniqueOptions = unique__default.default(
11
+ options.map(({ value: optionValue }) => optionValue),
12
+ !1,
13
+ !0
14
+ );
15
+ return !uniqueOptions.find((optionValue) => optionValue === query) && canCreateNew ? [
16
+ ...uniqueOptions.map((optionValue) => ({ value: optionValue })),
17
+ { value: query, isNew: !0 }
18
+ ] : uniqueOptions.map((optionValue) => ({ value: optionValue }));
19
+ }, [query, options, canCreateNew]), handleQueryChange = React.useCallback((queryValue) => {
20
+ setQuery(queryValue ?? "");
21
+ }, []), handleChange = React.useCallback(
22
+ (newValue) => {
23
+ onChange(sanity.PatchEvent.from(newValue ? sanity.set(newValue) : sanity.unset()));
24
+ },
25
+ [onChange]
26
+ ), renderOption = React.useCallback(
27
+ (option) => /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { as: "button", padding: 3, tone: option.isNew ? "primary" : "default", shadow: 1, children: option.isNew ? canCreateNew && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
28
+ 'Create new option "',
29
+ option.value,
30
+ '"'
31
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: option.value }) }),
32
+ [canCreateNew]
33
+ );
34
+ return React.useEffect(() => {
35
+ if (schemaType.options?.options) {
36
+ setOptions(schemaType.options.options), setLoading(!1);
37
+ return;
38
+ }
39
+ const path = schemaType.options?.autocompleteFieldPath ?? "title", {
40
+ query: groqQuery,
41
+ transform,
42
+ params = {}
43
+ } = schemaType.options?.groq || {
44
+ query: `*[defined(${path})] { "value": ${path} }`
45
+ }, resolvedParams = typeof params == "function" ? params(documentValue) : params;
46
+ setLoading(!0), sanityClient.fetch(groqQuery, resolvedParams).then((results) => {
47
+ if (Array.isArray(results)) {
48
+ const transformedResults = transform ? transform(results) : results, compactedValues = compact__default.default(transformedResults.map((doc) => get__default.default(doc, "value")));
49
+ setOptions(compactedValues.map((optionValue) => ({ value: String(optionValue) }))), setLoading(!1);
50
+ }
51
+ });
52
+ }, [schemaType.options, documentValue, sanityClient]), /* @__PURE__ */ jsxRuntime.jsx(
53
+ ui.Autocomplete,
54
+ {
55
+ id,
56
+ readOnly: readOnly ?? !1,
57
+ customValidity: validationError,
58
+ loading,
59
+ disabled: loading,
60
+ options: optionsList,
61
+ value: value ?? "",
62
+ onChange: handleChange,
63
+ onQueryChange: handleQueryChange,
64
+ renderOption
65
+ }
66
+ );
67
+ }, typeName = "autocomplete", autocompleteString = sanity.defineType({
68
+ name: typeName,
69
+ type: "string",
70
+ title: "Autocomplete",
71
+ components: { input: AutoCompleteInput }
72
+ }), autocompletInput = sanity.definePlugin({
73
+ name: "sanity-plugin-autocomplete-input",
74
+ schema: {
75
+ types: [autocompleteString]
76
+ }
77
+ });
78
+ exports.autocompletInput = autocompletInput;
79
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/AutoCompleteInput.tsx","../src/schemas/autocompleteString.ts","../src/index.ts"],"sourcesContent":["import {Autocomplete, Card, Text} from '@sanity/ui'\nimport compact from 'just-compact'\nimport get from 'just-safe-get'\nimport unique from 'just-unique'\nimport React, {useCallback, useEffect, useMemo, useState} from 'react'\nimport {\n PatchEvent,\n set,\n StringInputProps,\n StringSchemaType,\n unset,\n useClient,\n useFormValue,\n} from 'sanity'\n\nimport type {InputOptions, Option} from './types/index.js'\n\nexport type AutocompleteSchemaType = Omit<StringSchemaType, 'options'> & {\n options?: StringSchemaType['options'] & InputOptions\n}\nexport type InputProps = StringInputProps<AutocompleteSchemaType>\n\nexport const AutoCompleteInput = (props: InputProps): React.ReactElement => {\n const {id, schemaType, value, validationError, readOnly, onChange} = props\n\n const sanityClient = useClient()\n const documentValue = useFormValue([])\n const [loading, setLoading] = useState(false)\n const [query, setQuery] = React.useState('')\n const [options, setOptions] = React.useState<Option[]>([])\n const canCreateNew = schemaType.options?.disableNew !== true\n\n const optionsList = useMemo<(Option & {isNew?: boolean})[]>(() => {\n const uniqueOptions = unique(\n options.map(({value: optionValue}) => optionValue),\n false,\n true,\n )\n const queryInOptions = uniqueOptions.find((optionValue) => optionValue === query)\n if (!queryInOptions && canCreateNew) {\n return [\n ...uniqueOptions.map((optionValue) => ({value: optionValue})),\n {value: query, isNew: true},\n ]\n }\n\n return uniqueOptions.map((optionValue) => ({value: optionValue}))\n }, [query, options, canCreateNew])\n\n const handleQueryChange = useCallback((queryValue: string | null) => {\n setQuery(queryValue ?? '')\n }, [])\n\n const handleChange = useCallback(\n (newValue: string) => {\n onChange(PatchEvent.from(newValue ? set(newValue) : unset()))\n },\n [onChange],\n )\n\n const renderOption = useCallback(\n (option: Option & {isNew?: boolean}) => (\n <Card as=\"button\" padding={3} tone={option.isNew ? 'primary' : 'default'} shadow={1}>\n {option.isNew ? (\n canCreateNew && <Text>Create new option &quot;{option.value}&quot;</Text>\n ) : (\n <Text>{option.value}</Text>\n )}\n </Card>\n ),\n [canCreateNew],\n )\n\n useEffect(() => {\n if (schemaType.options?.options) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setOptions(schemaType.options.options)\n setLoading(false)\n return\n }\n\n const path = schemaType.options?.autocompleteFieldPath ?? 'title'\n const {\n query: groqQuery,\n transform,\n params = {},\n } = schemaType.options?.groq || {\n query: `*[defined(${path})] { \"value\": ${path} }`,\n }\n\n const resolvedParams =\n typeof params === 'function'\n ? params(documentValue as Record<string, unknown> | undefined)\n : params\n\n setLoading(true)\n sanityClient.fetch(groqQuery, resolvedParams).then((results) => {\n if (Array.isArray(results)) {\n const transformedResults = transform ? transform(results) : results\n const compactedValues = compact(transformedResults.map((doc) => get(doc, 'value')))\n setOptions(compactedValues.map((optionValue) => ({value: String(optionValue)})))\n setLoading(false)\n }\n })\n }, [schemaType.options, documentValue, sanityClient])\n\n return (\n <Autocomplete\n id={id}\n readOnly={readOnly ?? false}\n customValidity={validationError}\n loading={loading}\n disabled={loading}\n options={optionsList}\n value={value ?? ''}\n onChange={handleChange}\n onQueryChange={handleQueryChange}\n renderOption={renderOption}\n />\n )\n}\n","import {defineType, StringDefinition} from 'sanity'\n\nimport {AutoCompleteInput} from '../AutoCompleteInput.js'\nimport type {InputOptions} from '../types/index.js'\n\nconst typeName = 'autocomplete' as const\n\n/**\n * @public\n */\nexport interface AutocompleteStringDefinition extends Omit<\n StringDefinition,\n 'type' | 'fields' | 'options'\n> {\n type: typeof typeName\n options?: InputOptions\n}\n\ndeclare module '@sanity/types' {\n // makes type: 'color' narrow correctly when using defineTyp/defineField/defineArrayMember\n export interface IntrinsicDefinitions {\n autocomplete: AutocompleteStringDefinition\n }\n}\n\nexport const autocompleteString = defineType({\n name: typeName,\n type: 'string',\n title: 'Autocomplete',\n components: {input: AutoCompleteInput},\n})\n","import {definePlugin} from 'sanity'\n\nimport {autocompleteString} from './schemas/autocompleteString.js'\n\nexport const autocompletInput = definePlugin({\n name: 'sanity-plugin-autocomplete-input',\n schema: {\n types: [autocompleteString],\n },\n})\n"],"names":["useClient","useFormValue","useState","React","useMemo","unique","useCallback","PatchEvent","set","unset","jsx","Card","Text","useEffect","compact","get","Autocomplete","defineType","definePlugin"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import {Plugin as Plugin_2} from 'sanity'
2
+
3
+ export declare const autocompletInput: Plugin_2<void>
4
+
5
+ export {}
@@ -0,0 +1,5 @@
1
+ import {Plugin as Plugin_2} from 'sanity'
2
+
3
+ export declare const autocompletInput: Plugin_2<void>
4
+
5
+ export {}
package/dist/index.js ADDED
@@ -0,0 +1,81 @@
1
+ import { useClient, useFormValue, PatchEvent, set, unset, defineType, definePlugin } from "sanity";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Card, Text, Autocomplete } from "@sanity/ui";
4
+ import compact from "just-compact";
5
+ import get from "just-safe-get";
6
+ import unique from "just-unique";
7
+ import React, { useState, useMemo, useCallback, useEffect } from "react";
8
+ const AutoCompleteInput = (props) => {
9
+ const { id, schemaType, value, validationError, readOnly, onChange } = props, sanityClient = useClient(), documentValue = useFormValue([]), [loading, setLoading] = useState(!1), [query, setQuery] = React.useState(""), [options, setOptions] = React.useState([]), canCreateNew = schemaType.options?.disableNew !== !0, optionsList = useMemo(() => {
10
+ const uniqueOptions = unique(
11
+ options.map(({ value: optionValue }) => optionValue),
12
+ !1,
13
+ !0
14
+ );
15
+ return !uniqueOptions.find((optionValue) => optionValue === query) && canCreateNew ? [
16
+ ...uniqueOptions.map((optionValue) => ({ value: optionValue })),
17
+ { value: query, isNew: !0 }
18
+ ] : uniqueOptions.map((optionValue) => ({ value: optionValue }));
19
+ }, [query, options, canCreateNew]), handleQueryChange = useCallback((queryValue) => {
20
+ setQuery(queryValue ?? "");
21
+ }, []), handleChange = useCallback(
22
+ (newValue) => {
23
+ onChange(PatchEvent.from(newValue ? set(newValue) : unset()));
24
+ },
25
+ [onChange]
26
+ ), renderOption = useCallback(
27
+ (option) => /* @__PURE__ */ jsx(Card, { as: "button", padding: 3, tone: option.isNew ? "primary" : "default", shadow: 1, children: option.isNew ? canCreateNew && /* @__PURE__ */ jsxs(Text, { children: [
28
+ 'Create new option "',
29
+ option.value,
30
+ '"'
31
+ ] }) : /* @__PURE__ */ jsx(Text, { children: option.value }) }),
32
+ [canCreateNew]
33
+ );
34
+ return useEffect(() => {
35
+ if (schemaType.options?.options) {
36
+ setOptions(schemaType.options.options), setLoading(!1);
37
+ return;
38
+ }
39
+ const path = schemaType.options?.autocompleteFieldPath ?? "title", {
40
+ query: groqQuery,
41
+ transform,
42
+ params = {}
43
+ } = schemaType.options?.groq || {
44
+ query: `*[defined(${path})] { "value": ${path} }`
45
+ }, resolvedParams = typeof params == "function" ? params(documentValue) : params;
46
+ setLoading(!0), sanityClient.fetch(groqQuery, resolvedParams).then((results) => {
47
+ if (Array.isArray(results)) {
48
+ const transformedResults = transform ? transform(results) : results, compactedValues = compact(transformedResults.map((doc) => get(doc, "value")));
49
+ setOptions(compactedValues.map((optionValue) => ({ value: String(optionValue) }))), setLoading(!1);
50
+ }
51
+ });
52
+ }, [schemaType.options, documentValue, sanityClient]), /* @__PURE__ */ jsx(
53
+ Autocomplete,
54
+ {
55
+ id,
56
+ readOnly: readOnly ?? !1,
57
+ customValidity: validationError,
58
+ loading,
59
+ disabled: loading,
60
+ options: optionsList,
61
+ value: value ?? "",
62
+ onChange: handleChange,
63
+ onQueryChange: handleQueryChange,
64
+ renderOption
65
+ }
66
+ );
67
+ }, typeName = "autocomplete", autocompleteString = defineType({
68
+ name: typeName,
69
+ type: "string",
70
+ title: "Autocomplete",
71
+ components: { input: AutoCompleteInput }
72
+ }), autocompletInput = definePlugin({
73
+ name: "sanity-plugin-autocomplete-input",
74
+ schema: {
75
+ types: [autocompleteString]
76
+ }
77
+ });
78
+ export {
79
+ autocompletInput
80
+ };
81
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/AutoCompleteInput.tsx","../src/schemas/autocompleteString.ts","../src/index.ts"],"sourcesContent":["import {Autocomplete, Card, Text} from '@sanity/ui'\nimport compact from 'just-compact'\nimport get from 'just-safe-get'\nimport unique from 'just-unique'\nimport React, {useCallback, useEffect, useMemo, useState} from 'react'\nimport {\n PatchEvent,\n set,\n StringInputProps,\n StringSchemaType,\n unset,\n useClient,\n useFormValue,\n} from 'sanity'\n\nimport type {InputOptions, Option} from './types/index.js'\n\nexport type AutocompleteSchemaType = Omit<StringSchemaType, 'options'> & {\n options?: StringSchemaType['options'] & InputOptions\n}\nexport type InputProps = StringInputProps<AutocompleteSchemaType>\n\nexport const AutoCompleteInput = (props: InputProps): React.ReactElement => {\n const {id, schemaType, value, validationError, readOnly, onChange} = props\n\n const sanityClient = useClient()\n const documentValue = useFormValue([])\n const [loading, setLoading] = useState(false)\n const [query, setQuery] = React.useState('')\n const [options, setOptions] = React.useState<Option[]>([])\n const canCreateNew = schemaType.options?.disableNew !== true\n\n const optionsList = useMemo<(Option & {isNew?: boolean})[]>(() => {\n const uniqueOptions = unique(\n options.map(({value: optionValue}) => optionValue),\n false,\n true,\n )\n const queryInOptions = uniqueOptions.find((optionValue) => optionValue === query)\n if (!queryInOptions && canCreateNew) {\n return [\n ...uniqueOptions.map((optionValue) => ({value: optionValue})),\n {value: query, isNew: true},\n ]\n }\n\n return uniqueOptions.map((optionValue) => ({value: optionValue}))\n }, [query, options, canCreateNew])\n\n const handleQueryChange = useCallback((queryValue: string | null) => {\n setQuery(queryValue ?? '')\n }, [])\n\n const handleChange = useCallback(\n (newValue: string) => {\n onChange(PatchEvent.from(newValue ? set(newValue) : unset()))\n },\n [onChange],\n )\n\n const renderOption = useCallback(\n (option: Option & {isNew?: boolean}) => (\n <Card as=\"button\" padding={3} tone={option.isNew ? 'primary' : 'default'} shadow={1}>\n {option.isNew ? (\n canCreateNew && <Text>Create new option &quot;{option.value}&quot;</Text>\n ) : (\n <Text>{option.value}</Text>\n )}\n </Card>\n ),\n [canCreateNew],\n )\n\n useEffect(() => {\n if (schemaType.options?.options) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setOptions(schemaType.options.options)\n setLoading(false)\n return\n }\n\n const path = schemaType.options?.autocompleteFieldPath ?? 'title'\n const {\n query: groqQuery,\n transform,\n params = {},\n } = schemaType.options?.groq || {\n query: `*[defined(${path})] { \"value\": ${path} }`,\n }\n\n const resolvedParams =\n typeof params === 'function'\n ? params(documentValue as Record<string, unknown> | undefined)\n : params\n\n setLoading(true)\n sanityClient.fetch(groqQuery, resolvedParams).then((results) => {\n if (Array.isArray(results)) {\n const transformedResults = transform ? transform(results) : results\n const compactedValues = compact(transformedResults.map((doc) => get(doc, 'value')))\n setOptions(compactedValues.map((optionValue) => ({value: String(optionValue)})))\n setLoading(false)\n }\n })\n }, [schemaType.options, documentValue, sanityClient])\n\n return (\n <Autocomplete\n id={id}\n readOnly={readOnly ?? false}\n customValidity={validationError}\n loading={loading}\n disabled={loading}\n options={optionsList}\n value={value ?? ''}\n onChange={handleChange}\n onQueryChange={handleQueryChange}\n renderOption={renderOption}\n />\n )\n}\n","import {defineType, StringDefinition} from 'sanity'\n\nimport {AutoCompleteInput} from '../AutoCompleteInput.js'\nimport type {InputOptions} from '../types/index.js'\n\nconst typeName = 'autocomplete' as const\n\n/**\n * @public\n */\nexport interface AutocompleteStringDefinition extends Omit<\n StringDefinition,\n 'type' | 'fields' | 'options'\n> {\n type: typeof typeName\n options?: InputOptions\n}\n\ndeclare module '@sanity/types' {\n // makes type: 'color' narrow correctly when using defineTyp/defineField/defineArrayMember\n export interface IntrinsicDefinitions {\n autocomplete: AutocompleteStringDefinition\n }\n}\n\nexport const autocompleteString = defineType({\n name: typeName,\n type: 'string',\n title: 'Autocomplete',\n components: {input: AutoCompleteInput},\n})\n","import {definePlugin} from 'sanity'\n\nimport {autocompleteString} from './schemas/autocompleteString.js'\n\nexport const autocompletInput = definePlugin({\n name: 'sanity-plugin-autocomplete-input',\n schema: {\n types: [autocompleteString],\n },\n})\n"],"names":[],"mappings":";;;;;;;AAsBO,MAAM,oBAAoB,CAAC,UAA0C;AAC1E,QAAM,EAAC,IAAI,YAAY,OAAO,iBAAiB,UAAU,aAAY,OAE/D,eAAe,UAAA,GACf,gBAAgB,aAAa,EAAE,GAC/B,CAAC,SAAS,UAAU,IAAI,SAAS,EAAK,GACtC,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,EAAE,GACrC,CAAC,SAAS,UAAU,IAAI,MAAM,SAAmB,EAAE,GACnD,eAAe,WAAW,SAAS,eAAe,IAElD,cAAc,QAAwC,MAAM;AAChE,UAAM,gBAAgB;AAAA,MACpB,QAAQ,IAAI,CAAC,EAAC,OAAO,YAAA,MAAiB,WAAW;AAAA,MACjD;AAAA,MACA;AAAA,IAAA;AAGF,WAAI,CADmB,cAAc,KAAK,CAAC,gBAAgB,gBAAgB,KAAK,KACzD,eACd;AAAA,MACL,GAAG,cAAc,IAAI,CAAC,iBAAiB,EAAC,OAAO,cAAa;AAAA,MAC5D,EAAC,OAAO,OAAO,OAAO,GAAA;AAAA,IAAI,IAIvB,cAAc,IAAI,CAAC,iBAAiB,EAAC,OAAO,cAAa;AAAA,EAClE,GAAG,CAAC,OAAO,SAAS,YAAY,CAAC,GAE3B,oBAAoB,YAAY,CAAC,eAA8B;AACnE,aAAS,cAAc,EAAE;AAAA,EAC3B,GAAG,CAAA,CAAE,GAEC,eAAe;AAAA,IACnB,CAAC,aAAqB;AACpB,eAAS,WAAW,KAAK,WAAW,IAAI,QAAQ,IAAI,MAAA,CAAO,CAAC;AAAA,IAC9D;AAAA,IACA,CAAC,QAAQ;AAAA,EAAA,GAGL,eAAe;AAAA,IACnB,CAAC,WACC,oBAAC,QAAK,IAAG,UAAS,SAAS,GAAG,MAAM,OAAO,QAAQ,YAAY,WAAW,QAAQ,GAC/E,iBAAO,QACN,qCAAiB,MAAA,EAAK,UAAA;AAAA,MAAA;AAAA,MAAyB,OAAO;AAAA,MAAM;AAAA,IAAA,GAAM,IAElE,oBAAC,MAAA,EAAM,UAAA,OAAO,OAAM,GAExB;AAAA,IAEF,CAAC,YAAY;AAAA,EAAA;AAGf,SAAA,UAAU,MAAM;AACd,QAAI,WAAW,SAAS,SAAS;AAE/B,iBAAW,WAAW,QAAQ,OAAO,GACrC,WAAW,EAAK;AAChB;AAAA,IACF;AAEA,UAAM,OAAO,WAAW,SAAS,yBAAyB,SACpD;AAAA,MACJ,OAAO;AAAA,MACP;AAAA,MACA,SAAS,CAAA;AAAA,IAAC,IACR,WAAW,SAAS,QAAQ;AAAA,MAC9B,OAAO,aAAa,IAAI,iBAAiB,IAAI;AAAA,IAAA,GAGzC,iBACJ,OAAO,UAAW,aACd,OAAO,aAAoD,IAC3D;AAEN,eAAW,EAAI,GACf,aAAa,MAAM,WAAW,cAAc,EAAE,KAAK,CAAC,YAAY;AAC9D,UAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,cAAM,qBAAqB,YAAY,UAAU,OAAO,IAAI,SACtD,kBAAkB,QAAQ,mBAAmB,IAAI,CAAC,QAAQ,IAAI,KAAK,OAAO,CAAC,CAAC;AAClF,mBAAW,gBAAgB,IAAI,CAAC,iBAAiB,EAAC,OAAO,OAAO,WAAW,EAAA,EAAG,CAAC,GAC/E,WAAW,EAAK;AAAA,MAClB;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,WAAW,SAAS,eAAe,YAAY,CAAC,GAGlD;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,gBAAgB;AAAA,MAChB;AAAA,MACA,UAAU;AAAA,MACV,SAAS;AAAA,MACT,OAAO,SAAS;AAAA,MAChB,UAAU;AAAA,MACV,eAAe;AAAA,MACf;AAAA,IAAA;AAAA,EAAA;AAGN,GCnHM,WAAW,gBAoBJ,qBAAqB,WAAW;AAAA,EAC3C,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,YAAY,EAAC,OAAO,kBAAA;AACtB,CAAC,GC1BY,mBAAmB,aAAa;AAAA,EAC3C,MAAM;AAAA,EACN,QAAQ;AAAA,IACN,OAAO,CAAC,kBAAkB;AAAA,EAAA;AAE9B,CAAC;"}
package/package.json CHANGED
@@ -1,30 +1,65 @@
1
1
  {
2
2
  "name": "@srothgan/sanity-plugin-autocomplete-input",
3
- "version": "3.0.3",
3
+ "version": "3.1.0",
4
+ "description": "Autocomplete input plugin for Sanity",
5
+ "keywords": [
6
+ "sanity",
7
+ "sanity-plugin",
8
+ "autocomplete",
9
+ "input",
10
+ "v4",
11
+ "v5",
12
+ "maintained",
13
+ "sanity-v5"
14
+ ],
4
15
  "license": "MIT",
16
+ "author": "Simon Rothgang <simonrothgang@gmail.com>",
17
+ "contributors": [
18
+ {
19
+ "name": "Liam Martens",
20
+ "email": "liam@freighter.studio",
21
+ "url": "https://github.com/LiamMartens"
22
+ }
23
+ ],
24
+ "sideEffects": false,
5
25
  "type": "module",
6
- "scripts": {
7
- "build:js": "swc ./src -d ./lib --strip-leading-paths",
8
- "build:types": "tsc -p .",
9
- "build": "run-p build:*",
10
- "lint": "eslint src/**/*.{ts,tsx}",
11
- "lint:fix": "eslint src/**/*.{ts,tsx} --fix",
12
- "format": "prettier --write \"src/**/*.{ts,tsx,json}\"",
13
- "format:check": "prettier --check \"src/**/*.{ts,tsx,json}\""
14
- },
15
- "types": "./lib/index.d.ts",
16
- "module": "./lib/index.js",
17
26
  "exports": {
18
27
  ".": {
19
- "types": "./lib/index.d.ts",
20
- "import": "./lib/index.js",
21
- "default": "./lib/index.js"
22
- }
28
+ "source": "./src/index.ts",
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./package.json": "./package.json"
23
34
  },
24
- "browserslist": "> 0.5%, last 2 versions, not dead",
35
+ "main": "./dist/index.cjs",
36
+ "module": "./dist/index.js",
37
+ "types": "./dist/index.d.ts",
25
38
  "files": [
26
- "lib"
39
+ "dist",
40
+ "sanity.json",
41
+ "src",
42
+ "v2-incompatible.js"
27
43
  ],
44
+ "publishConfig": {
45
+ "exports": {
46
+ ".": {
47
+ "import": "./dist/index.js",
48
+ "require": "./dist/index.cjs",
49
+ "default": "./dist/index.js"
50
+ },
51
+ "./package.json": "./package.json"
52
+ }
53
+ },
54
+ "scripts": {
55
+ "build": "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
56
+ "format": "prettier --write --cache --ignore-unknown .",
57
+ "format:check": "prettier --check --cache --ignore-unknown .",
58
+ "link-watch": "plugin-kit link-watch",
59
+ "lint": "eslint .",
60
+ "prepublishOnly": "npm run build",
61
+ "watch": "pkg-utils watch --strict"
62
+ },
28
63
  "repository": {
29
64
  "type": "git",
30
65
  "url": "https://github.com/srothgan/srothgan-sanity-plugin-autocomplete-input"
@@ -32,51 +67,48 @@
32
67
  "bugs": {
33
68
  "url": "https://github.com/srothgan/srothgan-sanity-plugin-autocomplete-input/issues"
34
69
  },
35
- "author": {
36
- "name": "Simon Rothgang",
37
- "email": "simonrothgang@gmail.com"
70
+ "dependencies": {
71
+ "@sanity/incompatible-plugin": "^1.0.5",
72
+ "just-compact": "^3.2.0",
73
+ "just-pick": "^4.2.0",
74
+ "just-safe-get": "^4.2.0",
75
+ "just-unique": "^4.2.0"
38
76
  },
39
- "contributors": [
40
- {
41
- "name": "Liam Martens",
42
- "email": "liam@freighter.studio",
43
- "url": "https://github.com/LiamMartens"
44
- }
45
- ],
46
- "keywords": [
47
- "sanity-plugin",
48
- "autocomplete",
49
- "v4",
50
- "v5",
51
- "maintained",
52
- "input",
53
- "sanity-v5"
54
- ],
55
77
  "devDependencies": {
78
+ "@eslint/eslintrc": "^3.2.0",
56
79
  "@eslint/js": "^9.39.2",
80
+ "@sanity/pkg-utils": "^10.2.3",
81
+ "@sanity/plugin-kit": "^4.0.20",
57
82
  "@sanity/ui": "^3.1.11",
58
- "@swc/cli": "^0.7.9",
59
- "@swc/core": "^1.3.99",
60
- "@types/node": "^20.19.0",
61
- "@types/react": "^18.2.38",
62
- "@types/react-dom": "^18.2.17",
83
+ "@types/react": "^19.2.7",
63
84
  "@typescript-eslint/eslint-plugin": "^8.50.1",
64
85
  "@typescript-eslint/parser": "^8.50.1",
65
86
  "eslint": "^9.39.2",
66
87
  "eslint-config-prettier": "^10.1.8",
88
+ "eslint-config-sanity": "^7.1.4",
89
+ "eslint-plugin-prettier": "^5.5.4",
67
90
  "eslint-plugin-react": "^7.37.5",
68
91
  "eslint-plugin-react-hooks": "^7.0.1",
69
- "npm-run-all": "^4.1.5",
92
+ "prettier": "^3.7.4",
93
+ "prettier-plugin-packagejson": "^2.5.20",
70
94
  "react": "^19.2.3",
71
95
  "react-dom": "^19.2.3",
72
96
  "sanity": "^5.1.0",
73
97
  "styled-components": "^6.1.19",
74
- "typescript": "~5.3.2"
98
+ "typescript": "^5.9.3"
75
99
  },
76
- "dependencies": {
77
- "just-compact": "^3.2.0",
78
- "just-pick": "^4.2.0",
79
- "just-safe-get": "^4.2.0",
80
- "just-unique": "^4.2.0"
100
+ "peerDependencies": {
101
+ "@sanity/ui": "^2 || ^3",
102
+ "react": "^18 || ^19",
103
+ "sanity": "^4 || ^5"
104
+ },
105
+ "engines": {
106
+ "node": ">=18"
107
+ },
108
+ "browserslist": "extends @sanity/browserslist-config",
109
+ "sanityPlugin": {
110
+ "verifyPackage": {
111
+ "eslintImports": false
112
+ }
81
113
  }
82
114
  }
package/sanity.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "parts": [
3
+ {
4
+ "implements": "part:@sanity/base/sanity-root",
5
+ "path": "./version-incompatible.js"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,121 @@
1
+ import {Autocomplete, Card, Text} from '@sanity/ui'
2
+ import compact from 'just-compact'
3
+ import get from 'just-safe-get'
4
+ import unique from 'just-unique'
5
+ import React, {useCallback, useEffect, useMemo, useState} from 'react'
6
+ import {
7
+ PatchEvent,
8
+ set,
9
+ StringInputProps,
10
+ StringSchemaType,
11
+ unset,
12
+ useClient,
13
+ useFormValue,
14
+ } from 'sanity'
15
+
16
+ import type {InputOptions, Option} from './types/index.js'
17
+
18
+ export type AutocompleteSchemaType = Omit<StringSchemaType, 'options'> & {
19
+ options?: StringSchemaType['options'] & InputOptions
20
+ }
21
+ export type InputProps = StringInputProps<AutocompleteSchemaType>
22
+
23
+ export const AutoCompleteInput = (props: InputProps): React.ReactElement => {
24
+ const {id, schemaType, value, validationError, readOnly, onChange} = props
25
+
26
+ const sanityClient = useClient()
27
+ const documentValue = useFormValue([])
28
+ const [loading, setLoading] = useState(false)
29
+ const [query, setQuery] = React.useState('')
30
+ const [options, setOptions] = React.useState<Option[]>([])
31
+ const canCreateNew = schemaType.options?.disableNew !== true
32
+
33
+ const optionsList = useMemo<(Option & {isNew?: boolean})[]>(() => {
34
+ const uniqueOptions = unique(
35
+ options.map(({value: optionValue}) => optionValue),
36
+ false,
37
+ true,
38
+ )
39
+ const queryInOptions = uniqueOptions.find((optionValue) => optionValue === query)
40
+ if (!queryInOptions && canCreateNew) {
41
+ return [
42
+ ...uniqueOptions.map((optionValue) => ({value: optionValue})),
43
+ {value: query, isNew: true},
44
+ ]
45
+ }
46
+
47
+ return uniqueOptions.map((optionValue) => ({value: optionValue}))
48
+ }, [query, options, canCreateNew])
49
+
50
+ const handleQueryChange = useCallback((queryValue: string | null) => {
51
+ setQuery(queryValue ?? '')
52
+ }, [])
53
+
54
+ const handleChange = useCallback(
55
+ (newValue: string) => {
56
+ onChange(PatchEvent.from(newValue ? set(newValue) : unset()))
57
+ },
58
+ [onChange],
59
+ )
60
+
61
+ const renderOption = useCallback(
62
+ (option: Option & {isNew?: boolean}) => (
63
+ <Card as="button" padding={3} tone={option.isNew ? 'primary' : 'default'} shadow={1}>
64
+ {option.isNew ? (
65
+ canCreateNew && <Text>Create new option &quot;{option.value}&quot;</Text>
66
+ ) : (
67
+ <Text>{option.value}</Text>
68
+ )}
69
+ </Card>
70
+ ),
71
+ [canCreateNew],
72
+ )
73
+
74
+ useEffect(() => {
75
+ if (schemaType.options?.options) {
76
+ // eslint-disable-next-line react-hooks/set-state-in-effect
77
+ setOptions(schemaType.options.options)
78
+ setLoading(false)
79
+ return
80
+ }
81
+
82
+ const path = schemaType.options?.autocompleteFieldPath ?? 'title'
83
+ const {
84
+ query: groqQuery,
85
+ transform,
86
+ params = {},
87
+ } = schemaType.options?.groq || {
88
+ query: `*[defined(${path})] { "value": ${path} }`,
89
+ }
90
+
91
+ const resolvedParams =
92
+ typeof params === 'function'
93
+ ? params(documentValue as Record<string, unknown> | undefined)
94
+ : params
95
+
96
+ setLoading(true)
97
+ sanityClient.fetch(groqQuery, resolvedParams).then((results) => {
98
+ if (Array.isArray(results)) {
99
+ const transformedResults = transform ? transform(results) : results
100
+ const compactedValues = compact(transformedResults.map((doc) => get(doc, 'value')))
101
+ setOptions(compactedValues.map((optionValue) => ({value: String(optionValue)})))
102
+ setLoading(false)
103
+ }
104
+ })
105
+ }, [schemaType.options, documentValue, sanityClient])
106
+
107
+ return (
108
+ <Autocomplete
109
+ id={id}
110
+ readOnly={readOnly ?? false}
111
+ customValidity={validationError}
112
+ loading={loading}
113
+ disabled={loading}
114
+ options={optionsList}
115
+ value={value ?? ''}
116
+ onChange={handleChange}
117
+ onQueryChange={handleQueryChange}
118
+ renderOption={renderOption}
119
+ />
120
+ )
121
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ import {definePlugin} from 'sanity'
2
+
3
+ import {autocompleteString} from './schemas/autocompleteString.js'
4
+
5
+ export const autocompletInput = definePlugin({
6
+ name: 'sanity-plugin-autocomplete-input',
7
+ schema: {
8
+ types: [autocompleteString],
9
+ },
10
+ })
@@ -0,0 +1,31 @@
1
+ import {defineType, StringDefinition} from 'sanity'
2
+
3
+ import {AutoCompleteInput} from '../AutoCompleteInput.js'
4
+ import type {InputOptions} from '../types/index.js'
5
+
6
+ const typeName = 'autocomplete' as const
7
+
8
+ /**
9
+ * @public
10
+ */
11
+ export interface AutocompleteStringDefinition extends Omit<
12
+ StringDefinition,
13
+ 'type' | 'fields' | 'options'
14
+ > {
15
+ type: typeof typeName
16
+ options?: InputOptions
17
+ }
18
+
19
+ declare module '@sanity/types' {
20
+ // makes type: 'color' narrow correctly when using defineTyp/defineField/defineArrayMember
21
+ export interface IntrinsicDefinitions {
22
+ autocomplete: AutocompleteStringDefinition
23
+ }
24
+ }
25
+
26
+ export const autocompleteString = defineType({
27
+ name: typeName,
28
+ type: 'string',
29
+ title: 'Autocomplete',
30
+ components: {input: AutoCompleteInput},
31
+ })
@@ -0,0 +1,12 @@
1
+ import type {Option} from './Option.js'
2
+
3
+ export type InputOptions<Parent = Record<string, unknown>, Params = Record<string, unknown>> = {
4
+ autocompleteFieldPath?: string
5
+ disableNew?: boolean
6
+ options?: Option[]
7
+ groq?: {
8
+ query: string
9
+ params?: Params | ((parent?: Parent) => Params)
10
+ transform?: (result: unknown) => Option[]
11
+ }
12
+ }
@@ -1,3 +1,3 @@
1
1
  export type Option = {
2
- value: string;
3
- };
2
+ value: string
3
+ }
@@ -0,0 +1,2 @@
1
+ export type {InputOptions} from './InputOptions.js'
2
+ export type {Option} from './Option.js'
@@ -1,8 +0,0 @@
1
- import React from "react";
2
- import { StringInputProps, StringSchemaType } from "sanity";
3
- import type { InputOptions } from "./types";
4
- export type AutocompleteSchemaType = Omit<StringSchemaType, "options"> & {
5
- options?: StringSchemaType["options"] & InputOptions;
6
- };
7
- export type InputProps = StringInputProps<AutocompleteSchemaType>;
8
- export declare const AutoCompleteInput: (props: InputProps) => React.JSX.Element;
@@ -1,90 +0,0 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from "react";
2
- import get from "just-safe-get";
3
- import compact from "just-compact";
4
- import unique from "just-unique";
5
- import { Autocomplete, Text, Card } from "@sanity/ui";
6
- import { PatchEvent, set, unset, useClient, useFormValue } from "sanity";
7
- export const AutoCompleteInput = (props)=>{
8
- const { id, schemaType, value, validationError, readOnly, onChange } = props;
9
- const sanityClient = useClient();
10
- const documentValue = useFormValue([]);
11
- const [loading, setLoading] = useState(false);
12
- const [query, setQuery] = React.useState("");
13
- const [options, setOptions] = React.useState([]);
14
- const canCreateNew = schemaType.options?.disableNew !== true;
15
- const optionsList = useMemo(()=>{
16
- const uniqueOptions = unique(options.map(({ value })=>value), false, true);
17
- const queryInOptions = uniqueOptions.find((value)=>value === query);
18
- if (!queryInOptions && canCreateNew) {
19
- return [
20
- ...uniqueOptions.map((value)=>({
21
- value
22
- })),
23
- {
24
- value: query,
25
- isNew: true
26
- }
27
- ];
28
- }
29
- return uniqueOptions.map((value)=>({
30
- value
31
- }));
32
- }, [
33
- query,
34
- options,
35
- canCreateNew
36
- ]);
37
- const handleQueryChange = useCallback((query)=>{
38
- setQuery(query ?? "");
39
- }, []);
40
- const handleChange = useCallback((value)=>{
41
- onChange(PatchEvent.from(value ? set(value) : unset()));
42
- }, [
43
- onChange
44
- ]);
45
- useEffect(()=>{
46
- if (schemaType.options?.options) {
47
- // eslint-disable-next-line react-hooks/set-state-in-effect
48
- setOptions(schemaType.options.options);
49
- setLoading(false);
50
- return;
51
- }
52
- const path = schemaType.options?.autocompleteFieldPath ?? "title";
53
- const { query, transform, params = {} } = schemaType.options?.groq || {
54
- query: `*[defined(${path})] { "value": ${path} }`
55
- };
56
- const resolvedParams = typeof params === "function" ? params(documentValue) : params;
57
- setLoading(true);
58
- sanityClient.fetch(query, resolvedParams).then((results)=>{
59
- if (Array.isArray(results)) {
60
- const transformedResults = transform ? transform(results) : results;
61
- const compactedValues = compact(transformedResults.map((doc)=>get(doc, "value")));
62
- setOptions(compactedValues.map((value)=>({
63
- value: String(value)
64
- })));
65
- setLoading(false);
66
- }
67
- });
68
- }, [
69
- schemaType.options,
70
- documentValue,
71
- sanityClient
72
- ]);
73
- return /*#__PURE__*/ React.createElement(Autocomplete, {
74
- id: id,
75
- readOnly: readOnly ?? false,
76
- customValidity: validationError,
77
- loading: loading,
78
- disabled: loading,
79
- options: optionsList,
80
- value: value ?? "",
81
- onChange: handleChange,
82
- onQueryChange: handleQueryChange,
83
- renderOption: (option)=>/*#__PURE__*/ React.createElement(Card, {
84
- as: "button",
85
- padding: 3,
86
- tone: option.isNew ? "primary" : "default",
87
- shadow: 1
88
- }, option.isNew ? canCreateNew && /*#__PURE__*/ React.createElement(Text, null, 'Create new option "', option.value, '"') : /*#__PURE__*/ React.createElement(Text, null, option.value))
89
- });
90
- };
package/lib/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare const autocompletInput: import("sanity").Plugin<void>;
package/lib/index.js DELETED
@@ -1,10 +0,0 @@
1
- import { definePlugin } from "sanity";
2
- import { autocompleteString } from "./schemas/autocompleteString";
3
- export const autocompletInput = definePlugin({
4
- name: "sanity-plugin-autocomplete-input",
5
- schema: {
6
- types: [
7
- autocompleteString
8
- ]
9
- }
10
- });
@@ -1,20 +0,0 @@
1
- import { StringDefinition } from "sanity";
2
- import type { InputOptions } from "../types";
3
- declare const typeName: "autocomplete";
4
- /**
5
- * @public
6
- */
7
- export interface AutocompleteStringDefinition extends Omit<StringDefinition, "type" | "fields" | "options"> {
8
- type: typeof typeName;
9
- options?: InputOptions;
10
- }
11
- declare module "@sanity/types" {
12
- interface IntrinsicDefinitions {
13
- autocomplete: AutocompleteStringDefinition;
14
- }
15
- }
16
- export declare const autocompleteString: {
17
- type: "string";
18
- name: "autocomplete";
19
- } & Omit<StringDefinition, "preview">;
20
- export {};
@@ -1,11 +0,0 @@
1
- import { defineType } from "sanity";
2
- import { AutoCompleteInput } from "../AutoCompleteInput";
3
- const typeName = "autocomplete";
4
- export const autocompleteString = defineType({
5
- name: typeName,
6
- type: "string",
7
- title: "Autocomplete",
8
- components: {
9
- input: AutoCompleteInput
10
- }
11
- });
@@ -1,11 +0,0 @@
1
- import type { Option } from "./Option";
2
- export type InputOptions<Parent = Record<string, unknown>, Params = Record<string, unknown>> = {
3
- autocompleteFieldPath?: string;
4
- disableNew?: boolean;
5
- options?: Option[];
6
- groq?: {
7
- query: string;
8
- params?: Params | ((parent?: Parent) => Params);
9
- transform?: (result: unknown) => Option[];
10
- };
11
- };
@@ -1 +0,0 @@
1
- export { };
@@ -1 +0,0 @@
1
- export { };
@@ -1,2 +0,0 @@
1
- export type { InputOptions } from "./InputOptions";
2
- export type { Option } from "./Option";
@@ -1 +0,0 @@
1
- export { };