sanity-plugin-media 2.0.6 → 2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-media",
3
- "version": "2.0.6",
3
+ "version": "2.1.0",
4
4
  "description": "This version of `sanity-plugin-media` is for Sanity Studio V3.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -58,6 +58,7 @@
58
58
  "@reduxjs/toolkit": "^1.9.0",
59
59
  "@sanity/incompatible-plugin": "^1.0.4",
60
60
  "@sanity/ui": "^1.7.0",
61
+ "@sanity/uuid": "^3.0.1",
61
62
  "@tanem/react-nprogress": "^5.0.0",
62
63
  "copy-to-clipboard": "^3.3.1",
63
64
  "date-fns": "^2.27.0",
@@ -1,6 +1,6 @@
1
1
  import {CloseIcon} from '@sanity/icons'
2
2
  import {Box, Flex, Label, Text} from '@sanity/ui'
3
- import {SearchFacetInputProps} from '@types'
3
+ import {SearchFacetInputProps, WithId} from '@types'
4
4
  import React, {ReactNode} from 'react'
5
5
  import {useDispatch} from 'react-redux'
6
6
  import styled from 'styled-components'
@@ -8,7 +8,7 @@ import {searchActions} from '../../modules/search'
8
8
 
9
9
  type Props = {
10
10
  children: ReactNode
11
- facet: SearchFacetInputProps
11
+ facet: WithId<SearchFacetInputProps>
12
12
  }
13
13
 
