@sanity/document-internationalization 1.0.0 → 1.0.1
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/LICENSE +1 -1
- package/README.md +89 -47
- package/lib/index.esm.js +2 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/src/index.d.ts +200 -0
- package/package.json +100 -43
- package/src/actions/DeleteWithi18nAction.tsx +118 -0
- package/src/actions/DuplicateWithi18nAction.tsx +77 -0
- package/src/actions/PublishWithi18nAction.ts +118 -0
- package/src/actions/index.ts +42 -0
- package/src/badges/LanguageBadge.tsx +22 -0
- package/src/constants/I18nDelimiter.ts +1 -0
- package/src/constants/I18nPrefix.ts +1 -0
- package/src/constants/IdStructure.ts +4 -0
- package/src/constants/LanguageCultures.ts +902 -0
- package/src/constants/ReferenceBehavior.ts +5 -0
- package/src/constants/SupportedEmojiFlagCodes.ts +251 -0
- package/src/constants/UiMessages.ts +56 -0
- package/src/constants/index.ts +7 -0
- package/src/documentI18n.tsx +33 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useConfig.ts +22 -0
- package/src/hooks/useDelayedFlag.ts +31 -0
- package/src/index.ts +6 -1
- package/src/language-select/components/LanguageSelect/LanguageConfigContext.tsx +4 -0
- package/src/language-select/components/LanguageSelect/LanguageSelect.tsx +188 -0
- package/src/language-select/components/LanguageSelect/LanguageSelectContext.ts +16 -0
- package/src/language-select/components/LanguageSelect/LanguageSelectLabel.tsx +12 -0
- package/src/language-select/components/LanguageSelect/LanguageSelectList.tsx +84 -0
- package/src/language-select/components/LanguageSelect/LanguageSelectListItem.tsx +251 -0
- package/src/language-select/components/LanguageSelect/index.tsx +25 -0
- package/src/language-select/components/SingleFlag/SingleFlag.tsx +44 -0
- package/src/language-select/components/SingleFlag/allEmojiFlagCodes.ts +251 -0
- package/src/language-select/components/SingleFlag/index.ts +1 -0
- package/src/language-select/components/SplitPaneIcon/SplitPaneIcon.tsx +25 -0
- package/src/language-select/components/SplitPaneIcon/index.ts +1 -0
- package/src/language-select/components/index.ts +2 -0
- package/src/language-select/hooks/index.ts +1 -0
- package/src/language-select/hooks/useLanguages.ts +37 -0
- package/src/language-select/hooks/useListeningQuery.ts +66 -0
- package/src/structure/IDefaultDocumentNodeStructureProps.ts +4 -0
- package/src/structure/components/MaintenanceTab/MaintenanceTab.tsx +13 -0
- package/src/structure/components/MaintenanceTab/MaintenanceTabContent.tsx +259 -0
- package/src/structure/components/MaintenanceTab/index.ts +1 -0
- package/src/structure/components/MaintenanceTabResult/MaintenanceTabResult.tsx +44 -0
- package/src/structure/components/MaintenanceTabResult/index.ts +1 -0
- package/src/structure/components/MaintenanceTabTypeSelector/MaintenanceTabTypeSelector.tsx +56 -0
- package/src/structure/components/MaintenanceTabTypeSelector/index.ts +1 -0
- package/src/structure/hooks/index.ts +1 -0
- package/src/structure/hooks/useDocumentsInformation.ts +110 -0
- package/src/structure/index.tsx +120 -0
- package/src/structure/utils/fixBaseDocumentRefs.ts +30 -0
- package/src/structure/utils/fixBaseLanguageMismatch.ts +21 -0
- package/src/structure/utils/fixIdStructureMismatchDocuments.ts +72 -0
- package/src/structure/utils/fixLanguageFields.ts +32 -0
- package/src/structure/utils/fixOrphanedDocuments.ts +22 -0
- package/src/structure/utils/fixTranslationRefs.ts +50 -0
- package/src/structure/utils/index.ts +6 -0
- package/src/types/IEditState.ts +6 -0
- package/src/types/IExtendedLanguageObject.ts +6 -0
- package/src/types/ILanguageObject.ts +4 -0
- package/src/types/ILanguageQuery.ts +13 -0
- package/src/types/IResolverProps.ts +10 -0
- package/src/types/ITranslationRef.ts +6 -0
- package/src/types/IType.ts +40 -0
- package/src/types/IUseDocumentOperationResult.ts +11 -0
- package/src/types/TFieldNamesConfig.ts +5 -0
- package/src/types/TLanguage.ts +3 -0
- package/src/types/TLanguagesOption.ts +4 -0
- package/src/types/TSchema.ts +6 -0
- package/src/types/Ti18nConfig.ts +33 -0
- package/src/types/Ti18nDocument.ts +14 -0
- package/src/types/Ti18nSchema.ts +13 -0
- package/src/types/index.ts +15 -0
- package/src/utils/applyConfig.ts +43 -0
- package/src/utils/baseToTop.ts +5 -0
- package/src/utils/buildDocId.ts +8 -0
- package/src/utils/createSanityReference.ts +11 -0
- package/src/utils/getBaseIdFromId.ts +20 -0
- package/src/utils/getBaseLanguage.ts +6 -0
- package/src/utils/getFlag.ts +38 -0
- package/src/utils/getLanguageFromDocument.ts +9 -0
- package/src/utils/getLanguageFromId.ts +13 -0
- package/src/utils/getLanguagesFromOption.ts +40 -0
- package/src/utils/getTranslationsForId.ts +39 -0
- package/src/utils/index.ts +14 -0
- package/src/utils/makeObjectKey.ts +3 -0
- package/src/utils/normalizeLanguageList.ts +26 -0
- package/src/utils/serializePath.ts +11 -0
- package/src/utils/updateIntlFieldsForDocument.ts +79 -0
- package/src/utils/useSanityClient.ts +6 -0
- package/src/validators/index.ts +1 -0
- package/src/validators/isSlugUnique.ts +56 -0
- package/src/withDocumentI18nPlugin.ts +24 -0
- package/v2-incompatible.js +1 -1
- package/lib/cjs/index.js +0 -539
- package/lib/cjs/index.js.map +0 -1
- package/lib/esm/index.js +0 -532
- package/lib/esm/index.js.map +0 -1
- package/lib/types/index.d.ts +0 -12
- package/lib/types/index.d.ts.map +0 -1
- package/src/LanguageManage.tsx +0 -25
- package/src/LanguageOption.tsx +0 -158
- package/src/LanguagePatch.tsx +0 -61
- package/src/MenuButton.tsx +0 -101
- package/src/badges/index.tsx +0 -23
- package/src/constants.ts +0 -1
- package/src/hooks/useLanguageMetadata.tsx +0 -21
- package/src/hooks/useOpenInNewPane.tsx +0 -27
- package/src/plugin.tsx +0 -104
- package/src/schema/translation/metadata.ts +0 -42
- package/src/types.ts +0 -24
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import styled from 'styled-components'
|
|
3
|
+
import {Box, Grid} from '@sanity/ui'
|
|
4
|
+
import {IExtendedLanguageObject} from '../../../types'
|
|
5
|
+
import {UiMessages} from '../../../constants'
|
|
6
|
+
import {baseToTop} from '../../../utils/baseToTop'
|
|
7
|
+
import {LanguageSelectLabel} from './LanguageSelectLabel'
|
|
8
|
+
import {LanguageSelectListItem} from './LanguageSelectListItem'
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
draftLanguageObjects: IExtendedLanguageObject[]
|
|
12
|
+
publishedLanguageObjects: IExtendedLanguageObject[]
|
|
13
|
+
missingLanguageObjects: IExtendedLanguageObject[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const Divider = styled(Box)`
|
|
17
|
+
border-bottom: 1px solid var(--card-shadow-outline-color);
|
|
18
|
+
`
|
|
19
|
+
|
|
20
|
+
const SelectorBox = styled(Box)`
|
|
21
|
+
minwidth: 250;
|
|
22
|
+
`
|
|
23
|
+
|
|
24
|
+
export const LanguageSelectList: React.FC<Props> = ({
|
|
25
|
+
draftLanguageObjects,
|
|
26
|
+
publishedLanguageObjects,
|
|
27
|
+
missingLanguageObjects,
|
|
28
|
+
}) => {
|
|
29
|
+
const showDivider = React.useMemo(
|
|
30
|
+
() =>
|
|
31
|
+
Boolean(
|
|
32
|
+
!!(draftLanguageObjects.length || publishedLanguageObjects.length) &&
|
|
33
|
+
missingLanguageObjects.length
|
|
34
|
+
),
|
|
35
|
+
[draftLanguageObjects, publishedLanguageObjects, missingLanguageObjects]
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const existingLanguageObjects = React.useMemo(() => {
|
|
39
|
+
return [
|
|
40
|
+
...draftLanguageObjects.map((lang) => ({
|
|
41
|
+
...lang,
|
|
42
|
+
status: 'draft' as const,
|
|
43
|
+
})),
|
|
44
|
+
...publishedLanguageObjects.map((lang) => ({
|
|
45
|
+
...lang,
|
|
46
|
+
status: 'published' as const,
|
|
47
|
+
})),
|
|
48
|
+
]
|
|
49
|
+
.sort(baseToTop)
|
|
50
|
+
.reverse()
|
|
51
|
+
}, [draftLanguageObjects, publishedLanguageObjects])
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<SelectorBox>
|
|
55
|
+
{!!existingLanguageObjects.length && (
|
|
56
|
+
<Box>
|
|
57
|
+
<LanguageSelectLabel>{UiMessages.languageSelect.listLabels.existing}</LanguageSelectLabel>
|
|
58
|
+
<Grid columns={1} gap={1}>
|
|
59
|
+
{existingLanguageObjects.map((language) => (
|
|
60
|
+
<LanguageSelectListItem
|
|
61
|
+
key={language.id}
|
|
62
|
+
status={language.status}
|
|
63
|
+
language={language}
|
|
64
|
+
/>
|
|
65
|
+
))}
|
|
66
|
+
</Grid>
|
|
67
|
+
</Box>
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
{showDivider && <Divider marginY={1} />}
|
|
71
|
+
|
|
72
|
+
{!!missingLanguageObjects.length && (
|
|
73
|
+
<Box>
|
|
74
|
+
<LanguageSelectLabel>{UiMessages.languageSelect.listLabels.missing}</LanguageSelectLabel>
|
|
75
|
+
<Grid columns={1} gap={1}>
|
|
76
|
+
{missingLanguageObjects.map((language) => (
|
|
77
|
+
<LanguageSelectListItem key={language.id} status="missing" language={language} />
|
|
78
|
+
))}
|
|
79
|
+
</Grid>
|
|
80
|
+
</Box>
|
|
81
|
+
)}
|
|
82
|
+
</SelectorBox>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import React, {useContext} from 'react'
|
|
2
|
+
import omit from 'just-omit'
|
|
3
|
+
import {hues} from '@sanity/color'
|
|
4
|
+
import {Text, Button, Badge, Flex, useToast, MenuItem, Box} from '@sanity/ui'
|
|
5
|
+
import {AddIcon, SpinnerIcon} from '@sanity/icons'
|
|
6
|
+
import styled, {css, keyframes} from 'styled-components'
|
|
7
|
+
import {usePaneRouter} from 'sanity/desk'
|
|
8
|
+
import {useEditState} from 'sanity'
|
|
9
|
+
import {RouterContext} from 'sanity/router'
|
|
10
|
+
import {useConfig} from '../../../hooks'
|
|
11
|
+
import {SingleFlag} from '../SingleFlag'
|
|
12
|
+
import type {IExtendedLanguageObject} from '../../../types'
|
|
13
|
+
import {ReferenceBehavior, UiMessages} from '../../../constants'
|
|
14
|
+
import {buildDocId, getBaseIdFromId, useSanityClient} from '../../../utils'
|
|
15
|
+
import {SplitPaneIcon} from '../SplitPaneIcon'
|
|
16
|
+
import {LanguageSelectContext} from './LanguageSelectContext'
|
|
17
|
+
import {LanguageConfigContext} from './LanguageConfigContext'
|
|
18
|
+
|
|
19
|
+
type Props = {
|
|
20
|
+
status: 'draft' | 'published' | 'missing'
|
|
21
|
+
language: IExtendedLanguageObject
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const rotate = keyframes`
|
|
25
|
+
from { transform: rotate(0deg); }
|
|
26
|
+
to { transform: rotate(360deg); }
|
|
27
|
+
`
|
|
28
|
+
|
|
29
|
+
const ListItemSpinner = styled(SpinnerIcon)`
|
|
30
|
+
animation: ${rotate} 500ms linear infinite;
|
|
31
|
+
`
|
|
32
|
+
|
|
33
|
+
const ListItemSplitButton = styled(Button)`
|
|
34
|
+
display: none;
|
|
35
|
+
flex-shrink: 0;
|
|
36
|
+
margin-left: 4px;
|
|
37
|
+
|
|
38
|
+
& svg {
|
|
39
|
+
display: block;
|
|
40
|
+
color: ${hues.gray[700].hex};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
${({theme}) => css`
|
|
44
|
+
@media (min-width: ${theme.sanity.media[1] / 16}em) {
|
|
45
|
+
display: block;
|
|
46
|
+
}
|
|
47
|
+
`}
|
|
48
|
+
`
|
|
49
|
+
|
|
50
|
+
const MenuItemButtonComponent: React.ForwardRefRenderFunction<
|
|
51
|
+
HTMLButtonElement,
|
|
52
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
53
|
+
> = (props, ref) => <button type="button" ref={ref} {...props} />
|
|
54
|
+
const MenuItemButton = React.forwardRef(MenuItemButtonComponent)
|
|
55
|
+
|
|
56
|
+
const MenuItemSelectedButton = styled.button`
|
|
57
|
+
color: ${({theme}) => theme.sanity.color.button.default.primary.enabled.fg};
|
|
58
|
+
background-color: ${({theme}) => theme.sanity.color.button.default.primary.enabled.bg};
|
|
59
|
+
|
|
60
|
+
span {
|
|
61
|
+
color: ${({theme}) => theme.sanity.color.button.default.primary.enabled.fg};
|
|
62
|
+
}
|
|
63
|
+
`
|
|
64
|
+
|
|
65
|
+
export const LanguageSelectListItem: React.FC<Props> = ({status, language}) => {
|
|
66
|
+
const toast = useToast()
|
|
67
|
+
const pluginConfig = useContext(LanguageConfigContext)
|
|
68
|
+
const client = useSanityClient()
|
|
69
|
+
const {currentDocumentId, currentDocumentType, baseLanguage} =
|
|
70
|
+
React.useContext(LanguageSelectContext)
|
|
71
|
+
|
|
72
|
+
if (!currentDocumentId || !currentDocumentType) {
|
|
73
|
+
throw new Error('No document in view')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const config = useConfig(pluginConfig, currentDocumentType)
|
|
77
|
+
const routerContext = React.useContext(RouterContext)
|
|
78
|
+
const {routerPanesState, groupIndex, replaceCurrent} = usePaneRouter()
|
|
79
|
+
const [pending, setPending] = React.useState(false)
|
|
80
|
+
|
|
81
|
+
const baseId = React.useMemo(() => getBaseIdFromId(currentDocumentId), [currentDocumentId])
|
|
82
|
+
const flagCode = React.useMemo(() => language.id.split(/[_-]/).pop(), [language.id])
|
|
83
|
+
|
|
84
|
+
const translatedId = React.useMemo(
|
|
85
|
+
() => (language.id === baseLanguage?.id ? baseId : buildDocId(config, baseId, language.id)),
|
|
86
|
+
[config, baseId, language.id, baseLanguage]
|
|
87
|
+
)
|
|
88
|
+
const baseDocumentEditState = useEditState(baseId, currentDocumentType)
|
|
89
|
+
const baseTranslationBadgeLabel = React.useMemo(() => {
|
|
90
|
+
if (language.isBase) {
|
|
91
|
+
if (language.title.length >= 20) {
|
|
92
|
+
return UiMessages.base.split(' ')[0]
|
|
93
|
+
}
|
|
94
|
+
return UiMessages.base
|
|
95
|
+
}
|
|
96
|
+
return null
|
|
97
|
+
}, [language])
|
|
98
|
+
|
|
99
|
+
const openDocumentInCurrentPane = React.useCallback(
|
|
100
|
+
(id: string, type: string) => {
|
|
101
|
+
replaceCurrent({
|
|
102
|
+
id,
|
|
103
|
+
params: {type},
|
|
104
|
+
})
|
|
105
|
+
},
|
|
106
|
+
[replaceCurrent]
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
const openDocumentInSidePane = React.useCallback(
|
|
110
|
+
(id: string, type: string) => {
|
|
111
|
+
if (!routerContext) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
const panes = [...routerPanesState]
|
|
115
|
+
panes.splice(groupIndex + 1, 0, [
|
|
116
|
+
{
|
|
117
|
+
id: id,
|
|
118
|
+
params: {type},
|
|
119
|
+
},
|
|
120
|
+
])
|
|
121
|
+
|
|
122
|
+
const href = routerContext.resolvePathFromState({panes})
|
|
123
|
+
routerContext.navigateUrl({path: href})
|
|
124
|
+
},
|
|
125
|
+
[routerContext, routerPanesState, groupIndex]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
const handleCreateClick = React.useCallback(async () => {
|
|
129
|
+
try {
|
|
130
|
+
setPending(true)
|
|
131
|
+
const baseDocument = baseDocumentEditState.draft || baseDocumentEditState.published
|
|
132
|
+
const langFieldName = config.fieldNames.lang
|
|
133
|
+
const referencesFieldName = config.fieldNames.references
|
|
134
|
+
const baseFieldName = config.fieldNames.baseReference
|
|
135
|
+
const referenceBehavior = config.referenceBehavior
|
|
136
|
+
await client.createIfNotExists({
|
|
137
|
+
_id: `drafts.${translatedId}`,
|
|
138
|
+
_type: currentDocumentType,
|
|
139
|
+
|
|
140
|
+
// Remove other language references from new draft
|
|
141
|
+
...(baseDocument ? omit(baseDocument, [`_id`, `_type`, referencesFieldName]) : {}),
|
|
142
|
+
|
|
143
|
+
// Set new language
|
|
144
|
+
[langFieldName]: language.id,
|
|
145
|
+
|
|
146
|
+
// Set base language reference
|
|
147
|
+
...(baseDocument?._id && referenceBehavior !== ReferenceBehavior.DISABLED
|
|
148
|
+
? {
|
|
149
|
+
[baseFieldName]: {
|
|
150
|
+
_type: 'reference',
|
|
151
|
+
_ref: baseDocument._id.replace(`drafts.`, ``),
|
|
152
|
+
_weak: true,
|
|
153
|
+
_strengthenOnPublish: {
|
|
154
|
+
type: currentDocumentType,
|
|
155
|
+
weak: referenceBehavior === 'weak',
|
|
156
|
+
template: {
|
|
157
|
+
id: currentDocumentType,
|
|
158
|
+
params: undefined,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
: {}),
|
|
164
|
+
})
|
|
165
|
+
toast.push({
|
|
166
|
+
closable: true,
|
|
167
|
+
status: 'success',
|
|
168
|
+
title: UiMessages.translationCreatedToast.title(language.title),
|
|
169
|
+
description: baseLanguage
|
|
170
|
+
? UiMessages.translationCreatedToast.description(baseLanguage.title)
|
|
171
|
+
: undefined,
|
|
172
|
+
})
|
|
173
|
+
openDocumentInCurrentPane(translatedId, currentDocumentType)
|
|
174
|
+
} catch (err) {
|
|
175
|
+
toast.push({
|
|
176
|
+
closable: true,
|
|
177
|
+
status: 'error',
|
|
178
|
+
title: (err as Error).toString(),
|
|
179
|
+
})
|
|
180
|
+
} finally {
|
|
181
|
+
setPending(false)
|
|
182
|
+
}
|
|
183
|
+
}, [
|
|
184
|
+
config,
|
|
185
|
+
baseDocumentEditState,
|
|
186
|
+
translatedId,
|
|
187
|
+
currentDocumentType,
|
|
188
|
+
language.id,
|
|
189
|
+
language.title,
|
|
190
|
+
toast,
|
|
191
|
+
baseLanguage,
|
|
192
|
+
openDocumentInCurrentPane,
|
|
193
|
+
client,
|
|
194
|
+
])
|
|
195
|
+
|
|
196
|
+
const handleOpenClick = React.useCallback(() => {
|
|
197
|
+
openDocumentInCurrentPane(translatedId, currentDocumentType)
|
|
198
|
+
}, [openDocumentInCurrentPane, translatedId, currentDocumentType])
|
|
199
|
+
|
|
200
|
+
const handleOpenInSidePaneClick = React.useCallback(() => {
|
|
201
|
+
openDocumentInSidePane(translatedId, currentDocumentType)
|
|
202
|
+
}, [openDocumentInSidePane, translatedId, currentDocumentType])
|
|
203
|
+
|
|
204
|
+
if (status === 'missing') {
|
|
205
|
+
return (
|
|
206
|
+
<MenuItem
|
|
207
|
+
as={MenuItemButton}
|
|
208
|
+
data-as="button"
|
|
209
|
+
disabled={pending}
|
|
210
|
+
icon={pending ? ListItemSpinner : AddIcon}
|
|
211
|
+
text={language.title}
|
|
212
|
+
onClick={handleCreateClick}
|
|
213
|
+
/>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<Flex>
|
|
219
|
+
<MenuItem
|
|
220
|
+
as={language.isCurrentLanguage ? MenuItemSelectedButton : MenuItemButton}
|
|
221
|
+
data-as="button"
|
|
222
|
+
data-selected={language.isCurrentLanguage}
|
|
223
|
+
selected={language.isCurrentLanguage}
|
|
224
|
+
onClick={handleOpenClick}
|
|
225
|
+
paddingY={1}
|
|
226
|
+
paddingX={2}
|
|
227
|
+
>
|
|
228
|
+
<Flex align="center" gap={2}>
|
|
229
|
+
<SingleFlag code={flagCode} langCulture={language.id} />
|
|
230
|
+
<Box flex={1}>
|
|
231
|
+
<Text>{language.title}</Text>
|
|
232
|
+
</Box>
|
|
233
|
+
{baseTranslationBadgeLabel ? (
|
|
234
|
+
<Badge fontSize={0}>{baseTranslationBadgeLabel}</Badge>
|
|
235
|
+
) : null}
|
|
236
|
+
</Flex>
|
|
237
|
+
</MenuItem>
|
|
238
|
+
{!language.isCurrentLanguage && (
|
|
239
|
+
<ListItemSplitButton
|
|
240
|
+
type="button"
|
|
241
|
+
tone="default"
|
|
242
|
+
mode="bleed"
|
|
243
|
+
padding={2}
|
|
244
|
+
onClick={handleOpenInSidePaneClick}
|
|
245
|
+
>
|
|
246
|
+
<SplitPaneIcon width={19} height={19} />
|
|
247
|
+
</ListItemSplitButton>
|
|
248
|
+
)}
|
|
249
|
+
</Flex>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {InputProps, SanityDocument} from 'sanity'
|
|
3
|
+
import {Flex} from '@sanity/ui'
|
|
4
|
+
import {Ti18nConfig} from '../../../types'
|
|
5
|
+
import {LanguageSelect} from './LanguageSelect'
|
|
6
|
+
import {LanguageConfigContext} from './LanguageConfigContext'
|
|
7
|
+
|
|
8
|
+
export type LanguageSelectProps = InputProps & {
|
|
9
|
+
config: Ti18nConfig
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function LanguageSelectWrapped(props: LanguageSelectProps) {
|
|
13
|
+
const {config, ...inputProps} = props
|
|
14
|
+
const doc = props.value as SanityDocument
|
|
15
|
+
return (
|
|
16
|
+
<LanguageConfigContext.Provider value={config}>
|
|
17
|
+
{doc._id && (
|
|
18
|
+
<Flex flex={1} justify="flex-end">
|
|
19
|
+
<LanguageSelect schemaType={props.schemaType} document={doc} />
|
|
20
|
+
</Flex>
|
|
21
|
+
)}
|
|
22
|
+
{props.renderDefault(inputProps)}
|
|
23
|
+
</LanguageConfigContext.Provider>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, {useContext, useMemo} from 'react'
|
|
2
|
+
import twemoji from 'twemoji'
|
|
3
|
+
import parse from 'html-react-parser'
|
|
4
|
+
import {Box} from '@sanity/ui'
|
|
5
|
+
import styled from 'styled-components'
|
|
6
|
+
import {getFlag} from '../../../utils/getFlag'
|
|
7
|
+
import {LanguageConfigContext} from '../LanguageSelect/LanguageConfigContext'
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
code?: string
|
|
11
|
+
langCulture?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const EmojiBox = styled(Box)`
|
|
15
|
+
min-width: 24px;
|
|
16
|
+
transform: translateY(1px);
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
export const SingleFlag: React.FunctionComponent<Props> = ({code, langCulture}) => {
|
|
20
|
+
const flagEmoji = useMemo(() => code && getFlag(code), [code])
|
|
21
|
+
const flagHtml = twemoji.parse(flagEmoji ?? `🇺🇳`, {folder: 'svg', ext: '.svg'})
|
|
22
|
+
const flagReact = parse(flagHtml)
|
|
23
|
+
|
|
24
|
+
const {customFlagComponents} = useContext(LanguageConfigContext)
|
|
25
|
+
const CustomFlagComponent = useMemo(() => {
|
|
26
|
+
if (langCulture && customFlagComponents) {
|
|
27
|
+
const exportedName = langCulture.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
28
|
+
if (exportedName in customFlagComponents) {
|
|
29
|
+
return customFlagComponents[exportedName]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null
|
|
33
|
+
}, [langCulture, customFlagComponents])
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Box aria-label={code}>
|
|
37
|
+
{CustomFlagComponent && code ? (
|
|
38
|
+
<CustomFlagComponent code={code} />
|
|
39
|
+
) : (
|
|
40
|
+
<EmojiBox>{flagReact}</EmojiBox>
|
|
41
|
+
)}
|
|
42
|
+
</Box>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
export const allEmojiFlagCodes = [
|
|
2
|
+
'AD',
|
|
3
|
+
'AE',
|
|
4
|
+
'AF',
|
|
5
|
+
'AG',
|
|
6
|
+
'AI',
|
|
7
|
+
'AL',
|
|
8
|
+
'AM',
|
|
9
|
+
'AO',
|
|
10
|
+
'AQ',
|
|
11
|
+
'AR',
|
|
12
|
+
'AS',
|
|
13
|
+
'AT',
|
|
14
|
+
'AU',
|
|
15
|
+
'AW',
|
|
16
|
+
'AX',
|
|
17
|
+
'AZ',
|
|
18
|
+
'BA',
|
|
19
|
+
'BB',
|
|
20
|
+
'BD',
|
|
21
|
+
'BE',
|
|
22
|
+
'BF',
|
|
23
|
+
'BG',
|
|
24
|
+
'BH',
|
|
25
|
+
'BI',
|
|
26
|
+
'BJ',
|
|
27
|
+
'BL',
|
|
28
|
+
'BM',
|
|
29
|
+
'BN',
|
|
30
|
+
'BO',
|
|
31
|
+
'BQ',
|
|
32
|
+
'BR',
|
|
33
|
+
'BS',
|
|
34
|
+
'BT',
|
|
35
|
+
'BV',
|
|
36
|
+
'BW',
|
|
37
|
+
'BY',
|
|
38
|
+
'BZ',
|
|
39
|
+
'CA',
|
|
40
|
+
'CC',
|
|
41
|
+
'CD',
|
|
42
|
+
'CF',
|
|
43
|
+
'CG',
|
|
44
|
+
'CH',
|
|
45
|
+
'CI',
|
|
46
|
+
'CK',
|
|
47
|
+
'CL',
|
|
48
|
+
'CM',
|
|
49
|
+
'CN',
|
|
50
|
+
'CO',
|
|
51
|
+
'CR',
|
|
52
|
+
'CU',
|
|
53
|
+
'CV',
|
|
54
|
+
'CW',
|
|
55
|
+
'CX',
|
|
56
|
+
'CY',
|
|
57
|
+
'CZ',
|
|
58
|
+
'DE',
|
|
59
|
+
'DJ',
|
|
60
|
+
'DK',
|
|
61
|
+
'DM',
|
|
62
|
+
'DO',
|
|
63
|
+
'DZ',
|
|
64
|
+
'EC',
|
|
65
|
+
'EE',
|
|
66
|
+
'EG',
|
|
67
|
+
'EH',
|
|
68
|
+
'ER',
|
|
69
|
+
'ES',
|
|
70
|
+
'ET',
|
|
71
|
+
'FI',
|
|
72
|
+
'FJ',
|
|
73
|
+
'FK',
|
|
74
|
+
'FM',
|
|
75
|
+
'FO',
|
|
76
|
+
'FR',
|
|
77
|
+
'GA',
|
|
78
|
+
'GB',
|
|
79
|
+
'GD',
|
|
80
|
+
'GE',
|
|
81
|
+
'GF',
|
|
82
|
+
'GG',
|
|
83
|
+
'GH',
|
|
84
|
+
'GI',
|
|
85
|
+
'GL',
|
|
86
|
+
'GM',
|
|
87
|
+
'GN',
|
|
88
|
+
'GP',
|
|
89
|
+
'GQ',
|
|
90
|
+
'GR',
|
|
91
|
+
'GS',
|
|
92
|
+
'GT',
|
|
93
|
+
'GU',
|
|
94
|
+
'GW',
|
|
95
|
+
'GY',
|
|
96
|
+
'HK',
|
|
97
|
+
'HM',
|
|
98
|
+
'HN',
|
|
99
|
+
'HR',
|
|
100
|
+
'HT',
|
|
101
|
+
'HU',
|
|
102
|
+
'ID',
|
|
103
|
+
'IE',
|
|
104
|
+
'IL',
|
|
105
|
+
'IM',
|
|
106
|
+
'IN',
|
|
107
|
+
'IO',
|
|
108
|
+
'IQ',
|
|
109
|
+
'IR',
|
|
110
|
+
'IS',
|
|
111
|
+
'IT',
|
|
112
|
+
'JE',
|
|
113
|
+
'JM',
|
|
114
|
+
'JO',
|
|
115
|
+
'JP',
|
|
116
|
+
'KE',
|
|
117
|
+
'KG',
|
|
118
|
+
'KH',
|
|
119
|
+
'KI',
|
|
120
|
+
'KM',
|
|
121
|
+
'KN',
|
|
122
|
+
'KP',
|
|
123
|
+
'KR',
|
|
124
|
+
'KW',
|
|
125
|
+
'KY',
|
|
126
|
+
'KZ',
|
|
127
|
+
'LA',
|
|
128
|
+
'LB',
|
|
129
|
+
'LC',
|
|
130
|
+
'LI',
|
|
131
|
+
'LK',
|
|
132
|
+
'LR',
|
|
133
|
+
'LS',
|
|
134
|
+
'LT',
|
|
135
|
+
'LU',
|
|
136
|
+
'LV',
|
|
137
|
+
'LY',
|
|
138
|
+
'MA',
|
|
139
|
+
'MC',
|
|
140
|
+
'MD',
|
|
141
|
+
'ME',
|
|
142
|
+
'MF',
|
|
143
|
+
'MG',
|
|
144
|
+
'MH',
|
|
145
|
+
'MK',
|
|
146
|
+
'ML',
|
|
147
|
+
'MM',
|
|
148
|
+
'MN',
|
|
149
|
+
'MO',
|
|
150
|
+
'MP',
|
|
151
|
+
'MQ',
|
|
152
|
+
'MR',
|
|
153
|
+
'MS',
|
|
154
|
+
'MT',
|
|
155
|
+
'MU',
|
|
156
|
+
'MV',
|
|
157
|
+
'MW',
|
|
158
|
+
'MX',
|
|
159
|
+
'MY',
|
|
160
|
+
'MZ',
|
|
161
|
+
'NA',
|
|
162
|
+
'NC',
|
|
163
|
+
'NE',
|
|
164
|
+
'NF',
|
|
165
|
+
'NG',
|
|
166
|
+
'NI',
|
|
167
|
+
'NL',
|
|
168
|
+
'NO',
|
|
169
|
+
'NP',
|
|
170
|
+
'NR',
|
|
171
|
+
'NU',
|
|
172
|
+
'NZ',
|
|
173
|
+
'OM',
|
|
174
|
+
'PA',
|
|
175
|
+
'PE',
|
|
176
|
+
'PF',
|
|
177
|
+
'PG',
|
|
178
|
+
'PH',
|
|
179
|
+
'PK',
|
|
180
|
+
'PL',
|
|
181
|
+
'PM',
|
|
182
|
+
'PN',
|
|
183
|
+
'PR',
|
|
184
|
+
'PS',
|
|
185
|
+
'PT',
|
|
186
|
+
'PW',
|
|
187
|
+
'PY',
|
|
188
|
+
'QA',
|
|
189
|
+
'RE',
|
|
190
|
+
'RO',
|
|
191
|
+
'RS',
|
|
192
|
+
'RU',
|
|
193
|
+
'RW',
|
|
194
|
+
'SA',
|
|
195
|
+
'SB',
|
|
196
|
+
'SC',
|
|
197
|
+
'SD',
|
|
198
|
+
'SE',
|
|
199
|
+
'SG',
|
|
200
|
+
'SH',
|
|
201
|
+
'SI',
|
|
202
|
+
'SJ',
|
|
203
|
+
'SK',
|
|
204
|
+
'SL',
|
|
205
|
+
'SM',
|
|
206
|
+
'SN',
|
|
207
|
+
'SO',
|
|
208
|
+
'SR',
|
|
209
|
+
'SS',
|
|
210
|
+
'ST',
|
|
211
|
+
'SV',
|
|
212
|
+
'SX',
|
|
213
|
+
'SY',
|
|
214
|
+
'SZ',
|
|
215
|
+
'TC',
|
|
216
|
+
'TD',
|
|
217
|
+
'TF',
|
|
218
|
+
'TG',
|
|
219
|
+
'TH',
|
|
220
|
+
'TJ',
|
|
221
|
+
'TK',
|
|
222
|
+
'TL',
|
|
223
|
+
'TM',
|
|
224
|
+
'TN',
|
|
225
|
+
'TO',
|
|
226
|
+
'TR',
|
|
227
|
+
'TT',
|
|
228
|
+
'TV',
|
|
229
|
+
'TW',
|
|
230
|
+
'TZ',
|
|
231
|
+
'UA',
|
|
232
|
+
'UG',
|
|
233
|
+
'UM',
|
|
234
|
+
'US',
|
|
235
|
+
'UY',
|
|
236
|
+
'UZ',
|
|
237
|
+
'VA',
|
|
238
|
+
'VC',
|
|
239
|
+
'VE',
|
|
240
|
+
'VG',
|
|
241
|
+
'VI',
|
|
242
|
+
'VN',
|
|
243
|
+
'VU',
|
|
244
|
+
'WF',
|
|
245
|
+
'WS',
|
|
246
|
+
'YE',
|
|
247
|
+
'YT',
|
|
248
|
+
'ZA',
|
|
249
|
+
'ZM',
|
|
250
|
+
'ZW',
|
|
251
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SingleFlag'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React, {forwardRef} from 'react'
|
|
2
|
+
|
|
3
|
+
export const SplitPaneIcon = forwardRef(function UndoIcon(
|
|
4
|
+
props: React.SVGProps<SVGSVGElement>,
|
|
5
|
+
ref: React.Ref<SVGSVGElement>
|
|
6
|
+
) {
|
|
7
|
+
return (
|
|
8
|
+
<svg
|
|
9
|
+
data-sanity-icon="splitPane"
|
|
10
|
+
width="1em"
|
|
11
|
+
height="1em"
|
|
12
|
+
viewBox="0 0 25 25"
|
|
13
|
+
fill="none"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
ref={ref}
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
<path
|
|
19
|
+
d="M10.5 5.5V19.5M13 12.5H15.5M18 12.5H15.5M15.5 12.5V15M15.5 12.5V10M4.5 5.5H20.5V19.5H4.5V5.5Z"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeWidth={1.2}
|
|
22
|
+
/>
|
|
23
|
+
</svg>
|
|
24
|
+
)
|
|
25
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SplitPaneIcon'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLanguages'
|