sanity-plugin-media 2.0.5 → 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/lib/index.esm.js +1 -2
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/package.json +6 -7
- package/src/components/AssetGridVirtualized/index.tsx +2 -1
- package/src/components/Browser/index.tsx +29 -25
- package/src/components/ButtonAssetCopy/index.tsx +4 -7
- package/src/components/CardUpload/index.tsx +12 -24
- package/src/components/DebugControls/index.tsx +1 -0
- package/src/components/FormBuilderTool/index.tsx +1 -1
- package/src/components/FormSubmitButton/index.tsx +1 -0
- package/src/components/OrderSelect/index.tsx +4 -0
- package/src/components/SearchFacet/index.tsx +3 -3
- package/src/components/SearchFacetNumber/index.tsx +12 -8
- package/src/components/SearchFacetSelect/index.tsx +9 -5
- package/src/components/SearchFacetString/index.tsx +9 -7
- package/src/components/SearchFacetTags/index.tsx +16 -9
- package/src/components/SearchFacets/index.tsx +5 -4
- package/src/components/SearchFacetsControl/index.tsx +11 -9
- package/src/components/TableRowUpload/index.tsx +12 -24
- package/src/components/Tag/index.tsx +5 -4
- package/src/components/TagViewHeader/index.tsx +2 -5
- package/src/hooks/useKeyPress.ts +14 -12
- package/src/hooks/usePortalPopoverProps.ts +12 -0
- package/src/modules/assets/index.ts +8 -2
- package/src/modules/index.ts +18 -4
- package/src/modules/search/index.ts +62 -29
- package/src/types/index.ts +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {hues} from '@sanity/color'
|
|
2
2
|
import {CloseIcon} from '@sanity/icons'
|
|
3
|
-
import {Box, Button, Flex, Grid, Stack, Text,
|
|
3
|
+
import {Box, Button, Flex, Grid, Stack, Text, useMediaIndex} from '@sanity/ui'
|
|
4
4
|
import filesize from 'filesize'
|
|
5
5
|
import React from 'react'
|
|
6
6
|
import {useDispatch} from 'react-redux'
|
|
@@ -100,10 +100,10 @@ const TableRowUpload = (props: Props) => {
|
|
|
100
100
|
</div>
|
|
101
101
|
)}
|
|
102
102
|
|
|
103
|
-
{/*
|
|
103
|
+
{/*
|
|
104
104
|
Cancel upload button.
|
|
105
105
|
Assets will only have a `complete` status _after_ it has been created on your dataset.
|
|
106
|
-
As such, we also hide the cancel button when `percentLoaded === 100`, as cancelling when the asset
|
|
106
|
+
As such, we also hide the cancel button when `percentLoaded === 100`, as cancelling when the asset
|
|
107
107
|
has been fully uploaded (even with a status of `progress`) won't stop the asset from being created.
|
|
108
108
|
*/}
|
|
109
109
|
{!isComplete && percentLoaded !== 100 && (
|
|
@@ -118,27 +118,15 @@ const TableRowUpload = (props: Props) => {
|
|
|
118
118
|
width: '100%'
|
|
119
119
|
}}
|
|
120
120
|
>
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
placement="top"
|
|
131
|
-
>
|
|
132
|
-
<Button
|
|
133
|
-
fontSize={3}
|
|
134
|
-
icon={CloseIcon}
|
|
135
|
-
mode="bleed"
|
|
136
|
-
onClick={handleCancelUpload}
|
|
137
|
-
padding={2}
|
|
138
|
-
style={{background: 'none', boxShadow: 'none'}}
|
|
139
|
-
tone="critical"
|
|
140
|
-
/>
|
|
141
|
-
</Tooltip>
|
|
121
|
+
<Button
|
|
122
|
+
fontSize={3}
|
|
123
|
+
icon={CloseIcon}
|
|
124
|
+
mode="bleed"
|
|
125
|
+
onClick={handleCancelUpload}
|
|
126
|
+
padding={2}
|
|
127
|
+
style={{background: 'none', boxShadow: 'none'}}
|
|
128
|
+
tone="critical"
|
|
129
|
+
/>
|
|
142
130
|
</Flex>
|
|
143
131
|
)}
|
|
144
132
|
</Box>
|
|
@@ -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,
|
|
14
|
+
import {searchActions, selectIsSearchFacetTag} from '../../modules/search'
|
|
15
15
|
|
|
16
16
|
type Props = {
|
|
17
17
|
actions?: TagActions[]
|
|
@@ -44,6 +44,7 @@ type TagButtonProps = {
|
|
|
44
44
|
|
|
45
45
|
const TagButton = (props: TagButtonProps) => {
|
|
46
46
|
const {disabled, icon, onClick, tone, tooltip} = props
|
|
47
|
+
|
|
47
48
|
return (
|
|
48
49
|
<Tooltip
|
|
49
50
|
content={
|
|
@@ -55,6 +56,7 @@ const TagButton = (props: TagButtonProps) => {
|
|
|
55
56
|
}
|
|
56
57
|
disabled={'ontouchstart' in window}
|
|
57
58
|
placement="top"
|
|
59
|
+
portal
|
|
58
60
|
>
|
|
59
61
|
<Button
|
|
60
62
|
disabled={disabled}
|
|
@@ -75,12 +77,11 @@ const Tag = (props: Props) => {
|
|
|
75
77
|
// Redux
|
|
76
78
|
const dispatch = useDispatch()
|
|
77
79
|
const assetsPicked = useTypedSelector(selectAssetsPicked)
|
|
78
|
-
const hasSearchFacetTag = useTypedSelector(selectHasSearchFacetTag)
|
|
79
80
|
const isSearchFacetTag = useTypedSelector(state => selectIsSearchFacetTag(state, tag?.tag?._id))
|
|
80
81
|
|
|
81
82
|
// Callbacks
|
|
82
83
|
const handleSearchFacetTagRemove = () => {
|
|
83
|
-
dispatch(searchActions.
|
|
84
|
+
dispatch(searchActions.facetsRemoveByTag({tagId: tag.tag._id}))
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
const handleShowAddTagToAssetsDialog = () => {
|
|
@@ -108,7 +109,7 @@ const Tag = (props: Props) => {
|
|
|
108
109
|
}
|
|
109
110
|
} as SearchFacetInputSearchableProps
|
|
110
111
|
|
|
111
|
-
if (
|
|
112
|
+
if (isSearchFacetTag) {
|
|
112
113
|
dispatch(
|
|
113
114
|
searchActions.facetsUpdate({
|
|
114
115
|
name: 'tag',
|
|
@@ -13,15 +13,11 @@ type Props = {
|
|
|
13
13
|
title: string
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const TagViewHeader = (
|
|
17
|
-
const {allowCreate, light, title} = props
|
|
18
|
-
|
|
19
|
-
// Redux
|
|
16
|
+
const TagViewHeader = ({allowCreate, light, title}: Props) => {
|
|
20
17
|
const dispatch = useDispatch()
|
|
21
18
|
const tagsCreating = useTypedSelector(state => state.tags.creating)
|
|
22
19
|
const tagsFetching = useTypedSelector(state => state.tags.fetching)
|
|
23
20
|
|
|
24
|
-
// Callbacks
|
|
25
21
|
const handleTagCreate = () => {
|
|
26
22
|
dispatch(DIALOG_ACTIONS.showTagCreate())
|
|
27
23
|
}
|
|
@@ -35,6 +31,7 @@ const TagViewHeader = (props: Props) => {
|
|
|
35
31
|
style={{
|
|
36
32
|
background: light ? hues.gray?.[900].hex : black.hex,
|
|
37
33
|
borderBottom: `1px solid ${hues.gray?.[900].hex}`,
|
|
34
|
+
flexShrink: 0,
|
|
38
35
|
height: `${PANEL_HEIGHT}px`
|
|
39
36
|
}}
|
|
40
37
|
>
|
package/src/hooks/useKeyPress.ts
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import isHotkey from 'is-hotkey'
|
|
2
|
-
import {RefObject, useEffect, useRef} from 'react'
|
|
2
|
+
import {RefObject, useCallback, useEffect, useRef} from 'react'
|
|
3
3
|
|
|
4
4
|
const useKeyPress = (hotkey: string, onPress?: () => void): RefObject<boolean> => {
|
|
5
5
|
const keyPressed = useRef(false)
|
|
6
6
|
|
|
7
7
|
// If pressed key is our target key then set to true
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const downHandler = useCallback(
|
|
9
|
+
(e: KeyboardEvent) => {
|
|
10
|
+
if (isHotkey(hotkey, e)) {
|
|
11
|
+
keyPressed.current = true
|
|
12
|
+
if (onPress) {
|
|
13
|
+
onPress()
|
|
14
|
+
}
|
|
14
15
|
}
|
|
15
|
-
}
|
|
16
|
-
|
|
16
|
+
},
|
|
17
|
+
[hotkey, onPress]
|
|
18
|
+
)
|
|
17
19
|
|
|
18
20
|
// If released key is our target key then set to false
|
|
19
|
-
const upHandler = () => {
|
|
21
|
+
const upHandler = useCallback(() => {
|
|
20
22
|
keyPressed.current = false
|
|
21
|
-
}
|
|
23
|
+
}, [])
|
|
22
24
|
|
|
23
25
|
// Add event listeners
|
|
24
26
|
useEffect(() => {
|
|
@@ -29,7 +31,7 @@ const useKeyPress = (hotkey: string, onPress?: () => void): RefObject<boolean> =
|
|
|
29
31
|
window.removeEventListener('keydown', downHandler)
|
|
30
32
|
window.removeEventListener('keyup', upHandler)
|
|
31
33
|
}
|
|
32
|
-
}, [])
|
|
34
|
+
}, [downHandler, upHandler])
|
|
33
35
|
|
|
34
36
|
return keyPressed
|
|
35
37
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {PopoverProps, usePortal} from '@sanity/ui'
|
|
2
|
+
|
|
3
|
+
export function usePortalPopoverProps(): PopoverProps {
|
|
4
|
+
const portal = usePortal()
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
constrainSize: true,
|
|
8
|
+
floatingBoundary: portal.element,
|
|
9
|
+
portal: true,
|
|
10
|
+
referenceBoundary: portal.element
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -575,8 +575,11 @@ export const assetsSearchEpic: MyEpic = action$ =>
|
|
|
575
575
|
ofType(
|
|
576
576
|
searchActions.facetsAdd.type,
|
|
577
577
|
searchActions.facetsClear.type,
|
|
578
|
-
searchActions.
|
|
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.
|
|
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(() => {
|
package/src/modules/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ActionFromReducersMapObject,
|
|
3
|
+
Reducer,
|
|
4
|
+
StateFromReducersMapObject,
|
|
5
|
+
combineReducers
|
|
6
|
+
} from '@reduxjs/toolkit'
|
|
2
7
|
import {combineEpics} from 'redux-observable'
|
|
3
8
|
|
|
4
9
|
import assetsReducer, {
|
|
@@ -97,7 +102,7 @@ export const rootEpic = combineEpics(
|
|
|
97
102
|
uploadsCompleteQueueEpic
|
|
98
103
|
)
|
|
99
104
|
|
|
100
|
-
const reducers =
|
|
105
|
+
const reducers = {
|
|
101
106
|
assets: assetsReducer,
|
|
102
107
|
debug: debugReducer,
|
|
103
108
|
dialog: dialogReducer,
|
|
@@ -106,5 +111,14 @@ const reducers = combineReducers({
|
|
|
106
111
|
selected: selectedReducer,
|
|
107
112
|
tags: tagsReducer,
|
|
108
113
|
uploads: uploadsReducer
|
|
109
|
-
}
|
|
110
|
-
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type ReducersMapObject = typeof reducers
|
|
117
|
+
|
|
118
|
+
// Workaround to avoid `$CombinedState` ts errors
|
|
119
|
+
// source: https://github.com/reduxjs/redux-toolkit/issues/2068#issuecomment-1130796500
|
|
120
|
+
// TODO: remove once we use `redux-toolkit` v2
|
|
121
|
+
export const rootReducer: Reducer<
|
|
122
|
+
StateFromReducersMapObject<ReducersMapObject>,
|
|
123
|
+
ActionFromReducersMapObject<ReducersMapObject>
|
|
124
|
+
> = combineReducers(reducers)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {createSelector, createSlice
|
|
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
|
-
|
|
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.
|
|
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
|
-
(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|