14
14
  const Container = styled(Box)`
@@ -23,7 +23,7 @@ const SearchFacet = (props: Props) => {
23
23
  const dispatch = useDispatch()
24
24
 
25
25
  const handleClose = () => {
26
- dispatch(searchActions.facetsRemove({facetName: facet.name}))
26
+ dispatch(searchActions.facetsRemoveById({facetId: facet.id}))
27
27
  }
28
28
 
29
29
  return (
@@ -3,7 +3,8 @@ import {Box, Button, Menu, MenuButton, MenuDivider, MenuItem} from '@sanity/ui'
3
3
  import {
4
4
  SearchFacetInputNumberModifier,
5
5
  SearchFacetInputNumberProps,
6
- SearchFacetOperatorType
6
+ SearchFacetOperatorType,
7
+ WithId
7
8
  } from '@types'
8
9
  import React from 'react'
9
10
  import {useDispatch} from 'react-redux'
@@ -14,7 +15,7 @@ import SearchFacet from '../SearchFacet'
14
15
  import TextInputNumber from '../TextInputNumber'
15
16
 
16
17
  type Props = {
17
- facet: SearchFacetInputNumberProps
18
+ facet: WithId<SearchFacetInputNumberProps>
18
19
  }
19
20
 
20
21
  const SearchFacetNumber = ({facet}: Props) => {
@@ -29,15 +30,15 @@ const SearchFacetNumber = ({facet}: Props) => {
29
30
  : modifiers?.[0]
30
31
 
31
32
  const handleOperatorItemClick = (operatorType: SearchFacetOperatorType) => {
32
- dispatch(searchActions.facetsUpdate({name: facet.name, operatorType}))
33
+ dispatch(searchActions.facetsUpdateById({id: facet.id, operatorType}))
33
34
  }
34
35
 
35
36
  const handleModifierClick = (modifier: SearchFacetInputNumberModifier) => {
36
- dispatch(searchActions.facetsUpdate({name: facet.name, modifier: modifier.name}))
37
+ dispatch(searchActions.facetsUpdateById({id: facet.id, modifier: modifier.name}))
37
38
  }
38
39
 
39
40
  const handleValueChange = (value: number) => {
40
- dispatch(searchActions.facetsUpdate({name: facet.name, value}))
41
+ dispatch(searchActions.facetsUpdateById({id: facet.id, value}))
41
42
  }
42
43
 
43
44
  const selectedOperatorType: SearchFacetOperatorType = facet.operatorType ?? 'greaterThan'
@@ -3,7 +3,8 @@ import {Button, Menu, MenuButton, MenuDivider, MenuItem} from '@sanity/ui'
3
3
  import {
4
4
  SearchFacetInputSelectListItemProps,
5
5
  SearchFacetInputSelectProps,
6
- SearchFacetOperatorType
6
+ SearchFacetOperatorType,
7
+ WithId
7
8
  } from '@types'
8
9
  import React from 'react'
9
10
  import {useDispatch} from 'react-redux'
@@ -14,7 +15,7 @@ import SearchFacet from '../SearchFacet'
14
15
  import {usePortalPopoverProps} from '../../hooks/usePortalPopoverProps'
15
16
 
16
17
  type Props = {
17
- facet: SearchFacetInputSelectProps
18
+ facet: WithId<SearchFacetInputSelectProps>
18
19
  }
19
20
 
20
21
  const SearchFacetSelect = ({facet}: Props) => {
@@ -1,6 +1,6 @@
1
1
  import {SelectIcon} from '@sanity/icons'
2
2
  import {Box, Button, Menu, MenuButton, MenuDivider, MenuItem, TextInput} from '@sanity/ui'
3
- import {SearchFacetInputStringProps, SearchFacetOperatorType} from '@types'
3
+ import {SearchFacetInputStringProps, SearchFacetOperatorType, WithId} from '@types'
4
4
  import React, {ChangeEvent} from 'react'
5
5
  import {useDispatch} from 'react-redux'
6
6
 
@@ -10,7 +10,7 @@ import {searchActions} from '../../modules/search'
10
10
  import SearchFacet from '../SearchFacet'
11
11
 
12
12
  type Props = {
13
- facet: SearchFacetInputStringProps
13
+ facet: WithId<SearchFacetInputStringProps>
14
14
  }
15
15
 
16
16
  const SearchFacetString = ({facet}: Props) => {
@@ -20,11 +20,11 @@ const SearchFacetString = ({facet}: Props) => {
20
20
  const popoverProps = usePortalPopoverProps()
21
21
 
22
22
  const handleOperatorItemClick = (operatorType: SearchFacetOperatorType) => {
23
- dispatch(searchActions.facetsUpdate({name: facet.name, operatorType}))
23
+ dispatch(searchActions.facetsUpdateById({id: facet.id, operatorType}))
24
24
  }
25
25
 
26
26
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
27
- dispatch(searchActions.facetsUpdate({name: facet.name, value: e.target.value}))
27
+ dispatch(searchActions.facetsUpdateById({id: facet.id, value: e.target.value}))
28
28
  }
29
29
 
30
30
  const selectedOperatorType: SearchFacetOperatorType = facet.operatorType
@@ -1,6 +1,11 @@
1
1
  import {SelectIcon} from '@sanity/icons'
2
2
  import {Box, Button, Menu, MenuButton, MenuDivider, MenuItem} from '@sanity/ui'
3
- import {ReactSelectOption, SearchFacetInputSearchableProps, SearchFacetOperatorType} from '@types'
3
+ import {
4
+ ReactSelectOption,
5
+ SearchFacetInputSearchableProps,
6
+ SearchFacetOperatorType,
7
+ WithId
8
+ } from '@types'
4
9
  import React from 'react'
5
10
  import {useDispatch} from 'react-redux'
6
11
  import Select from 'react-select'
@@ -14,7 +19,7 @@ import getTagSelectOptions from '../../utils/getTagSelectOptions'
14
19
  import SearchFacet from '../SearchFacet'
15
20
 
16
21
  type Props = {
17
- facet: SearchFacetInputSearchableProps
22
+ facet: WithId<SearchFacetInputSearchableProps>
18
23
  }
19
24
 
20
25
  const SearchFacetTags = ({facet}: Props) => {
@@ -28,8 +33,8 @@ const SearchFacetTags = ({facet}: Props) => {
28
33
 
29
34
  const handleChange = (option: ReactSelectOption) => {
30
35
  dispatch(
31
- searchActions.facetsUpdate({
32
- name: facet.name,
36
+ searchActions.facetsUpdateById({
37
+ id: facet.id,
33
38
  value: option
34
39
  })
35
40
  )
@@ -37,8 +42,8 @@ const SearchFacetTags = ({facet}: Props) => {
37
42
 
38
43
  const handleOperatorItemClick = (operatorType: SearchFacetOperatorType) => {
39
44
  dispatch(
40
- searchActions.facetsUpdate({
41
- name: facet.name,
45
+ searchActions.facetsUpdateById({
46
+ id: facet.id,
42
47
  operatorType
43
48
  })
44
49
  )
@@ -25,20 +25,21 @@ const SearchFacets = (props: Props) => {
25
25
  const searchFacets = useTypedSelector(state => state.search.facets)
26
26
 
27
27
  const Items = searchFacets.map(facet => {
28
+ const key = facet.id
28
29
  if (facet.type === 'number') {
29
- return <SearchFacetNumber facet={facet} key={facet.name} />
30
+ return <SearchFacetNumber facet={facet} key={key} />
30
31
  }
31
32
 
32
33
  if (facet.type === 'searchable') {
33
- return <SearchFacetTags facet={facet} key={facet.name} />
34
+ return <SearchFacetTags facet={facet} key={key} />
34
35
  }
35
36
 
36
37
  if (facet.type === 'select') {
37
- return <SearchFacetSelect facet={facet} key={facet.name} />
38
+ return <SearchFacetSelect facet={facet} key={key} />
38
39
  }
39
40
 
40
41
  if (facet.type === 'string') {
41
- return <SearchFacetString facet={facet} key={facet.name} />
42
+ return <SearchFacetString facet={facet} key={key} />
42
43
  }
43
44
 
44
45
  return null
@@ -49,10 +49,7 @@ const SearchFacetsControl = () => {
49
49
  return true
50
50
  })
51
51
 
52
- // Determine if there are any remaining un-selected facets
53
- // (This operates under the assumption that only one of each facet can be active at any given time)
54
- const hasRemainingSearchFacets =
55
- filteredFacets.filter(facet => facet).length - searchFacets.length > 0
52
+ const hasSearchFacets = filteredFacets.length > 0
56
53
 
57
54
  const renderMenuFacets = (
58
55
  facets: (SearchFacetDivider | SearchFacetGroup | SearchFacetInputProps)[],
@@ -75,11 +72,11 @@ const SearchFacetsControl = () => {
75
72
  }
76
73
 
77
74
  if (facet) {
78
- const isPresent = !!searchFacets.find(v => v.name === facet.name)
75
+ const disabled = !facet.operatorTypes && !!searchFacets.find(v => v.name === facet.name)
79
76
 
80
77
  return (
81
78
  <MenuItem
82
- disabled={isPresent}
79
+ disabled={disabled}
83
80
  fontSize={1}
84
81
  key={facet.name}
85
82
  onClick={() => dispatch(searchActions.facetsAdd({facet}))}
@@ -101,7 +98,7 @@ const SearchFacetsControl = () => {
101
98
  <MenuButton
102
99
  button={
103
100
  <Button
104
- disabled={!hasRemainingSearchFacets}
101
+ disabled={!hasSearchFacets}
105
102
  fontSize={1}
106
103
  icon={AddCircleIcon}
107
104
  mode="bleed"
@@ -11,7 +11,7 @@ import useTypedSelector from '../../hooks/useTypedSelector'
11
11
  import {selectAssetsPicked} from '../../modules/assets'
12
12
  import {dialogActions} from '../../modules/dialog'
13
13
  import {DIALOG_ACTIONS} from '../../modules/dialog/actions'
14
- import {searchActions, selectHasSearchFacetTag, selectIsSearchFacetTag} from '../../modules/search'
14
+ import {searchActions, selectIsSearchFacetTag} from '../../modules/search'
15
15
 
16
16
  type Props = {
17
17
  actions?: TagActions[]
@@ -77,12 +77,11 @@ const Tag = (props: Props) => {
77
77
  // Redux
78
78
  const dispatch = useDispatch()
79
79
  const assetsPicked = useTypedSelector(selectAssetsPicked)
80
- const hasSearchFacetTag = useTypedSelector(selectHasSearchFacetTag)
81
80
  const isSearchFacetTag = useTypedSelector(state => selectIsSearchFacetTag(state, tag?.tag?._id))
82
81
 
83
82
  // Callbacks
84
83
  const handleSearchFacetTagRemove = () => {
85
- dispatch(searchActions.facetsRemove({facetName: 'tag'}))
84
+ dispatch(searchActions.facetsRemoveByTag({tagId: tag.tag._id}))
86
85
  }
87
86
 
88
87
  const handleShowAddTagToAssetsDialog = () => {
@@ -110,7 +109,7 @@ const Tag = (props: Props) => {
110
109
  }
111
110
  } as SearchFacetInputSearchableProps
112
111
 
113
- if (hasSearchFacetTag) {
112
+ if (isSearchFacetTag) {
114
113
  dispatch(
115
114
  searchActions.facetsUpdate({
116
115
  name: 'tag',
@@ -575,8 +575,11 @@ export const assetsSearchEpic: MyEpic = action$ =>
575
575
  ofType(
576
576
  searchActions.facetsAdd.type,
577
577
  searchActions.facetsClear.type,
578
- searchActions.facetsRemove.type,
578
+ searchActions.facetsRemoveById.type,
579
+ searchActions.facetsRemoveByName.type,
580
+ searchActions.facetsRemoveByTag.type,
579
581
  searchActions.facetsUpdate.type,
582
+ searchActions.facetsUpdateById.type,
580
583
  searchActions.querySet.type
581
584
  ),
582
585
  debounceTime(400),
@@ -723,8 +726,11 @@ export const assetsUnpickEpic: MyEpic = action$ =>
723
726
  assetsActions.viewSet.type,
724
727
  searchActions.facetsAdd.type,
725
728
  searchActions.facetsClear.type,
726
- searchActions.facetsRemove.type,
729
+ searchActions.facetsRemoveById.type,
730
+ searchActions.facetsRemoveByName.type,
731
+ searchActions.facetsRemoveByTag.type,
727
732
  searchActions.facetsUpdate.type,
733
+ searchActions.facetsUpdateById.type,
728
734
  searchActions.querySet.type
729
735
  ),
730
736
  mergeMap(() => {
@@ -1,8 +1,9 @@
1
- import {createSelector, createSlice, PayloadAction} from '@reduxjs/toolkit'
2
- import type {MyEpic, SearchFacetInputProps, SearchFacetOperatorType} from '@types'
3
- import {Selector} from 'react-redux'
1
+ import {PayloadAction, createSelector, createSlice} from '@reduxjs/toolkit'
2
+ import type {MyEpic, SearchFacetInputProps, SearchFacetOperatorType, WithId} from '@types'
4
3
  import {empty, of} from 'rxjs'
5
4
  import {filter, mergeMap, withLatestFrom} from 'rxjs/operators'
5
+ import {uuid} from '@sanity/uuid'
6
+
6
7
  import {tagsActions} from '../tags'
7
8
  import type {RootReducerState} from '../types'
8
9
 
@@ -10,7 +11,7 @@ import type {RootReducerState} from '../types'
10
11
  // (The main offender is `fieldModifier` which is currently a function)
11
12
 
12
13
  type SearchState = {
13
- facets: SearchFacetInputProps[]
14
+ facets: WithId<SearchFacetInputProps>[]
14
15
  query: string
15
16
  }
16
17
 
@@ -25,16 +26,32 @@ const searchSlice = createSlice({
25
26
  reducers: {
26
27
  // Add search facet
27
28
  facetsAdd(state, action: PayloadAction<{facet: SearchFacetInputProps}>) {
28
- state.facets.push(action.payload.facet)
29
+ state.facets.push({...action.payload.facet, id: uuid()})
29
30
  },
30
31
  // Clear all search facets
31
32
  facetsClear(state) {
32
33
  state.facets = []
33
34
  },
34
35
  // Remove search facet by name
35
- facetsRemove(state, action: PayloadAction<{facetName: string}>) {
36
+ facetsRemoveByName(state, action: PayloadAction<{facetName: string}>) {
36
37
  state.facets = state.facets.filter(facet => facet.name !== action.payload.facetName)
37
38
  },
39
+ // Remove search facet by name
40
+ facetsRemoveByTag(state, action: PayloadAction<{tagId: string}>) {
41
+ state.facets = state.facets.filter(
42
+ facet =>
43
+ !(
44
+ facet.name === 'tag' &&
45
+ facet.type === 'searchable' &&
46
+ (facet.operatorType === 'references' || facet.operatorType === 'doesNotReference') &&
47
+ facet.value?.value === action.payload.tagId
48
+ )
49
+ )
50
+ },
51
+ // Remove search facet by name
52
+ facetsRemoveById(state, action: PayloadAction<{facetId: string}>) {
53
+ state.facets = state.facets.filter(facet => facet.id !== action.payload.facetId)
54
+ },
38
55
  // Update an existing search facet
39
56
  facetsUpdate(
40
57
  state,
@@ -47,8 +64,38 @@ const searchSlice = createSlice({
47
64
  ) {
48
65
  const {modifier, name, operatorType, value} = action.payload
49
66
 
67
+ const facet = state.facets.find(f => f.name === name)
68
+
69
+ if (!facet) {
70
+ return
71
+ }
72
+
73
+ if (facet.type === 'number' && modifier) {
74
+ facet.modifier = modifier
75
+ }
76
+ if (operatorType) {
77
+ facet.operatorType = operatorType
78
+ }
79
+ if (typeof value !== 'undefined') {
80
+ facet.value = value
81
+ }
82
+
83
+ state.facets = state.facets.filter(f => f.name !== facet.name || f.id === facet.id)
84
+ },
85
+ // Update an existing search facet
86
+ facetsUpdateById(
87
+ state,
88
+ action: PayloadAction<{
89
+ modifier?: string
90
+ id: string
91
+ operatorType?: SearchFacetOperatorType
92
+ value?: any // TODO: type correctly
93
+ }>
94
+ ) {
95
+ const {modifier, id, operatorType, value} = action.payload
96
+
50
97
  state.facets.forEach((facet, index) => {
51
- if (facet.name === name) {
98
+ if (facet.id === id) {
52
99
  if (facet.type === 'number' && modifier) {
53
100
  facet.modifier = modifier
54
101
  }
@@ -100,33 +147,19 @@ export const searchFacetTagUpdateEpic: MyEpic = (action$, state$) =>
100
147
  )
101
148
 
102
149
  // Selectors
103
-
104
- export const selectHasSearchFacetTag: Selector<RootReducerState, boolean> = createSelector(
105
- (state: RootReducerState) => state.search.facets,
106
- searchFacets => !!searchFacets?.find(facet => facet.name === 'tag')
107
- )
108
-
109
150
  export const selectIsSearchFacetTag = createSelector(
110
151
  [
111
- (state: RootReducerState) => state.tags.byIds,
112
152
  (state: RootReducerState) => state.search.facets,
113
153
  (_state: RootReducerState, tagId: string) => tagId
114
154
  ],
115
- (tagsByIds, searchFacets, tagId) => {
116
- const searchFacet = searchFacets?.find(facet => facet.name === 'tag')
117
-
118
- if (searchFacet?.type === 'searchable') {
119
- const searchFacetTagId = searchFacet.value?.value
120
- if (searchFacetTagId) {
121
- return (
122
- tagsByIds[searchFacetTagId]?.tag?._id === tagId &&
123
- searchFacet?.operatorType === 'references'
124
- )
125
- }
126
- }
127
-
128
- return false
129
- }
155
+ (searchFacets, tagId) =>
156
+ searchFacets.some(
157
+ facet =>
158
+ facet.name === 'tag' &&
159
+ facet.type === 'searchable' &&
160
+ (facet.operatorType === 'references' || facet.operatorType === 'doesNotReference') &&
161
+ facet.value?.value === tagId
162
+ )
130
163
  )
131
164
 
132
165
  export const searchActions = searchSlice.actions
@@ -330,3 +330,7 @@ export type UploadItem = {
330
330
  size: number
331
331
  status: 'complete' | 'queued' | 'uploading'
332
332
  }
333
+
334
+ export type WithId<T extends SearchFacetInputProps> = T & {
335
+ id: string
336
+ }