sanity-plugin-media 4.3.5 → 5.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/dist/index.d.ts +190 -413
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2532 -3564
- package/dist/index.js.map +1 -1
- package/package.json +29 -35
- package/dist/index.cjs +0 -5753
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -462
- package/sanity.json +0 -8
- package/src/__tests__/fixtures/createEpicTestStore.ts +0 -28
- package/src/__tests__/fixtures/listenMock.ts +0 -9
- package/src/__tests__/fixtures/mockSanityClient.ts +0 -84
- package/src/__tests__/fixtures/renderWithProviders.tsx +0 -55
- package/src/__tests__/fixtures/rootState.ts +0 -27
- package/src/__tests__/fixtures/withinDialog.ts +0 -28
- package/src/components/AssetGridVirtualized/index.tsx +0 -94
- package/src/components/AssetMetadata/index.tsx +0 -122
- package/src/components/AssetTableVirtualized/index.tsx +0 -73
- package/src/components/AutoTagInputWrapper/index.tsx +0 -87
- package/src/components/Browser/Browser.test.tsx +0 -45
- package/src/components/Browser/index.tsx +0 -90
- package/src/components/Browser/useBrowserInit.ts +0 -126
- package/src/components/ButtonAssetCopy/index.tsx +0 -65
- package/src/components/ButtonViewGroup/index.tsx +0 -39
- package/src/components/CardAsset/CardAsset.test.tsx +0 -323
- package/src/components/CardAsset/index.tsx +0 -290
- package/src/components/CardUpload/index.tsx +0 -161
- package/src/components/Controls/index.tsx +0 -136
- package/src/components/DebugControls/index.tsx +0 -80
- package/src/components/Dialog/index.tsx +0 -11
- package/src/components/DialogAssetEdit/Details.tsx +0 -181
- package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +0 -216
- package/src/components/DialogAssetEdit/index.tsx +0 -492
- package/src/components/DialogConfirm/index.tsx +0 -88
- package/src/components/DialogSearchFacets/index.tsx +0 -42
- package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +0 -121
- package/src/components/DialogTagCreate/index.tsx +0 -103
- package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +0 -165
- package/src/components/DialogTagEdit/index.tsx +0 -190
- package/src/components/DialogTags/index.tsx +0 -45
- package/src/components/Dialogs/index.tsx +0 -76
- package/src/components/DocumentList/index.tsx +0 -62
- package/src/components/FileAssetPreview/index.tsx +0 -37
- package/src/components/FileIcon/index.tsx +0 -43
- package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +0 -63
- package/src/components/FormBuilderTool/index.tsx +0 -69
- package/src/components/FormFieldInputLabel/index.tsx +0 -66
- package/src/components/FormFieldInputTags/index.tsx +0 -98
- package/src/components/FormFieldInputText/index.tsx +0 -41
- package/src/components/FormFieldInputTextarea/index.tsx +0 -43
- package/src/components/FormSubmitButton/index.tsx +0 -59
- package/src/components/Header/index.tsx +0 -80
- package/src/components/Image/index.tsx +0 -41
- package/src/components/Items/index.tsx +0 -68
- package/src/components/Notifications/index.tsx +0 -24
- package/src/components/OrderSelect/index.tsx +0 -66
- package/src/components/PickedBar/index.tsx +0 -77
- package/src/components/Progress/index.tsx +0 -38
- package/src/components/ReduxProvider/index.tsx +0 -96
- package/src/components/SearchFacet/index.tsx +0 -66
- package/src/components/SearchFacetNumber/index.tsx +0 -133
- package/src/components/SearchFacetSelect/index.tsx +0 -110
- package/src/components/SearchFacetString/index.tsx +0 -88
- package/src/components/SearchFacetTags/index.tsx +0 -121
- package/src/components/SearchFacets/index.tsx +0 -72
- package/src/components/SearchFacetsControl/index.tsx +0 -140
- package/src/components/TableHeader/index.tsx +0 -110
- package/src/components/TableHeaderItem/index.tsx +0 -61
- package/src/components/TableRowAsset/index.tsx +0 -420
- package/src/components/TableRowUpload/index.tsx +0 -164
- package/src/components/Tag/index.tsx +0 -200
- package/src/components/TagIcon/index.tsx +0 -22
- package/src/components/TagView/index.tsx +0 -39
- package/src/components/TagViewHeader/index.tsx +0 -70
- package/src/components/TagsPanel/index.tsx +0 -40
- package/src/components/TagsVirtualized/index.tsx +0 -160
- package/src/components/TextInputNumber/index.tsx +0 -32
- package/src/components/TextInputSearch/index.tsx +0 -60
- package/src/components/Tool/index.tsx +0 -13
- package/src/components/UploadDropzone/UploadDropzone.test.tsx +0 -40
- package/src/components/UploadDropzone/index.tsx +0 -173
- package/src/config/orders.ts +0 -28
- package/src/config/searchFacets.ts +0 -312
- package/src/constants.ts +0 -87
- package/src/contexts/AssetSourceDispatchContext.tsx +0 -37
- package/src/contexts/DropzoneDispatchContext.tsx +0 -34
- package/src/contexts/ToolOptionsContext.tsx +0 -65
- package/src/formSchema/index.test.ts +0 -56
- package/src/formSchema/index.ts +0 -39
- package/src/hooks/useBreakpointIndex.ts +0 -49
- package/src/hooks/useKeyPress.ts +0 -39
- package/src/hooks/useOnScreen.ts +0 -34
- package/src/hooks/usePortalPopoverProps.ts +0 -13
- package/src/hooks/useTypedSelector.ts +0 -7
- package/src/hooks/useVersionedClient.ts +0 -6
- package/src/index.ts +0 -5
- package/src/modules/assets/actions.ts +0 -42
- package/src/modules/assets/deleteAndUpdateEpics.test.ts +0 -87
- package/src/modules/assets/fetchEpic.test.ts +0 -73
- package/src/modules/assets/index.ts +0 -825
- package/src/modules/assets/reducer.test.ts +0 -91
- package/src/modules/assets/tagsAndListenerEpics.test.ts +0 -206
- package/src/modules/debug/index.ts +0 -28
- package/src/modules/dialog/actions.ts +0 -10
- package/src/modules/dialog/epics.test.ts +0 -168
- package/src/modules/dialog/index.ts +0 -238
- package/src/modules/dialog/reducer.test.ts +0 -185
- package/src/modules/index.ts +0 -124
- package/src/modules/notifications/epics.test.ts +0 -374
- package/src/modules/notifications/index.ts +0 -199
- package/src/modules/notifications/reducer.test.ts +0 -54
- package/src/modules/search/index.test.ts +0 -36
- package/src/modules/search/index.ts +0 -167
- package/src/modules/selected/index.ts +0 -22
- package/src/modules/selectors.test.ts +0 -21
- package/src/modules/selectors.ts +0 -17
- package/src/modules/tags/epics.test.ts +0 -96
- package/src/modules/tags/index.test.ts +0 -42
- package/src/modules/tags/index.ts +0 -540
- package/src/modules/types.ts +0 -3
- package/src/modules/uploads/actions.ts +0 -13
- package/src/modules/uploads/epics.test.ts +0 -109
- package/src/modules/uploads/index.test.ts +0 -59
- package/src/modules/uploads/index.ts +0 -282
- package/src/operators/checkTagName.test.ts +0 -29
- package/src/operators/checkTagName.ts +0 -33
- package/src/operators/debugThrottle.ts +0 -25
- package/src/plugin.tsx +0 -54
- package/src/schemas/tag.ts +0 -28
- package/src/styled/GlobalStyles/index.tsx +0 -40
- package/src/styled/react-select/creatable.tsx +0 -184
- package/src/styled/react-select/single.tsx +0 -184
- package/src/types/index.ts +0 -379
- package/src/types/sanity-ui.d.ts +0 -6
- package/src/utils/applyMediaTags.ts +0 -87
- package/src/utils/blocksToText.test.ts +0 -43
- package/src/utils/blocksToText.ts +0 -27
- package/src/utils/constructFilter.test.ts +0 -120
- package/src/utils/constructFilter.ts +0 -98
- package/src/utils/generatePreviewBlobUrl.test.ts +0 -70
- package/src/utils/generatePreviewBlobUrl.ts +0 -53
- package/src/utils/getAssetResolution.test.ts +0 -13
- package/src/utils/getAssetResolution.ts +0 -7
- package/src/utils/getDocumentAssetIds.test.ts +0 -50
- package/src/utils/getDocumentAssetIds.ts +0 -35
- package/src/utils/getSchemeColor.test.ts +0 -12
- package/src/utils/getSchemeColor.ts +0 -43
- package/src/utils/getTagSelectOptions.test.ts +0 -44
- package/src/utils/getTagSelectOptions.ts +0 -16
- package/src/utils/getUniqueDocuments.test.ts +0 -26
- package/src/utils/getUniqueDocuments.ts +0 -15
- package/src/utils/imageDprUrl.test.ts +0 -46
- package/src/utils/imageDprUrl.ts +0 -27
- package/src/utils/isSupportedAssetType.test.ts +0 -16
- package/src/utils/isSupportedAssetType.ts +0 -15
- package/src/utils/mediaField.ts +0 -73
- package/src/utils/sanitizeFormData.test.ts +0 -59
- package/src/utils/sanitizeFormData.ts +0 -26
- package/src/utils/typeGuards.test.ts +0 -18
- package/src/utils/typeGuards.ts +0 -9
- package/src/utils/uploadSanityAsset.test.ts +0 -29
- package/src/utils/uploadSanityAsset.ts +0 -97
- package/src/utils/withMaxConcurrency.test.ts +0 -43
- package/src/utils/withMaxConcurrency.ts +0 -55
- package/src/utils/zodFormResolver.ts +0 -17
- package/v2-incompatible.js +0 -11
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import {ArrowDownIcon, ArrowUpIcon, CloseIcon, EditIcon, SearchIcon, TrashIcon} from '@sanity/icons'
|
|
2
|
-
import {Box, Button, Container, Flex, Text, Tooltip} from '@sanity/ui'
|
|
3
|
-
import {type ReactNode} from 'react'
|
|
4
|
-
import {useDispatch} from 'react-redux'
|
|
5
|
-
import {styled} from 'styled-components'
|
|
6
|
-
|
|
7
|
-
import {inputs} from '../../config/searchFacets'
|
|
8
|
-
import {PANEL_HEIGHT} from '../../constants'
|
|
9
|
-
import useTypedSelector from '../../hooks/useTypedSelector'
|
|
10
|
-
import {selectAssetsPicked} from '../../modules/assets'
|
|
11
|
-
import {dialogActions} from '../../modules/dialog'
|
|
12
|
-
import {DIALOG_ACTIONS} from '../../modules/dialog/actions'
|
|
13
|
-
import {searchActions, selectIsSearchFacetTag} from '../../modules/search'
|
|
14
|
-
import type {SearchFacetInputSearchableProps, TagActions, TagItem} from '../../types'
|
|
15
|
-
|
|
16
|
-
type Props = {
|
|
17
|
-
actions?: TagActions[]
|
|
18
|
-
tag: TagItem
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const TagContainer = styled(Flex)`
|
|
22
|
-
height: ${PANEL_HEIGHT}px;
|
|
23
|
-
`
|
|
24
|
-
|
|
25
|
-
const ButtonContainer = styled(Flex)`
|
|
26
|
-
@media (pointer: fine) {
|
|
27
|
-
visibility: hidden;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@media (hover: hover) and (pointer: fine) {
|
|
31
|
-
${TagContainer}:hover & {
|
|
32
|
-
visibility: visible;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
`
|
|
36
|
-
|
|
37
|
-
type TagButtonProps = {
|
|
38
|
-
disabled?: boolean
|
|
39
|
-
icon: ReactNode
|
|
40
|
-
onClick: () => void
|
|
41
|
-
tone?: 'critical' | 'primary'
|
|
42
|
-
tooltip: string
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const TagButton = (props: TagButtonProps) => {
|
|
46
|
-
const {disabled, icon, onClick, tone, tooltip} = props
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<Tooltip
|
|
50
|
-
animate
|
|
51
|
-
content={
|
|
52
|
-
<Container padding={2} width={0}>
|
|
53
|
-
<Text muted size={1}>
|
|
54
|
-
{tooltip}
|
|
55
|
-
</Text>
|
|
56
|
-
</Container>
|
|
57
|
-
}
|
|
58
|
-
disabled={'ontouchstart' in window}
|
|
59
|
-
placement="top"
|
|
60
|
-
portal
|
|
61
|
-
>
|
|
62
|
-
<Button
|
|
63
|
-
disabled={disabled}
|
|
64
|
-
fontSize={1}
|
|
65
|
-
icon={icon}
|
|
66
|
-
mode="bleed"
|
|
67
|
-
onClick={onClick}
|
|
68
|
-
padding={2}
|
|
69
|
-
tone={tone}
|
|
70
|
-
/>
|
|
71
|
-
</Tooltip>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const Tag = (props: Props) => {
|
|
76
|
-
const {actions, tag} = props
|
|
77
|
-
|
|
78
|
-
// Redux
|
|
79
|
-
const dispatch = useDispatch()
|
|
80
|
-
const assetsPicked = useTypedSelector(selectAssetsPicked)
|
|
81
|
-
const isSearchFacetTag = useTypedSelector((state) => selectIsSearchFacetTag(state, tag?.tag?._id))
|
|
82
|
-
|
|
83
|
-
// Callbacks
|
|
84
|
-
const handleSearchFacetTagRemove = () => {
|
|
85
|
-
dispatch(searchActions.facetsRemoveByTag({tagId: tag.tag._id}))
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const handleShowAddTagToAssetsDialog = () => {
|
|
89
|
-
dispatch(dialogActions.showConfirmAssetsTagAdd({assetsPicked, tag: tag.tag}))
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const handleShowRemoveTagFromAssetsDialog = () => {
|
|
93
|
-
dispatch(dialogActions.showConfirmAssetsTagRemove({assetsPicked, tag: tag.tag}))
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const handleShowTagDeleteDialog = () => {
|
|
97
|
-
dispatch(dialogActions.showConfirmDeleteTag({tag: tag.tag}))
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const handleShowTagEditDialog = () => {
|
|
101
|
-
dispatch(DIALOG_ACTIONS.showTagEdit({tagId: tag?.tag?._id}))
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const handleSearchFacetTagAddOrUpdate = () => {
|
|
105
|
-
const searchFacet = {
|
|
106
|
-
...inputs.tag,
|
|
107
|
-
value: {
|
|
108
|
-
label: tag?.tag?.name?.current,
|
|
109
|
-
value: tag?.tag?._id,
|
|
110
|
-
},
|
|
111
|
-
} as SearchFacetInputSearchableProps
|
|
112
|
-
|
|
113
|
-
if (isSearchFacetTag) {
|
|
114
|
-
dispatch(
|
|
115
|
-
searchActions.facetsUpdate({
|
|
116
|
-
name: 'tag',
|
|
117
|
-
operatorType: 'references',
|
|
118
|
-
value: searchFacet.value,
|
|
119
|
-
}),
|
|
120
|
-
)
|
|
121
|
-
} else {
|
|
122
|
-
dispatch(searchActions.facetsAdd({facet: searchFacet}))
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<TagContainer align="center" flex={1} justify="space-between" paddingLeft={3}>
|
|
128
|
-
<Box flex={1}>
|
|
129
|
-
<Text
|
|
130
|
-
muted
|
|
131
|
-
size={1}
|
|
132
|
-
style={{
|
|
133
|
-
opacity: tag?.updating ? 0.5 : 1.0,
|
|
134
|
-
userSelect: 'none',
|
|
135
|
-
}}
|
|
136
|
-
textOverflow="ellipsis"
|
|
137
|
-
>
|
|
138
|
-
{tag?.tag?.name?.current}
|
|
139
|
-
</Text>
|
|
140
|
-
</Box>
|
|
141
|
-
|
|
142
|
-
<ButtonContainer align="center" style={{flexShrink: 0}}>
|
|
143
|
-
{/* Search facet toggle */}
|
|
144
|
-
{actions?.includes('search') && (
|
|
145
|
-
<TagButton
|
|
146
|
-
disabled={tag?.updating}
|
|
147
|
-
icon={isSearchFacetTag ? <CloseIcon /> : <SearchIcon />}
|
|
148
|
-
onClick={
|
|
149
|
-
isSearchFacetTag ? handleSearchFacetTagRemove : handleSearchFacetTagAddOrUpdate
|
|
150
|
-
}
|
|
151
|
-
tooltip={isSearchFacetTag ? 'Remove filter' : 'Filter by tag'}
|
|
152
|
-
/>
|
|
153
|
-
)}
|
|
154
|
-
{/* Edit icon */}
|
|
155
|
-
{actions?.includes('edit') && (
|
|
156
|
-
<TagButton
|
|
157
|
-
disabled={tag?.updating}
|
|
158
|
-
icon={<EditIcon />}
|
|
159
|
-
onClick={handleShowTagEditDialog}
|
|
160
|
-
tone="primary"
|
|
161
|
-
tooltip="Edit tag"
|
|
162
|
-
/>
|
|
163
|
-
)}
|
|
164
|
-
{/* Apply to all */}
|
|
165
|
-
{actions?.includes('applyAll') && (
|
|
166
|
-
<TagButton
|
|
167
|
-
disabled={tag?.updating}
|
|
168
|
-
icon={<ArrowUpIcon />}
|
|
169
|
-
onClick={handleShowAddTagToAssetsDialog}
|
|
170
|
-
tone="primary"
|
|
171
|
-
tooltip="Add tag to selected assets"
|
|
172
|
-
/>
|
|
173
|
-
)}
|
|
174
|
-
{/* Remove from all */}
|
|
175
|
-
{actions?.includes('removeAll') && (
|
|
176
|
-
<TagButton
|
|
177
|
-
disabled={tag?.updating}
|
|
178
|
-
icon={<ArrowDownIcon />}
|
|
179
|
-
onClick={handleShowRemoveTagFromAssetsDialog}
|
|
180
|
-
tone="critical"
|
|
181
|
-
tooltip="Remove tag from selected assets"
|
|
182
|
-
/>
|
|
183
|
-
)}
|
|
184
|
-
|
|
185
|
-
{/* Delete icon */}
|
|
186
|
-
{actions?.includes('delete') && (
|
|
187
|
-
<TagButton
|
|
188
|
-
disabled={tag?.updating}
|
|
189
|
-
icon={<TrashIcon />}
|
|
190
|
-
onClick={handleShowTagDeleteDialog}
|
|
191
|
-
tone="critical"
|
|
192
|
-
tooltip="Delete tag"
|
|
193
|
-
/>
|
|
194
|
-
)}
|
|
195
|
-
</ButtonContainer>
|
|
196
|
-
</TagContainer>
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export default Tag
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const TagIcon = () => (
|
|
2
|
-
<svg
|
|
3
|
-
data-sanity-icon="media__tag" // For icon usage with @sanity/ui <Button />
|
|
4
|
-
fill="currentColor"
|
|
5
|
-
height="1em"
|
|
6
|
-
stroke="currentColor"
|
|
7
|
-
strokeWidth="0"
|
|
8
|
-
viewBox="0 0 512 512"
|
|
9
|
-
width="1em"
|
|
10
|
-
>
|
|
11
|
-
<path
|
|
12
|
-
d="M435.25 48h-122.9a14.46 14.46 0 00-10.2 4.2L56.45 297.9a28.85 28.85 0 000 40.7l117 117a28.85 28.85 0 0040.7 0L459.75 210a14.46 14.46 0 004.2-10.2v-123a28.66 28.66 0 00-28.7-28.8z"
|
|
13
|
-
fill="none"
|
|
14
|
-
strokeLinecap="round"
|
|
15
|
-
strokeLinejoin="round"
|
|
16
|
-
strokeWidth="32"
|
|
17
|
-
/>
|
|
18
|
-
<path d="M384 160a32 32 0 1132-32 32 32 0 01-32 32z" />
|
|
19
|
-
</svg>
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
export default TagIcon
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import {Box, Flex, Text} from '@sanity/ui'
|
|
2
|
-
|
|
3
|
-
import useTypedSelector from '../../hooks/useTypedSelector'
|
|
4
|
-
import {selectAssetsPickedLength} from '../../modules/assets'
|
|
5
|
-
import {selectTags} from '../../modules/tags'
|
|
6
|
-
import TagsVirtualized from '../TagsVirtualized'
|
|
7
|
-
import TagViewHeader from '../TagViewHeader'
|
|
8
|
-
|
|
9
|
-
const TagView = () => {
|
|
10
|
-
const numPickedAssets = useTypedSelector(selectAssetsPickedLength)
|
|
11
|
-
const tags = useTypedSelector(selectTags)
|
|
12
|
-
const fetching = useTypedSelector((state) => state.tags.fetching)
|
|
13
|
-
const fetchCount = useTypedSelector((state) => state.tags.fetchCount)
|
|
14
|
-
const fetchComplete = fetchCount !== -1
|
|
15
|
-
const hasTags = !fetching && tags?.length > 0
|
|
16
|
-
const hasPicked = !!(numPickedAssets > 0)
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Flex direction="column" flex={1} height="fill">
|
|
20
|
-
<TagViewHeader
|
|
21
|
-
allowCreate
|
|
22
|
-
light={hasPicked}
|
|
23
|
-
title={hasPicked ? 'Tags (in selection)' : 'Tags'}
|
|
24
|
-
/>
|
|
25
|
-
|
|
26
|
-
{fetchComplete && !hasTags && (
|
|
27
|
-
<Box padding={3}>
|
|
28
|
-
<Text muted size={1}>
|
|
29
|
-
<em>No tags</em>
|
|
30
|
-
</Text>
|
|
31
|
-
</Box>
|
|
32
|
-
)}
|
|
33
|
-
|
|
34
|
-
{hasTags && <TagsVirtualized />}
|
|
35
|
-
</Flex>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export default TagView
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import {ComposeIcon} from '@sanity/icons'
|
|
2
|
-
import {Box, Button, Flex, Inline, Label} from '@sanity/ui'
|
|
3
|
-
import {useDispatch} from 'react-redux'
|
|
4
|
-
import {useColorSchemeValue} from 'sanity'
|
|
5
|
-
|
|
6
|
-
import {PANEL_HEIGHT} from '../../constants'
|
|
7
|
-
import useTypedSelector from '../../hooks/useTypedSelector'
|
|
8
|
-
import {DIALOG_ACTIONS} from '../../modules/dialog/actions'
|
|
9
|
-
import {getSchemeColor} from '../../utils/getSchemeColor'
|
|
10
|
-
|
|
11
|
-
type Props = {
|
|
12
|
-
allowCreate?: boolean
|
|
13
|
-
light?: boolean
|
|
14
|
-
title: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const TagViewHeader = ({allowCreate, light, title}: Props) => {
|
|
18
|
-
const scheme = useColorSchemeValue()
|
|
19
|
-
|
|
20
|
-
const dispatch = useDispatch()
|
|
21
|
-
const tagsCreating = useTypedSelector((state) => state.tags.creating)
|
|
22
|
-
const tagsFetching = useTypedSelector((state) => state.tags.fetching)
|
|
23
|
-
|
|
24
|
-
const handleTagCreate = () => {
|
|
25
|
-
dispatch(DIALOG_ACTIONS.showTagCreate())
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<>
|
|
30
|
-
<Flex
|
|
31
|
-
align="center"
|
|
32
|
-
justify="space-between"
|
|
33
|
-
paddingLeft={3}
|
|
34
|
-
style={{
|
|
35
|
-
background: light ? getSchemeColor(scheme, 'bg') : 'inherit',
|
|
36
|
-
borderBottom: '1px solid var(--card-border-color)',
|
|
37
|
-
flexShrink: 0,
|
|
38
|
-
height: `${PANEL_HEIGHT}px`,
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
<Inline space={2}>
|
|
42
|
-
<Label size={0}>{title}</Label>
|
|
43
|
-
{tagsFetching && (
|
|
44
|
-
<Label size={0} style={{opacity: 0.3}}>
|
|
45
|
-
Loading...
|
|
46
|
-
</Label>
|
|
47
|
-
)}
|
|
48
|
-
</Inline>
|
|
49
|
-
{/* Create new tag button */}
|
|
50
|
-
{allowCreate && (
|
|
51
|
-
<Box marginRight={1}>
|
|
52
|
-
<Button
|
|
53
|
-
disabled={tagsCreating}
|
|
54
|
-
fontSize={1} //
|
|
55
|
-
icon={ComposeIcon}
|
|
56
|
-
mode="bleed"
|
|
57
|
-
onClick={handleTagCreate}
|
|
58
|
-
style={{
|
|
59
|
-
background: 'transparent',
|
|
60
|
-
boxShadow: 'none',
|
|
61
|
-
}}
|
|
62
|
-
/>
|
|
63
|
-
</Box>
|
|
64
|
-
)}
|
|
65
|
-
</Flex>
|
|
66
|
-
</>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export default TagViewHeader
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {Box} from '@sanity/ui'
|
|
2
|
-
|
|
3
|
-
import {TAGS_PANEL_WIDTH} from '../../constants'
|
|
4
|
-
import useTypedSelector from '../../hooks/useTypedSelector'
|
|
5
|
-
import TagView from '../TagView'
|
|
6
|
-
|
|
7
|
-
const TagsPanel = () => {
|
|
8
|
-
const tagsPanelVisible = useTypedSelector((state) => state.tags.panelVisible)
|
|
9
|
-
|
|
10
|
-
if (!tagsPanelVisible) {
|
|
11
|
-
return null
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<Box
|
|
16
|
-
style={{
|
|
17
|
-
position: 'relative',
|
|
18
|
-
width: TAGS_PANEL_WIDTH,
|
|
19
|
-
}}
|
|
20
|
-
>
|
|
21
|
-
<Box
|
|
22
|
-
className="media__custom-scrollbar"
|
|
23
|
-
style={{
|
|
24
|
-
borderLeft: '1px solid var(--card-border-color)',
|
|
25
|
-
height: '100%',
|
|
26
|
-
overflowX: 'hidden',
|
|
27
|
-
overflowY: 'auto',
|
|
28
|
-
position: 'absolute',
|
|
29
|
-
right: 0,
|
|
30
|
-
top: 0,
|
|
31
|
-
width: '100%',
|
|
32
|
-
}}
|
|
33
|
-
>
|
|
34
|
-
<TagView />
|
|
35
|
-
</Box>
|
|
36
|
-
</Box>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default TagsPanel
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import {Flex, Label} from '@sanity/ui'
|
|
2
|
-
import {memo, useState} from 'react'
|
|
3
|
-
import {Virtuoso} from 'react-virtuoso'
|
|
4
|
-
|
|
5
|
-
import {PANEL_HEIGHT} from '../../constants'
|
|
6
|
-
import useTypedSelector from '../../hooks/useTypedSelector'
|
|
7
|
-
import {selectAssetsPicked} from '../../modules/assets'
|
|
8
|
-
import {selectTags} from '../../modules/tags'
|
|
9
|
-
import type {TagActions, TagItem} from '../../types'
|
|
10
|
-
import Tag from '../Tag'
|
|
11
|
-
|
|
12
|
-
const VirtualRow = memo(
|
|
13
|
-
({
|
|
14
|
-
isScrolling,
|
|
15
|
-
item,
|
|
16
|
-
}: {
|
|
17
|
-
isScrolling?: boolean
|
|
18
|
-
item:
|
|
19
|
-
| string
|
|
20
|
-
| (TagItem & {
|
|
21
|
-
actions: TagActions[]
|
|
22
|
-
})
|
|
23
|
-
}) => {
|
|
24
|
-
// Render label
|
|
25
|
-
if (typeof item === 'string') {
|
|
26
|
-
return (
|
|
27
|
-
<Flex
|
|
28
|
-
align="center"
|
|
29
|
-
justify="space-between"
|
|
30
|
-
key={item}
|
|
31
|
-
paddingX={3}
|
|
32
|
-
style={{height: `${PANEL_HEIGHT}px`}}
|
|
33
|
-
>
|
|
34
|
-
<Label size={0}>{item}</Label>
|
|
35
|
-
</Flex>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Render tag - only display actions if we're not in the process of scrolling
|
|
40
|
-
return <Tag actions={isScrolling ? undefined : item.actions} key={item.tag?._id} tag={item} />
|
|
41
|
-
},
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
const TagsVirtualized = () => {
|
|
45
|
-
const assetsPicked = useTypedSelector(selectAssetsPicked)
|
|
46
|
-
const tags = useTypedSelector(selectTags)
|
|
47
|
-
|
|
48
|
-
// State
|
|
49
|
-
const [isScrolling, setIsScrolling] = useState(false)
|
|
50
|
-
|
|
51
|
-
// TODO: refactor, there's most certainly a more performant way to do this
|
|
52
|
-
|
|
53
|
-
// Filter out all tag IDS used (across all) and dedupe
|
|
54
|
-
const pickedTagIds = assetsPicked?.reduce((acc: string[], val) => {
|
|
55
|
-
const assetTagIds = val?.asset?.opt?.media?.tags?.map((tag) => tag._ref) || []
|
|
56
|
-
acc = acc.concat(assetTagIds)
|
|
57
|
-
return acc
|
|
58
|
-
}, [])
|
|
59
|
-
const pickedTagIdsUnique = [...new Set(pickedTagIds)]
|
|
60
|
-
|
|
61
|
-
// Segment tags into two buckets:
|
|
62
|
-
// 1. those which exist in all picked assets ('applied to all')
|
|
63
|
-
// 2. those which exist in some picked assets ('applied to some')
|
|
64
|
-
const tagIdsSegmented = pickedTagIdsUnique.reduce(
|
|
65
|
-
(acc: {appliedToAll: string[]; appliedToSome: string[]}, tagId) => {
|
|
66
|
-
const tagIsInEveryAsset = assetsPicked.every((assetItem) => {
|
|
67
|
-
const tagIndex =
|
|
68
|
-
assetItem.asset.opt?.media?.tags?.findIndex((tag) => tag._ref === tagId) ?? -1
|
|
69
|
-
return tagIndex >= 0
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
if (tagIsInEveryAsset) {
|
|
73
|
-
acc.appliedToAll.push(tagId)
|
|
74
|
-
} else {
|
|
75
|
-
acc.appliedToSome.push(tagId)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return acc
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
appliedToAll: [],
|
|
82
|
-
appliedToSome: [],
|
|
83
|
-
},
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
const tagsAppliedToAll = tags
|
|
87
|
-
.filter((tag) => tagIdsSegmented.appliedToAll.includes(tag.tag._id))
|
|
88
|
-
.map((tagItem) => ({
|
|
89
|
-
...tagItem,
|
|
90
|
-
actions: ['delete', 'edit', 'removeAll', 'search'] as TagActions[],
|
|
91
|
-
}))
|
|
92
|
-
const tagsAppliedToSome = tags
|
|
93
|
-
.filter((tag) => tagIdsSegmented.appliedToSome.includes(tag.tag._id))
|
|
94
|
-
.map((tagItem) => ({
|
|
95
|
-
...tagItem,
|
|
96
|
-
actions: ['applyAll', 'delete', 'edit', 'removeAll', 'search'] as TagActions[],
|
|
97
|
-
}))
|
|
98
|
-
const tagsUnused = tags
|
|
99
|
-
.filter((tag) => !pickedTagIdsUnique.includes(tag.tag._id))
|
|
100
|
-
.map((tagItem) => ({
|
|
101
|
-
...tagItem,
|
|
102
|
-
actions: ['applyAll', 'delete', 'edit', 'search'] as TagActions[],
|
|
103
|
-
}))
|
|
104
|
-
|
|
105
|
-
let items: (
|
|
106
|
-
| string
|
|
107
|
-
| (TagItem & {
|
|
108
|
-
actions: TagActions[]
|
|
109
|
-
})
|
|
110
|
-
)[] = []
|
|
111
|
-
if (assetsPicked.length === 0) {
|
|
112
|
-
items = tags.map((tagItem) => ({
|
|
113
|
-
...tagItem,
|
|
114
|
-
actions: ['delete', 'edit', 'search'] as TagActions[],
|
|
115
|
-
}))
|
|
116
|
-
} else {
|
|
117
|
-
if (tagsAppliedToAll?.length > 0) {
|
|
118
|
-
items = [
|
|
119
|
-
...items, //
|
|
120
|
-
assetsPicked.length === 1 ? 'Used' : 'Used by all',
|
|
121
|
-
...tagsAppliedToAll,
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
|
-
if (tagsAppliedToSome?.length > 0) {
|
|
125
|
-
items = [
|
|
126
|
-
...items, //
|
|
127
|
-
'Used by some',
|
|
128
|
-
...tagsAppliedToSome,
|
|
129
|
-
]
|
|
130
|
-
}
|
|
131
|
-
if (tagsUnused?.length > 0) {
|
|
132
|
-
items = [
|
|
133
|
-
...items, //
|
|
134
|
-
'Unused',
|
|
135
|
-
...tagsUnused,
|
|
136
|
-
]
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<Virtuoso
|
|
142
|
-
className="media__custom-scrollbar"
|
|
143
|
-
computeItemKey={(index) => {
|
|
144
|
-
const item = items[index]
|
|
145
|
-
if (typeof item === 'string') {
|
|
146
|
-
return item
|
|
147
|
-
}
|
|
148
|
-
return item.tag._id
|
|
149
|
-
}}
|
|
150
|
-
isScrolling={setIsScrolling}
|
|
151
|
-
itemContent={(index) => {
|
|
152
|
-
return <VirtualRow isScrolling={isScrolling} item={items[index]} />
|
|
153
|
-
}}
|
|
154
|
-
style={{flex: 1, overflowX: 'hidden'}}
|
|
155
|
-
totalCount={items.length}
|
|
156
|
-
/>
|
|
157
|
-
)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export default TagsVirtualized
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {TextInput} from '@sanity/ui'
|
|
2
|
-
import {type ChangeEvent} from 'react'
|
|
3
|
-
|
|
4
|
-
// TODO: use correct type
|
|
5
|
-
type Props = any & {
|
|
6
|
-
onValueChange: (value: number) => void
|
|
7
|
-
value?: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const TextInputNumber = (props: Props) => {
|
|
11
|
-
const {onValueChange, value, ...remainingProps} = props
|
|
12
|
-
|
|
13
|
-
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
14
|
-
const val = e.target.value
|
|
15
|
-
const regex = /^[0-9\b]+$/
|
|
16
|
-
|
|
17
|
-
// Test for number and strip leading zeroes
|
|
18
|
-
if (val === '' || regex.test(val)) {
|
|
19
|
-
onValueChange(parseInt(val, 10) || '')
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<TextInput
|
|
25
|
-
{...remainingProps}
|
|
26
|
-
onChange={handleChange} //
|
|
27
|
-
value={value ?? ''}
|
|
28
|
-
/>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default TextInputNumber
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import {CloseIcon, SearchIcon} from '@sanity/icons'
|
|
2
|
-
import {Box, Flex, TextInput} from '@sanity/ui'
|
|
3
|
-
import {type ChangeEvent} from 'react'
|
|
4
|
-
import {useDispatch} from 'react-redux'
|
|
5
|
-
|
|
6
|
-
import useTypedSelector from '../../hooks/useTypedSelector'
|
|
7
|
-
import {searchActions} from '../../modules/search'
|
|
8
|
-
|
|
9
|
-
const TextInputSearch = () => {
|
|
10
|
-
// Redux
|
|
11
|
-
const searchQuery = useTypedSelector((state) => state.search.query)
|
|
12
|
-
|
|
13
|
-
// Redux
|
|
14
|
-
const dispatch = useDispatch()
|
|
15
|
-
|
|
16
|
-
// Callbacks
|
|
17
|
-
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
18
|
-
dispatch(searchActions.querySet({searchQuery: e.currentTarget.value}))
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const handleClear = () => {
|
|
22
|
-
dispatch(searchActions.querySet({searchQuery: ''}))
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<Box style={{position: 'relative'}}>
|
|
27
|
-
<TextInput
|
|
28
|
-
fontSize={1}
|
|
29
|
-
icon={SearchIcon}
|
|
30
|
-
onChange={handleChange}
|
|
31
|
-
placeholder="Search"
|
|
32
|
-
radius={2}
|
|
33
|
-
value={searchQuery}
|
|
34
|
-
/>
|
|
35
|
-
|
|
36
|
-
{/* Clear form button */}
|
|
37
|
-
{searchQuery.length > 0 && (
|
|
38
|
-
<Flex
|
|
39
|
-
align="center"
|
|
40
|
-
justify="center"
|
|
41
|
-
onClick={handleClear}
|
|
42
|
-
style={{
|
|
43
|
-
cursor: 'pointer',
|
|
44
|
-
height: '100%',
|
|
45
|
-
opacity: 0.75,
|
|
46
|
-
position: 'absolute',
|
|
47
|
-
right: 0,
|
|
48
|
-
top: 0,
|
|
49
|
-
width: '2em',
|
|
50
|
-
zIndex: 1, // force stacking context
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
<CloseIcon />
|
|
54
|
-
</Flex>
|
|
55
|
-
)}
|
|
56
|
-
</Box>
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export default TextInputSearch
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {describe, expect, it} from 'vitest'
|
|
2
|
-
|
|
3
|
-
import {renderWithProviders} from '../../__tests__/fixtures/renderWithProviders'
|
|
4
|
-
import {initialState as assetsInitialState} from '../../modules/assets'
|
|
5
|
-
import UploadDropzone from './index'
|
|
6
|
-
|
|
7
|
-
describe('UploadDropzone', () => {
|
|
8
|
-
it('still renders file input when directUploads is false (dropzone in disabled mode)', () => {
|
|
9
|
-
const {container} = renderWithProviders(
|
|
10
|
-
<UploadDropzone>
|
|
11
|
-
<div />
|
|
12
|
-
</UploadDropzone>,
|
|
13
|
-
{
|
|
14
|
-
toolOptions: {creditLine: {enabled: false}, directUploads: false},
|
|
15
|
-
preloaded: {
|
|
16
|
-
assets: {...assetsInitialState, assetTypes: ['image', 'file']},
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
expect(container.querySelector('input[type="file"]')).toBeTruthy()
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('enables file input when directUploads is true', () => {
|
|
25
|
-
const {container} = renderWithProviders(
|
|
26
|
-
<UploadDropzone>
|
|
27
|
-
<div />
|
|
28
|
-
</UploadDropzone>,
|
|
29
|
-
{
|
|
30
|
-
toolOptions: {creditLine: {enabled: false}, directUploads: true},
|
|
31
|
-
preloaded: {
|
|
32
|
-
assets: {...assetsInitialState, assetTypes: ['image', 'file']},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const input = container.querySelector('input[type="file"]')
|
|
38
|
-
expect(input).not.toHaveAttribute('disabled')
|
|
39
|
-
})
|
|
40
|
-
})
|