sanity-plugin-iconify 2.0.0 → 2.0.2
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/dist/index.cjs +78 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +136 -59
- package/dist/index.d.ts +136 -59
- package/dist/index.js +76 -65
- package/dist/index.js.map +1 -1
- package/package.json +52 -45
- package/src/combobox/iconify-combobox.styles.tsx +7 -7
- package/src/combobox/iconify-combobox.tsx +64 -53
- package/src/combobox/search-input.tsx +4 -14
- package/src/combobox/search-result.tsx +36 -35
- package/src/iconify-input.tsx +1 -11
- package/src/lib/icon-types.gen.ts +1 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { Combobox } from '@headlessui/react';
|
|
2
1
|
import { Popover, useToast } from '@sanity/ui';
|
|
3
|
-
import
|
|
2
|
+
import { useCombobox } from 'downshift';
|
|
4
3
|
import { useCallback, useEffect, useId, useRef } from 'react';
|
|
5
4
|
import { match } from 'ts-pattern';
|
|
6
5
|
import { useSearch } from '../lib/api';
|
|
7
|
-
import {
|
|
6
|
+
import { OptionsWrapper } from './iconify-combobox.styles';
|
|
8
7
|
import { SearchInput } from './search-input';
|
|
9
8
|
import type { SearchResultsProps } from './search-result';
|
|
10
9
|
import { SearchResults } from './search-result';
|
|
@@ -28,20 +27,36 @@ export function IconifyCombobox(props: IconifyComboboxProps) {
|
|
|
28
27
|
collections,
|
|
29
28
|
});
|
|
30
29
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
const {
|
|
31
|
+
isOpen,
|
|
32
|
+
getMenuProps,
|
|
33
|
+
getInputProps,
|
|
34
|
+
highlightedIndex,
|
|
35
|
+
getItemProps,
|
|
36
|
+
selectItem,
|
|
37
|
+
setInputValue,
|
|
38
|
+
} = useCombobox({
|
|
39
|
+
items: data ?? [],
|
|
40
|
+
inputValue: term,
|
|
41
|
+
onInputValueChange({ inputValue, selectedItem }) {
|
|
42
|
+
if (inputValue !== selectedItem) {
|
|
43
|
+
setTerm(inputValue);
|
|
44
|
+
}
|
|
40
45
|
},
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
onSelectedItemChange({ selectedItem }) {
|
|
47
|
+
if (selectedItem) {
|
|
48
|
+
pushSelection(selectedItem);
|
|
49
|
+
setTerm('', true);
|
|
50
|
+
setInputValue('');
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
43
54
|
|
|
44
|
-
const handleUnset = useCallback(() =>
|
|
55
|
+
const handleUnset = useCallback(() => {
|
|
56
|
+
pushSelection('');
|
|
57
|
+
setTerm('', true);
|
|
58
|
+
selectItem('');
|
|
59
|
+
}, []);
|
|
45
60
|
|
|
46
61
|
useEffect(() => {
|
|
47
62
|
if (isError) {
|
|
@@ -57,45 +72,41 @@ export function IconifyCombobox(props: IconifyComboboxProps) {
|
|
|
57
72
|
}, [error, id, isError, toast]);
|
|
58
73
|
|
|
59
74
|
return (
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
selectedIcon={selectedIcon}
|
|
68
|
-
onChange={handleTermChange}
|
|
69
|
-
suffix={selectedIcon ? <UnsetButton onUnset={handleUnset} /> : null}
|
|
70
|
-
/>
|
|
75
|
+
<div>
|
|
76
|
+
{/* @ts-expect-error wrong typings */}
|
|
77
|
+
<SearchInput
|
|
78
|
+
{...getInputProps({ ref: inputRef })}
|
|
79
|
+
selectedIcon={selectedIcon}
|
|
80
|
+
suffix={selectedIcon ? <UnsetButton onUnset={handleUnset} /> : null}
|
|
81
|
+
/>
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
83
|
+
<Popover
|
|
84
|
+
open={true}
|
|
85
|
+
style={{ display: isOpen ? 'block' : 'none' }}
|
|
86
|
+
placement="bottom"
|
|
87
|
+
arrow={false}
|
|
88
|
+
matchReferenceWidth
|
|
89
|
+
constrainSize
|
|
90
|
+
referenceElement={inputRef.current}
|
|
91
|
+
content={
|
|
92
|
+
<OptionsWrapper>
|
|
93
|
+
<SearchResults
|
|
94
|
+
{...getMenuProps()}
|
|
95
|
+
state={match<boolean>(true)
|
|
96
|
+
.returnType<SearchResultsProps['state']>()
|
|
97
|
+
.with(isLoading, () => 'loading')
|
|
98
|
+
.with(!debouncedTerm, () => 'initial')
|
|
99
|
+
.with(isError, () => 'error')
|
|
100
|
+
.with(!data || data.length === 0, () => 'empty')
|
|
101
|
+
.with(isPreviousData, () => 'stale')
|
|
102
|
+
.otherwise(() => 'data')}
|
|
103
|
+
data={data}
|
|
104
|
+
getItemProps={getItemProps}
|
|
105
|
+
highlightedIndex={highlightedIndex}
|
|
95
106
|
/>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
</
|
|
107
|
+
</OptionsWrapper>
|
|
108
|
+
}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
100
111
|
);
|
|
101
112
|
}
|
|
@@ -1,30 +1,20 @@
|
|
|
1
|
-
import { ComboboxInput } from '@headlessui/react';
|
|
2
1
|
import { Icon } from '@iconify/react';
|
|
3
2
|
import { TextInput } from '@sanity/ui';
|
|
4
|
-
import type { ChangeEventHandler } from 'react';
|
|
5
3
|
import { forwardRef } from 'react';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
// SEARCH INPUT //
|
|
9
|
-
// ------------ //
|
|
10
|
-
|
|
11
|
-
interface SearchInputProps {
|
|
12
|
-
term: string;
|
|
5
|
+
interface SearchInputProps extends React.HTMLAttributes<HTMLInputElement> {
|
|
13
6
|
selectedIcon: string | null;
|
|
14
|
-
onChange: ChangeEventHandler<HTMLInputElement>;
|
|
15
7
|
suffix?: React.ReactNode;
|
|
16
8
|
}
|
|
17
9
|
|
|
18
10
|
export const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>((props, ref) => {
|
|
19
|
-
const {
|
|
11
|
+
const { selectedIcon, suffix, ...rest } = props;
|
|
20
12
|
|
|
21
13
|
return (
|
|
22
|
-
<
|
|
23
|
-
|
|
14
|
+
<TextInput
|
|
15
|
+
{...rest}
|
|
24
16
|
ref={ref}
|
|
25
17
|
inputMode="search"
|
|
26
|
-
value={term}
|
|
27
|
-
onChange={onChange}
|
|
28
18
|
icon={selectedIcon ? <Icon icon={selectedIcon} /> : null}
|
|
29
19
|
placeholder={selectedIcon ? 'Search and replace selected icon...' : 'Search for an icon...'}
|
|
30
20
|
suffix={suffix}
|
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import { ComboboxOption, ComboboxOptions } from '@headlessui/react';
|
|
2
1
|
import { Icon } from '@iconify/react';
|
|
3
2
|
import { Button } from '@sanity/ui';
|
|
3
|
+
import type { UseComboboxGetItemPropsOptions, UseComboboxGetItemPropsReturnValue } from 'downshift';
|
|
4
|
+
import { forwardRef } from 'react';
|
|
4
5
|
import { MessageWrapper } from './iconify-combobox.styles';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
type GetItemProps = (
|
|
8
|
+
options: UseComboboxGetItemPropsOptions<string>,
|
|
9
|
+
) => UseComboboxGetItemPropsReturnValue;
|
|
9
10
|
|
|
10
|
-
export interface SearchResultsProps {
|
|
11
|
+
export interface SearchResultsProps extends React.HTMLAttributes<HTMLUListElement> {
|
|
11
12
|
state: 'initial' | 'loading' | 'error' | 'empty' | 'data' | 'stale';
|
|
12
13
|
data?: string[];
|
|
14
|
+
getItemProps: GetItemProps;
|
|
15
|
+
highlightedIndex: number;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
export
|
|
16
|
-
const { state, data } = props;
|
|
17
|
-
|
|
18
|
-
if (state === 'initial') {
|
|
19
|
-
return <MessageWrapper>Search for icons</MessageWrapper>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (state === 'loading') {
|
|
23
|
-
return <MessageWrapper>Searching...</MessageWrapper>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (state === 'error') {
|
|
27
|
-
return <MessageWrapper>Something went wrong...</MessageWrapper>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (state === 'empty') {
|
|
31
|
-
return <MessageWrapper>No icons found</MessageWrapper>;
|
|
32
|
-
}
|
|
18
|
+
export const SearchResults = forwardRef<HTMLUListElement, SearchResultsProps>((props, ref) => {
|
|
19
|
+
const { state, data, getItemProps, highlightedIndex, ...rest } = props;
|
|
33
20
|
|
|
34
21
|
return (
|
|
35
|
-
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
22
|
+
<>
|
|
23
|
+
{(() => {
|
|
24
|
+
switch (state) {
|
|
25
|
+
case 'initial':
|
|
26
|
+
return <MessageWrapper>Search for icons</MessageWrapper>;
|
|
27
|
+
case 'loading':
|
|
28
|
+
return <MessageWrapper>Searching...</MessageWrapper>;
|
|
29
|
+
case 'error':
|
|
30
|
+
return <MessageWrapper>Something went wrong...</MessageWrapper>;
|
|
31
|
+
case 'empty':
|
|
32
|
+
return <MessageWrapper>No icons found</MessageWrapper>;
|
|
33
|
+
}
|
|
34
|
+
})()}
|
|
35
|
+
|
|
36
|
+
<ul {...rest} ref={ref} style={{ opacity: state === 'stale' ? 0.5 : 1 }}>
|
|
37
|
+
{(state === 'data' || state === 'stale') &&
|
|
38
|
+
data?.map((icon, index) => (
|
|
39
|
+
<li key={icon} {...getItemProps({ item: icon, index })}>
|
|
40
|
+
<Button padding={3} mode="bleed" selected={index === highlightedIndex}>
|
|
41
|
+
<Icon icon={icon} width="100%" height="100%" />
|
|
42
|
+
</Button>
|
|
43
|
+
</li>
|
|
44
|
+
))}
|
|
45
|
+
</ul>
|
|
46
|
+
</>
|
|
46
47
|
);
|
|
47
|
-
}
|
|
48
|
+
});
|
package/src/iconify-input.tsx
CHANGED
|
@@ -8,10 +8,6 @@ import { QueryClientProvider } from './lib/query-client';
|
|
|
8
8
|
import type { IconifyPluginConfig, IconOptions } from './lib/types';
|
|
9
9
|
import { usePrettyIconName } from './lib/use-pretty-icon-name';
|
|
10
10
|
|
|
11
|
-
// -------------- //
|
|
12
|
-
// ICONIFY INPUT //
|
|
13
|
-
// -------------- //
|
|
14
|
-
|
|
15
11
|
interface IconifyInputProps extends ObjectInputProps {
|
|
16
12
|
config: IconifyPluginConfig;
|
|
17
13
|
}
|
|
@@ -28,12 +24,6 @@ export function IconifyInput(props: IconifyInputProps) {
|
|
|
28
24
|
null;
|
|
29
25
|
const showName = options?.showName ?? config?.showName ?? false;
|
|
30
26
|
|
|
31
|
-
// const parentTheme = useTheme();
|
|
32
|
-
// const theme: RootTheme = useMemo(
|
|
33
|
-
// () => (parentTheme ? { ...parentTheme.sanity, color: studioTheme.color } : studioTheme),
|
|
34
|
-
// [parentTheme],
|
|
35
|
-
// );
|
|
36
|
-
|
|
37
27
|
const handleSelect = useCallback(
|
|
38
28
|
(icon: string) => {
|
|
39
29
|
pushChange(icon === '' ? unset() : set(icon, ['name']));
|
|
@@ -62,7 +52,7 @@ interface IconifyNameDisplayProps {
|
|
|
62
52
|
name?: string | null;
|
|
63
53
|
}
|
|
64
54
|
|
|
65
|
-
function IconifyNameDisplay(props: IconifyNameDisplayProps) {
|
|
55
|
+
export function IconifyNameDisplay(props: IconifyNameDisplayProps) {
|
|
66
56
|
const { name } = props;
|
|
67
57
|
const prettyName = usePrettyIconName({ name });
|
|
68
58
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type IconPrefix = 'material-symbols' | 'material-symbols-light' | 'ic' | 'mdi' | 'mdi-light' | 'line-md' | 'solar' | 'tabler' | 'hugeicons' | 'mingcute' | 'ri' | 'mynaui' | 'iconamoon' | 'iconoir' | 'lucide' | 'lucide-lab' | 'uil' | 'tdesign' | 'si' | 'bx' | 'bxs' | 'majesticons' | 'gg' | 'flowbite' | 'basil' | 'pixelarticons' | 'pixel' | 'akar-icons' | 'ci' | 'proicons' | 'typcn' | 'meteor-icons' | 'prime' | 'circum' | 'fe' | 'eos-icons' | 'bitcoin-icons' | 'humbleicons' | 'uim' | 'uit' | 'uis' | 'gridicons' | 'mi' | 'cuida' | 'weui' | 'duo-icons' | 'svg-spinners' | 'lets-icons' | 'mage' | 'stash' | 'lineicons' | 'icon-park-outline' | 'icon-park-solid' | 'icon-park-twotone' | 'jam' | 'guidance' | 'carbon' | 'ion' | 'famicons' | 'ant-design' | 'lsicon' | 'gravity-ui' | 'cil' | 'ep' | 'charm' | 'quill' | 'bytesize' | 'bi' | 'rivet-icons' | 'nimbus' | 'formkit' | 'fluent' | 'ph' | 'teenyicons' | 'clarity' | 'ix' | 'octicon' | 'memory' | 'system-uicons' | 'radix-icons' | 'zondicons' | 'uiw' | 'maki' | 'codex' | 'ei' | 'heroicons' | 'pepicons-pop' | 'pepicons-print' | 'pepicons-pencil' | 'f7' | 'pajamas' | 'garden' | 'streamline' | 'fa6-solid' | 'fa6-regular' | 'ooui' | 'oui' | 'nrk' | 'qlementine-icons' | 'fluent-color' | 'icon-park' | 'marketeq' | 'vscode-icons' | 'codicon' | 'material-icon-theme' | 'file-icons' | 'devicon' | 'devicon-plain' | 'catppuccin' | 'skill-icons' | 'unjs' | 'simple-icons' | 'logos' | 'cib' | 'fa6-brands' | 'bxl' | 'nonicons' | 'arcticons' | 'cbi' | 'brandico' | 'entypo-social' | 'token' | 'token-branded' | 'cryptocurrency' | 'cryptocurrency-color' | 'openmoji' | 'twemoji' | 'noto' | 'fluent-emoji' | 'fluent-emoji-flat' | 'fluent-emoji-high-contrast' | 'noto-v1' | 'emojione' | 'emojione-monotone' | 'emojione-v1' | 'fxemoji' | 'streamline-emojis' | 'circle-flags' | 'flag' | 'flagpack' | 'cif' | 'gis' | 'map' | 'geo' | 'game-icons' | 'fad' | 'academicons' | 'wi' | 'meteocons' | 'healthicons' | 'medical-icon' | 'covid' | 'la' | 'eva' | 'dashicons' | 'flat-color-icons' | 'entypo' | 'foundation' | 'raphael' | 'icons8' | 'iwwa' | 'gala' | 'heroicons-outline' | 'heroicons-solid' | 'fa-solid' | 'fa-regular' | 'fa-brands' | 'fa' | 'fluent-mdl2' | 'fontisto' | 'icomoon-free' | 'subway' | 'oi' | 'wpf' | 'simple-line-icons' | 'et' | 'el' | 'vaadin' | 'grommet-icons' | 'whh' | 'si-glyph' | 'zmdi' | 'ls' | 'bpmn' | 'flat-ui' | 'vs' | 'topcoat' | 'il' | 'websymbol' | 'fontelico' | 'ps' | 'feather' | 'mono-icons' | 'pepicons';
|