Skip to content

Commit

Permalink
Localized fields language switch (#708)
Browse files Browse the repository at this point in the history
* Integrate language switch

* Display language switch based on active tab

* Remove editor settings

* Ignore vs code settings

* Reduce language selection to single selection

* Automatic frontend build

---------

Co-authored-by: vin0401 <[email protected]>
  • Loading branch information
vin0401 and vin0401 authored Nov 20, 2024
1 parent cdaf00e commit be746cc
Show file tree
Hide file tree
Showing 39 changed files with 2,435 additions and 47 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Thumbs.db
# PhpStorm / IDEA
.idea

# VS Code
.vscode

# Test env
/bin
Expand Down
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 8 additions & 4 deletions assets/js/src/core/modules/asset/asset-draft-slice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import {
} from '@Pimcore/modules/element/draft/hooks/use-trackable-changes'
import { useImageSettingsReducers } from '@Pimcore/modules/asset/draft/hooks/use-image-settings'
import { type SchedulesDraft, useSchedulesReducers } from '@Pimcore/modules/element/draft/hooks/use-schedules'
import { initialTabsStateValue, type TabsDraft, useTabsReducers } from '../element/draft/hooks/use-tabs'

export interface AssetDraft extends Asset, PropertiesDraft, SchedulesDraft, CustomMetadataDraft, TrackableChangesDraft {
export interface AssetDraft extends Asset, PropertiesDraft, SchedulesDraft, CustomMetadataDraft, TrackableChangesDraft, TabsDraft {
imageSettings: ImageData
}

Expand All @@ -39,7 +40,8 @@ export const slice = createSlice({
imageSettings: [],
schedule: [],
changes: {},
modifiedCells: {}
modifiedCells: {},
...initialTabsStateValue
}),
reducers: {
assetReceived: assetsAdapter.upsertOne,
Expand All @@ -57,7 +59,8 @@ export const slice = createSlice({
...usePropertiesReducers(assetsAdapter),
...useSchedulesReducers(assetsAdapter),
...useCustomMetadataReducers(assetsAdapter),
...useImageSettingsReducers(assetsAdapter)
...useImageSettingsReducers(assetsAdapter),
...useTabsReducers(assetsAdapter)
}
})

Expand Down Expand Up @@ -90,6 +93,7 @@ export const {
addCustomMetadata: addCustomMetadataToAsset,
removeCustomMetadata: removeCustomMetadataFromAsset,
updateCustomMetadata: updateCustomMetadataForAsset,
setCustomMetadata: setCustomMetadataForAsset
setCustomMetadata: setCustomMetadataForAsset,
setActiveTab: setActiveTabForAsset
} = slice.actions
export const { selectById: selectAssetById } = assetsAdapter.getSelectors((state: RootState) => state['asset-draft'])
15 changes: 13 additions & 2 deletions assets/js/src/core/modules/asset/hooks/use-asset-draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
resetChanges,
resetSchedulesChangesForAsset,
selectAssetById,
setActiveTabForAsset,
setCustomMetadataForAsset,
setModifiedCells,
setPropertiesForAsset,
Expand Down Expand Up @@ -56,12 +57,14 @@ import { useSchedulesDraft, type UseSchedulesDraftReturn } from '@Pimcore/module
import { type ElementEditorType, type TypeRegistryInterface } from '@Pimcore/modules/element/editor/services/type-registry'
import { useInjection } from '@Pimcore/app/depency-injection'
import { serviceIds } from '@Pimcore/app/config/services/service-ids'
import { initialTabsStateValue, useTabsDraft, type UseTabsDraftReturn } from '@Pimcore/modules/element/draft/hooks/use-tabs'

interface UseAssetDraftReturn extends
UseCustomMetadataDraftReturn,
UsePropertiesDraftReturn,
UseSchedulesDraftReturn,
UseTrackableChangesDraftReturn,
UseTabsDraftReturn,
UseImageSettingsDraftReturn {
isLoading: boolean
isError: boolean
Expand Down Expand Up @@ -148,7 +151,8 @@ export const useAssetDraft = (id: number): UseAssetDraftReturn => {
schedules: [],
imageSettings: customSettingsResponse,
changes: {},
modifiedCells: {}
modifiedCells: {},
...initialTabsStateValue
}

if (assetData !== undefined) {
Expand Down Expand Up @@ -213,6 +217,12 @@ export const useAssetDraft = (id: number): UseAssetDraftReturn => {
updateImageSettingForAsset
)

const tabsActions = useTabsDraft(
id,
asset,
setActiveTabForAsset
)

const editorType = asset?.type === undefined
? undefined
: (typeRegistry.get(asset.type) ?? typeRegistry.get('unknown'))
Expand All @@ -228,6 +238,7 @@ export const useAssetDraft = (id: number): UseAssetDraftReturn => {
...propertyActions,
...schedulesActions,
...customMetadataActions,
...imageSettingsActions
...imageSettingsActions,
...tabsActions
}
}
16 changes: 12 additions & 4 deletions assets/js/src/core/modules/data-object/data-object-draft-slice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ import {
type TrackableChangesDraft,
useTrackableChangesReducers
} from '@Pimcore/modules/element/draft/hooks/use-trackable-changes'
import {
initialTabsStateValue,
type TabsDraft,
useTabsReducers
} from '@Pimcore/modules/element/draft/hooks/use-tabs'
import { type SchedulesDraft, useSchedulesReducers } from '@Pimcore/modules/element/draft/hooks/use-schedules'
import { type DataObject } from '@Pimcore/modules/data-object/data-object-api-slice-enhanced'

export interface DataObjectDraft extends DataObject, PropertiesDraft, SchedulesDraft, TrackableChangesDraft {
export interface DataObjectDraft extends DataObject, PropertiesDraft, SchedulesDraft, TrackableChangesDraft, TabsDraft {
}

export const dataObjectsAdapter: EntityAdapter<DataObjectDraft, number> = createEntityAdapter<DataObjectDraft>({})
Expand All @@ -34,7 +39,8 @@ export const slice = createSlice({
properties: [],
schedule: [],
changes: {},
modifiedCells: {}
modifiedCells: {},
...initialTabsStateValue
}),
reducers: {
dataObjectReceived: dataObjectsAdapter.upsertOne,
Expand All @@ -50,7 +56,8 @@ export const slice = createSlice({
},
...useTrackableChangesReducers(dataObjectsAdapter),
...usePropertiesReducers(dataObjectsAdapter),
...useSchedulesReducers(dataObjectsAdapter)
...useSchedulesReducers(dataObjectsAdapter),
...useTabsReducers(dataObjectsAdapter)
}
})

Expand All @@ -73,7 +80,8 @@ export const {
removeSchedule: removeScheduleFromDataObject,
setSchedules: setSchedulesForDataObject,
updateSchedule: updateScheduleForDataObject,
resetSchedulesChanges: resetSchedulesChangesForDataObject
resetSchedulesChanges: resetSchedulesChangesForDataObject,
setActiveTab: setActiveTabForDataObject

} = slice.actions
export const { selectById: selectDataObjectById } = dataObjectsAdapter.getSelectors((state: RootState) => state['data-object-draft'])
23 changes: 13 additions & 10 deletions assets/js/src/core/modules/data-object/editor/editor-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useGlobalDataObjectContext } from '@Pimcore/modules/data-object/hooks/u
import { TabsContainer } from '@Pimcore/modules/element/editor/shared-tab-manager/tabs-container'
import { Toolbar } from '@Pimcore/modules/data-object/editor/toolbar/toolbar'
import { TabsToolbarView } from '@Pimcore/modules/element/editor/layouts/tabs-toolbar-view'
import { LanguageSelectionProvider } from './toolbar/language-selection/provider/language-selection-provider'

export interface EditorContainerProps {
id: number
Expand Down Expand Up @@ -64,17 +65,19 @@ const EditorContainer = (props: EditorContainerProps): React.JSX.Element => {

return (
<DataObjectProvider id={ id }>
<TabsToolbarView
renderTabbar={
<TabsContainer
elementEditorType={ editorType }
/>
}
<LanguageSelectionProvider>
<TabsToolbarView
renderTabbar={
<TabsContainer
elementEditorType={ editorType }
/>
}

renderToolbar={
<Toolbar />
}
/>
renderToolbar={
<Toolbar />
}
/>
</LanguageSelectionProvider>
</DataObjectProvider>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Pimcore
*
* This source file is available under two different licenses:
* - Pimcore Open Core License (POCL)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import { LanguageSelection as BaseLanguageSelection } from '@Pimcore/components/language-selection/language-selection'
import { useSettings } from '@Pimcore/modules/app/settings/hooks/use-settings'
import React from 'react'
import { useLanguageSelection } from './provider/use-language-selection'

export const LanguageSelection = (): React.JSX.Element => {
const settings = useSettings()
const { currentLanguage, setCurrentLanguage } = useLanguageSelection()

return (
<BaseLanguageSelection
languages={ [...settings.requiredLanguages] }
onSelectLanguage={ setCurrentLanguage }
selectedLanguage={ currentLanguage }
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Pimcore
*
* This source file is available under two different licenses:
* - Pimcore Open Core License (POCL)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import React, { createContext, useMemo, useState } from 'react'

export interface ILanguageSelectionContext {
currentLanguage: string
setCurrentLanguage: (language: string) => void
}

export const LanguageSelectionContext = createContext<ILanguageSelectionContext>({
currentLanguage: 'en',
setCurrentLanguage: () => {}
})

export interface LanguageSelectionProviderProps {
children: React.ReactNode
}

export const LanguageSelectionProvider = ({ children }: LanguageSelectionProviderProps): React.JSX.Element => {
// @todo check for default language
const [currentLanguage, setCurrentLanguage] = useState('en')

return useMemo(() => (
<LanguageSelectionContext.Provider value={ { currentLanguage, setCurrentLanguage } }>
{children}
</LanguageSelectionContext.Provider>
), [currentLanguage, children])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Pimcore
*
* This source file is available under two different licenses:
* - Pimcore Open Core License (POCL)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import { useContext } from 'react'
import { type ILanguageSelectionContext, LanguageSelectionContext } from './language-selection-provider'

export interface UseLanguageSelectionReturn extends ILanguageSelectionContext {}

export const useLanguageSelection = (): UseLanguageSelectionReturn => {
const context = useContext(LanguageSelectionContext)

return {
...context
}
}
13 changes: 11 additions & 2 deletions assets/js/src/core/modules/data-object/editor/toolbar/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ import {
type DataObjectUpdateByIdApiArg,
useDataObjectUpdateByIdMutation
} from '@Pimcore/modules/data-object/data-object-api-slice-enhanced'
import { Flex } from '@Pimcore/components/flex/flex'
import { TAB_EDIT } from '../types/object/tab-manager/tabs/edit/edit-container'
import { LanguageSelection } from './language-selection/language-selection'

export const Toolbar = (): React.JSX.Element => {
const { t } = useTranslation()
const { id } = useContext(DataObjectContext)
const { dataObject, properties, removeTrackedChanges } = useDataObjectDraft(id)
const { dataObject, properties, activeTab, removeTrackedChanges } = useDataObjectDraft(id)
const hasChanges = dataObject?.modified === true
const [saveDataObject, { isLoading, isSuccess, isError }] = useDataObjectUpdateByIdMutation()
const { saveSchedules, isLoading: isSchedulesLoading, isSuccess: isSchedulesSuccess, isError: isSchedulesError } = useSaveSchedules('data-object', id, false)
Expand All @@ -58,7 +61,13 @@ export const Toolbar = (): React.JSX.Element => {

return (
<ToolbarView>
<ContextMenu />
<Flex>
<ContextMenu />

{activeTab === TAB_EDIT.key && (
<LanguageSelection />
)}
</Flex>

<Button
disabled={ !hasChanges || isLoading || isSchedulesLoading }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import React from 'react'
import React, { type ReactNode } from 'react'
import { type ObjectComponentProps } from './object-component'
import { Form } from '@Pimcore/components/form/form'
import { useInjection } from '@Pimcore/app/depency-injection'
Expand All @@ -20,6 +20,8 @@ import { serviceIds } from '@Pimcore/app/config/services/service-ids'
import { Alert } from 'antd'
import { useFormList } from '../providers/form-list-provider/use-form-list'
import { useLocalizedFields } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/components/localized-fields/provider/localized-fields-provider/use-localized-fields'
import { useLanguageSelection } from '@Pimcore/modules/data-object/editor/toolbar/language-selection/provider/use-language-selection'
import { Text } from '@Pimcore/components/text/text'

export interface DataComponentProps extends ObjectComponentProps {
datatype: 'data'
Expand All @@ -37,6 +39,8 @@ export const DataComponent = (props: DataComponentProps): React.JSX.Element => {
const hasFormList = formList !== undefined
const hasLocalizedFields = localizedFields !== undefined && !hasFormList
let formFieldName: Array<number | string> = [name]
let title = props.title as ReactNode
const { currentLanguage } = useLanguageSelection()

if (hasFormList) {
formFieldName = [formList.field.name, name]
Expand All @@ -45,6 +49,11 @@ export const DataComponent = (props: DataComponentProps): React.JSX.Element => {
if (hasLocalizedFields) {
// @todo should handle multiple locales
formFieldName = ['localizedfields', localizedFields.locales[0], name]
title = (
<>
{title}<Text type='secondary'>({currentLanguage.toUpperCase()})</Text>
</>
)
}

// @todo unify to one fieldType after api is updated completely
Expand All @@ -61,20 +70,26 @@ export const DataComponent = (props: DataComponentProps): React.JSX.Element => {

const objectDataType = objectDataRegistry.getDynamicType(currentFieldType)

const _props = {
...props,
title,
name: hasLocalizedFields ? formFieldName : name
}

if (!objectDataType.isCollectionType) {
return (
<Form.Item
{ ...objectDataType.getObjectDataFormItemProps(props) }
{ ...objectDataType.getObjectDataFormItemProps(_props) }
name={ formFieldName }
>
{objectDataType.getObjectDataComponent(props)}
{objectDataType.getObjectDataComponent(_props)}
</Form.Item>
)
}

return (
<>
{objectDataType.getObjectDataComponent({ ...props, name: hasLocalizedFields ? formFieldName : name })}
{objectDataType.getObjectDataComponent(_props)}
</>
)
}
Loading

0 comments on commit be746cc

Please sign in to comment.